]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OpenID/OpenIDPlugin.php
i18n/L10n review, extension credits added.
[quix0rs-gnu-social.git] / plugins / OpenID / OpenIDPlugin.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * PHP version 5
6  *
7  * LICENCE: This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * @category  Plugin
21  * @package   StatusNet
22  * @author    Evan Prodromou <evan@status.net>
23  * @author   Craig Andrews <candrews@integralblue.com>
24  * @copyright 2009-2010 StatusNet, Inc.
25  * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 /**
35  * Plugin for OpenID authentication and identity
36  *
37  * This class enables consumer support for OpenID, the distributed authentication
38  * and identity system.
39  *
40  * @category Plugin
41  * @package  StatusNet
42  * @author   Evan Prodromou <evan@status.net>
43  * @author   Craig Andrews <candrews@integralblue.com>
44  * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
45  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
46  * @link     http://status.net/
47  * @link     http://openid.net/
48  */
49
50 class OpenIDPlugin extends Plugin
51 {
52     // Plugin parameter: set true to disallow non-OpenID logins
53     // If set, overrides the setting in database or $config['site']['openidonly']
54     public $openidOnly = null;
55
56     function initialize()
57     {
58         parent::initialize();
59         if ($this->openidOnly !== null) {
60             global $config;
61             $config['site']['openidonly'] = (bool)$this->openidOnly;
62         }
63
64     }
65
66     /**
67      * Add OpenID-related paths to the router table
68      *
69      * Hook for RouterInitialized event.
70      *
71      * @param Net_URL_Mapper $m URL mapper
72      *
73      * @return boolean hook return
74      */
75
76     function onStartInitializeRouter($m)
77     {
78         $m->connect('main/openid', array('action' => 'openidlogin'));
79         $m->connect('main/openidtrust', array('action' => 'openidtrust'));
80         $m->connect('settings/openid', array('action' => 'openidsettings'));
81         $m->connect('index.php?action=finishopenidlogin',
82                     array('action' => 'finishopenidlogin'));
83         $m->connect('index.php?action=finishaddopenid',
84                     array('action' => 'finishaddopenid'));
85         $m->connect('main/openidserver', array('action' => 'openidserver'));
86         $m->connect('admin/openid', array('action' => 'openidadminpanel'));
87
88         return true;
89     }
90
91     /**
92      * In OpenID-only mode, disable paths for password stuff
93      *
94      * @param string $path     path to connect
95      * @param array  $defaults path defaults
96      * @param array  $rules    path rules
97      * @param array  $result   unused
98      *
99      * @return boolean hook return
100      */
101
102     function onStartConnectPath(&$path, &$defaults, &$rules, &$result)
103     {
104         if (common_config('site', 'openidonly')) {
105             // Note that we should not remove the login and register
106             // actions. Lots of auth-related things link to them,
107             // such as when visiting a private site without a session
108             // or revalidating a remembered login for admin work.
109             //
110             // We take those two over with redirects to ourselves
111             // over in onArgsInitialize().
112             static $block = array('main/recoverpassword',
113                                   'settings/password');
114
115             if (in_array($path, $block)) {
116                 return false;
117             }
118         }
119
120         return true;
121     }
122
123     /**
124      * If we've been hit with password-login args, redirect
125      *
126      * @param array $args args (URL, Get, post)
127      *
128      * @return boolean hook return
129      */
130
131     function onArgsInitialize($args)
132     {
133         if (common_config('site', 'openidonly')) {
134             if (array_key_exists('action', $args)) {
135                 $action = trim($args['action']);
136                 if (in_array($action, array('login', 'register'))) {
137                     common_redirect(common_local_url('openidlogin'));
138                     exit(0);
139                 } else if ($action == 'passwordsettings') {
140                     common_redirect(common_local_url('openidsettings'));
141                     exit(0);
142                 } else if ($action == 'recoverpassword') {
143                     throw new ClientException('Unavailable action');
144                 }
145             }
146         }
147         return true;
148     }
149
150     /**
151      * Public XRDS output hook
152      *
153      * Puts the bits of code needed by some OpenID providers to show
154      * we're good citizens.
155      *
156      * @param Action       $action         Action being executed
157      * @param XMLOutputter &$xrdsOutputter Output channel
158      *
159      * @return boolean hook return
160      */
161
162     function onEndPublicXRDS($action, &$xrdsOutputter)
163     {
164         $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
165                                                   'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
166                                                   'version' => '2.0'));
167         $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
168         //consumer
169         foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) {
170             $xrdsOutputter->showXrdsService(Auth_OpenID_RP_RETURN_TO_URL_TYPE,
171                                             common_local_url($finish));
172         }
173         //provider
174         $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/server',
175                                         common_local_url('openidserver'),
176                                         null,
177                                         null,
178                                         'http://specs.openid.net/auth/2.0/identifier_select');
179         $xrdsOutputter->elementEnd('XRD');
180     }
181
182     /**
183      * User XRDS output hook
184      *
185      * Puts the bits of code needed to discover OpenID endpoints.
186      *
187      * @param Action       $action         Action being executed
188      * @param XMLOutputter &$xrdsOutputter Output channel
189      *
190      * @return boolean hook return
191      */
192
193     function onEndUserXRDS($action, &$xrdsOutputter)
194     {
195         $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
196                                                   'xml:id' => 'openid',
197                                                   'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
198                                                   'version' => '2.0'));
199         $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
200
201         //consumer
202         $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to',
203                                         common_local_url('finishopenidlogin'));
204
205         //provider
206         $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon',
207                                         common_local_url('openidserver'),
208                                         null,
209                                         null,
210                                         common_profile_url($action->user->nickname));
211         $xrdsOutputter->elementEnd('XRD');
212     }
213
214     /**
215      * If we're in OpenID-only mode, hide all the main menu except OpenID login.
216      *
217      * @param Action $action Action being run
218      *
219      * @return boolean hook return
220      */
221
222     function onStartPrimaryNav($action)
223     {
224         if (common_config('site', 'openidonly') && !common_logged_in()) {
225             // TRANS: Tooltip for main menu option "Login"
226             $tooltip = _m('TOOLTIP', 'Login to the site');
227             $action->menuItem(common_local_url('openidlogin'),
228                               // TRANS: Main menu option when not logged in to log in
229                               _m('MENU', 'Login'),
230                               $tooltip,
231                               false,
232                               'nav_login');
233             // TRANS: Tooltip for main menu option "Help"
234             $tooltip = _m('TOOLTIP', 'Help me!');
235             $action->menuItem(common_local_url('doc', array('title' => 'help')),
236                               // TRANS: Main menu option for help on the StatusNet site
237                               _m('MENU', 'Help'),
238                               $tooltip,
239                               false,
240                               'nav_help');
241             if (!common_config('site', 'private')) {
242                 // TRANS: Tooltip for main menu option "Search"
243                 $tooltip = _m('TOOLTIP', 'Search for people or text');
244                 $action->menuItem(common_local_url('peoplesearch'),
245                                   // TRANS: Main menu option when logged in or when the StatusNet instance is not private
246                                   _m('MENU', 'Search'), $tooltip, false, 'nav_search');
247             }
248             Event::handle('EndPrimaryNav', array($action));
249             return false;
250         }
251         return true;
252     }
253
254     /**
255      * Menu for login
256      *
257      * If we're in openidOnly mode, we disable the menu for all other login.
258      *
259      * @param Action &$action Action being executed
260      *
261      * @return boolean hook return
262      */
263
264     function onStartLoginGroupNav(&$action)
265     {
266         if (common_config('site', 'openidonly')) {
267             $this->showOpenIDLoginTab($action);
268             // Even though we replace this code, we
269             // DON'T run the End* hook, to keep others from
270             // adding tabs. Not nice, but.
271             return false;
272         }
273
274         return true;
275     }
276
277     /**
278      * Menu item for login
279      *
280      * @param Action &$action Action being executed
281      *
282      * @return boolean hook return
283      */
284
285     function onEndLoginGroupNav(&$action)
286     {
287         $this->showOpenIDLoginTab($action);
288
289         return true;
290     }
291
292     /**
293      * Show menu item for login
294      *
295      * @param Action $action Action being executed
296      *
297      * @return void
298      */
299
300     function showOpenIDLoginTab($action)
301     {
302         $action_name = $action->trimmed('action');
303
304         $action->menuItem(common_local_url('openidlogin'),
305                           // TRANS: OpenID plugin menu item on site logon page.
306                           _m('MENU', 'OpenID'),
307                           // TRANS: OpenID plugin tooltip for logon menu item.
308                           _m('Login or register with OpenID'),
309                           $action_name === 'openidlogin');
310     }
311
312     /**
313      * Show menu item for password
314      *
315      * We hide it in openID-only mode
316      *
317      * @param Action $menu    Widget for menu
318      * @param void   &$unused Unused value
319      *
320      * @return void
321      */
322
323     function onStartAccountSettingsPasswordMenuItem($menu, &$unused) {
324         if (common_config('site', 'openidonly')) {
325             return false;
326         }
327         return true;
328     }
329
330     /**
331      * Menu item for OpenID settings
332      *
333      * @param Action &$action Action being executed
334      *
335      * @return boolean hook return
336      */
337
338     function onEndAccountSettingsNav(&$action)
339     {
340         $action_name = $action->trimmed('action');
341
342         $action->menuItem(common_local_url('openidsettings'),
343                           // TRANS: OpenID plugin menu item on user settings page.
344                           _m('MENU', 'OpenID'),
345                           // TRANS: OpenID plugin tooltip for user settings menu item.
346                           _m('Add or remove OpenIDs'),
347                           $action_name === 'openidsettings');
348
349         return true;
350     }
351
352     /**
353      * Autoloader
354      *
355      * Loads our classes if they're requested.
356      *
357      * @param string $cls Class requested
358      *
359      * @return boolean hook return
360      */
361
362     function onAutoload($cls)
363     {
364         switch ($cls)
365         {
366         case 'OpenidloginAction':
367         case 'FinishopenidloginAction':
368         case 'FinishaddopenidAction':
369         case 'XrdsAction':
370         case 'PublicxrdsAction':
371         case 'OpenidsettingsAction':
372         case 'OpenidserverAction':
373         case 'OpenidtrustAction':
374         case 'OpenidadminpanelAction':
375             require_once dirname(__FILE__) . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
376             return false;
377         case 'User_openid':
378             require_once dirname(__FILE__) . '/User_openid.php';
379             return false;
380         case 'User_openid_trustroot':
381             require_once dirname(__FILE__) . '/User_openid_trustroot.php';
382             return false;
383         case 'Auth_OpenID_TeamsExtension':
384         case 'Auth_OpenID_TeamsRequest':
385         case 'Auth_OpenID_TeamsResponse':
386             require_once dirname(__FILE__) . '/extlib/teams-extension.php';
387             return false;
388         default:
389             return true;
390         }
391     }
392
393     /**
394      * Sensitive actions
395      *
396      * These actions should use https when SSL support is 'sometimes'
397      *
398      * @param Action  $action Action to form an URL for
399      * @param boolean &$ssl   Whether to mark it for SSL
400      *
401      * @return boolean hook return
402      */
403
404     function onSensitiveAction($action, &$ssl)
405     {
406         switch ($action)
407         {
408         case 'finishopenidlogin':
409         case 'finishaddopenid':
410             $ssl = true;
411             return false;
412         default:
413             return true;
414         }
415     }
416
417     /**
418      * Login actions
419      *
420      * These actions should be visible even when the site is marked private
421      *
422      * @param Action  $action Action to show
423      * @param boolean &$login Whether it's a login action
424      *
425      * @return boolean hook return
426      */
427
428     function onLoginAction($action, &$login)
429     {
430         switch ($action)
431         {
432         case 'openidlogin':
433         case 'finishopenidlogin':
434         case 'openidserver':
435             $login = true;
436             return false;
437         default:
438             return true;
439         }
440     }
441
442     /**
443      * We include a <meta> element linking to the userxrds page, for OpenID
444      * client-side authentication.
445      *
446      * @param Action $action Action being shown
447      *
448      * @return void
449      */
450
451     function onEndShowHeadElements($action)
452     {
453         if ($action instanceof ShowstreamAction) {
454             $action->element('link', array('rel' => 'openid2.provider',
455                                            'href' => common_local_url('openidserver')));
456             $action->element('link', array('rel' => 'openid2.local_id',
457                                            'href' => $action->profile->profileurl));
458             $action->element('link', array('rel' => 'openid.server',
459                                            'href' => common_local_url('openidserver')));
460             $action->element('link', array('rel' => 'openid.delegate',
461                                            'href' => $action->profile->profileurl));
462         }
463         return true;
464     }
465
466     /**
467      * Redirect to OpenID login if they have an OpenID
468      *
469      * @param Action $action Action being executed
470      * @param User   $user   User doing the action
471      *
472      * @return boolean whether to continue
473      */
474
475     function onRedirectToLogin($action, $user)
476     {
477         if (common_config('site', 'openid_only') || (!empty($user) && User_openid::hasOpenID($user->id))) {
478             common_redirect(common_local_url('openidlogin'), 303);
479             return false;
480         }
481         return true;
482     }
483
484     /**
485      * Show some extra instructions for using OpenID
486      *
487      * @param Action $action Action being executed
488      *
489      * @return boolean hook value
490      */
491
492     function onEndShowPageNotice($action)
493     {
494         $name = $action->trimmed('action');
495
496         switch ($name)
497         {
498         case 'register':
499             if (common_logged_in()) {
500                 $instr = '(Have an [OpenID](http://openid.net/)? ' .
501                   '[Add an OpenID to your account](%%action.openidsettings%%)!';
502             } else {
503                 $instr = '(Have an [OpenID](http://openid.net/)? ' .
504                   'Try our [OpenID registration]'.
505                   '(%%action.openidlogin%%)!)';
506             }
507             break;
508         case 'login':
509             $instr = '(Have an [OpenID](http://openid.net/)? ' .
510               'Try our [OpenID login]'.
511               '(%%action.openidlogin%%)!)';
512             break;
513         default:
514             return true;
515         }
516
517         $output = common_markup_to_html($instr);
518         $action->raw($output);
519         return true;
520     }
521
522     /**
523      * Load our document if requested
524      *
525      * @param string &$title  Title to fetch
526      * @param string &$output HTML to output
527      *
528      * @return boolean hook value
529      */
530
531     function onStartLoadDoc(&$title, &$output)
532     {
533         if ($title == 'openid') {
534             $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid';
535
536             $c      = file_get_contents($filename);
537             $output = common_markup_to_html($c);
538             return false; // success!
539         }
540
541         return true;
542     }
543
544     /**
545      * Add our document to the global menu
546      *
547      * @param string $title   Title being fetched
548      * @param string &$output HTML being output
549      *
550      * @return boolean hook value
551      */
552
553     function onEndLoadDoc($title, &$output)
554     {
555         if ($title == 'help') {
556             $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service';
557
558             $output .= common_markup_to_html($menuitem);
559         }
560
561         return true;
562     }
563
564     /**
565      * Data definitions
566      *
567      * Assure that our data objects are available in the DB
568      *
569      * @return boolean hook value
570      */
571
572     function onCheckSchema()
573     {
574         $schema = Schema::get();
575         $schema->ensureTable('user_openid',
576                              array(new ColumnDef('canonical', 'varchar',
577                                                  '255', false, 'PRI'),
578                                    new ColumnDef('display', 'varchar',
579                                                  '255', false, 'UNI'),
580                                    new ColumnDef('user_id', 'integer',
581                                                  null, false, 'MUL'),
582                                    new ColumnDef('created', 'datetime',
583                                                  null, false),
584                                    new ColumnDef('modified', 'timestamp')));
585         $schema->ensureTable('user_openid_trustroot',
586                              array(new ColumnDef('trustroot', 'varchar',
587                                                  '255', false, 'PRI'),
588                                    new ColumnDef('user_id', 'integer',
589                                                  null, false, 'PRI'),
590                                    new ColumnDef('created', 'datetime',
591                                                  null, false),
592                                    new ColumnDef('modified', 'timestamp')));
593         return true;
594     }
595
596     /**
597      * Add our tables to be deleted when a user is deleted
598      *
599      * @param User  $user    User being deleted
600      * @param array &$tables Array of table names
601      *
602      * @return boolean hook value
603      */
604
605     function onUserDeleteRelated($user, &$tables)
606     {
607         $tables[] = 'User_openid';
608         $tables[] = 'User_openid_trustroot';
609         return true;
610     }
611
612     /**
613      * Add an OpenID tab to the admin panel
614      *
615      * @param Widget $nav Admin panel nav
616      *
617      * @return boolean hook value
618      */
619
620     function onEndAdminPanelNav($nav)
621     {
622         if (AdminPanelAction::canAdmin('openid')) {
623
624             $action_name = $nav->action->trimmed('action');
625
626             $nav->out->menuItem(
627                 common_local_url('openidadminpanel'),
628                 _m('OpenID'),
629                 _m('OpenID configuration'),
630                 $action_name == 'openidadminpanel',
631                 'nav_openid_admin_panel'
632             );
633         }
634
635         return true;
636     }
637
638     /**
639      * Add our version information to output
640      *
641      * @param array &$versions Array of version-data arrays
642      *
643      * @return boolean hook value
644      */
645
646     function onPluginVersion(&$versions)
647     {
648         $versions[] = array('name' => 'OpenID',
649                             'version' => STATUSNET_VERSION,
650                             'author' => 'Evan Prodromou, Craig Andrews',
651                             'homepage' => 'http://status.net/wiki/Plugin:OpenID',
652                             'rawdescription' =>
653                             // TRANS: OpenID plugin description.
654                             _m('Use <a href="http://openid.net/">OpenID</a> to login to the site.'));
655         return true;
656     }
657 }