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