]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OpenID/OpenIDPlugin.php
Merge branch 'master' of gitorious.org:statusnet/mainline into testing
[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             // TRANS: Main menu option when not logged in to log in
219             $action->menuItem(common_local_url('openidlogin'),
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             // TRANS: Main menu option for help on the StatusNet site
227             $action->menuItem(common_local_url('doc', array('title' => 'help')),
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                 // TRANS: Main menu option when logged in or when the StatusNet instance is not private
236                 $action->menuItem(common_local_url('peoplesearch'),
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                           _m('OpenID'),
297                           _m('Login or register with OpenID'),
298                           $action_name === 'openidlogin');
299     }
300
301     /**
302      * Show menu item for password
303      *
304      * We hide it in openID-only mode
305      *
306      * @param Action $menu    Widget for menu
307      * @param void   &$unused Unused value
308      *
309      * @return void
310      */
311
312     function onStartAccountSettingsPasswordMenuItem($menu, &$unused) {
313         if (common_config('site', 'openidonly')) {
314             return false;
315         }
316         return true;
317     }
318
319     /**
320      * Menu item for OpenID settings
321      *
322      * @param Action &$action Action being executed
323      *
324      * @return boolean hook return
325      */
326
327     function onEndAccountSettingsNav(&$action)
328     {
329         $action_name = $action->trimmed('action');
330
331         $action->menuItem(common_local_url('openidsettings'),
332                           _m('OpenID'),
333                           _m('Add or remove OpenIDs'),
334                           $action_name === 'openidsettings');
335
336         return true;
337     }
338
339     /**
340      * Autoloader
341      *
342      * Loads our classes if they're requested.
343      *
344      * @param string $cls Class requested
345      *
346      * @return boolean hook return
347      */
348
349     function onAutoload($cls)
350     {
351         switch ($cls)
352         {
353         case 'OpenidloginAction':
354         case 'FinishopenidloginAction':
355         case 'FinishaddopenidAction':
356         case 'XrdsAction':
357         case 'PublicxrdsAction':
358         case 'OpenidsettingsAction':
359         case 'OpenidserverAction':
360         case 'OpenidtrustAction':
361         case 'OpenidadminpanelAction':
362             require_once dirname(__FILE__) . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
363             return false;
364         case 'User_openid':
365             require_once dirname(__FILE__) . '/User_openid.php';
366             return false;
367         case 'User_openid_trustroot':
368             require_once dirname(__FILE__) . '/User_openid_trustroot.php';
369             return false;
370         case 'Auth_OpenID_TeamsExtension':
371         case 'Auth_OpenID_TeamsRequest':
372         case 'Auth_OpenID_TeamsResponse':
373             require_once dirname(__FILE__) . '/extlib/teams-extension.php';
374             return false;
375         default:
376             return true;
377         }
378     }
379
380     /**
381      * Sensitive actions
382      *
383      * These actions should use https when SSL support is 'sometimes'
384      *
385      * @param Action  $action Action to form an URL for
386      * @param boolean &$ssl   Whether to mark it for SSL
387      *
388      * @return boolean hook return
389      */
390
391     function onSensitiveAction($action, &$ssl)
392     {
393         switch ($action)
394         {
395         case 'finishopenidlogin':
396         case 'finishaddopenid':
397             $ssl = true;
398             return false;
399         default:
400             return true;
401         }
402     }
403
404     /**
405      * Login actions
406      *
407      * These actions should be visible even when the site is marked private
408      *
409      * @param Action  $action Action to show
410      * @param boolean &$login Whether it's a login action
411      *
412      * @return boolean hook return
413      */
414
415     function onLoginAction($action, &$login)
416     {
417         switch ($action)
418         {
419         case 'openidlogin':
420         case 'finishopenidlogin':
421         case 'openidserver':
422             $login = true;
423             return false;
424         default:
425             return true;
426         }
427     }
428
429     /**
430      * We include a <meta> element linking to the userxrds page, for OpenID
431      * client-side authentication.
432      *
433      * @param Action $action Action being shown
434      *
435      * @return void
436      */
437
438     function onEndShowHeadElements($action)
439     {
440         if ($action instanceof ShowstreamAction) {
441             $action->element('link', array('rel' => 'openid2.provider',
442                                            'href' => common_local_url('openidserver')));
443             $action->element('link', array('rel' => 'openid2.local_id',
444                                            'href' => $action->profile->profileurl));
445             $action->element('link', array('rel' => 'openid.server',
446                                            'href' => common_local_url('openidserver')));
447             $action->element('link', array('rel' => 'openid.delegate',
448                                            'href' => $action->profile->profileurl));
449         }
450         return true;
451     }
452
453     /**
454      * Redirect to OpenID login if they have an OpenID
455      *
456      * @param Action $action Action being executed
457      * @param User   $user   User doing the action
458      *
459      * @return boolean whether to continue
460      */
461
462     function onRedirectToLogin($action, $user)
463     {
464         if (common_config('site', 'openid_only') || (!empty($user) && User_openid::hasOpenID($user->id))) {
465             common_redirect(common_local_url('openidlogin'), 303);
466             return false;
467         }
468         return true;
469     }
470
471     /**
472      * Show some extra instructions for using OpenID
473      *
474      * @param Action $action Action being executed
475      *
476      * @return boolean hook value
477      */
478
479     function onEndShowPageNotice($action)
480     {
481         $name = $action->trimmed('action');
482
483         switch ($name)
484         {
485         case 'register':
486             if (common_logged_in()) {
487                 $instr = '(Have an [OpenID](http://openid.net/)? ' .
488                   '[Add an OpenID to your account](%%action.openidsettings%%)!';
489             } else {
490                 $instr = '(Have an [OpenID](http://openid.net/)? ' .
491                   'Try our [OpenID registration]'.
492                   '(%%action.openidlogin%%)!)';
493             }
494             break;
495         case 'login':
496             $instr = '(Have an [OpenID](http://openid.net/)? ' .
497               'Try our [OpenID login]'.
498               '(%%action.openidlogin%%)!)';
499             break;
500         default:
501             return true;
502         }
503
504         $output = common_markup_to_html($instr);
505         $action->raw($output);
506         return true;
507     }
508
509     /**
510      * Load our document if requested
511      *
512      * @param string &$title  Title to fetch
513      * @param string &$output HTML to output
514      *
515      * @return boolean hook value
516      */
517
518     function onStartLoadDoc(&$title, &$output)
519     {
520         if ($title == 'openid') {
521             $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid';
522
523             $c      = file_get_contents($filename);
524             $output = common_markup_to_html($c);
525             return false; // success!
526         }
527
528         return true;
529     }
530
531     /**
532      * Add our document to the global menu
533      *
534      * @param string $title   Title being fetched
535      * @param string &$output HTML being output
536      *
537      * @return boolean hook value
538      */
539
540     function onEndLoadDoc($title, &$output)
541     {
542         if ($title == 'help') {
543             $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service';
544
545             $output .= common_markup_to_html($menuitem);
546         }
547
548         return true;
549     }
550
551     /**
552      * Data definitions
553      *
554      * Assure that our data objects are available in the DB
555      *
556      * @return boolean hook value
557      */
558
559     function onCheckSchema()
560     {
561         $schema = Schema::get();
562         $schema->ensureTable('user_openid',
563                              array(new ColumnDef('canonical', 'varchar',
564                                                  '255', false, 'PRI'),
565                                    new ColumnDef('display', 'varchar',
566                                                  '255', false, 'UNI'),
567                                    new ColumnDef('user_id', 'integer',
568                                                  null, false, 'MUL'),
569                                    new ColumnDef('created', 'datetime',
570                                                  null, false),
571                                    new ColumnDef('modified', 'timestamp')));
572         $schema->ensureTable('user_openid_trustroot',
573                              array(new ColumnDef('trustroot', 'varchar',
574                                                  '255', false, 'PRI'),
575                                    new ColumnDef('user_id', 'integer',
576                                                  null, false, 'PRI'),
577                                    new ColumnDef('created', 'datetime',
578                                                  null, false),
579                                    new ColumnDef('modified', 'timestamp')));
580         return true;
581     }
582
583     /**
584      * Add our tables to be deleted when a user is deleted
585      *
586      * @param User  $user    User being deleted
587      * @param array &$tables Array of table names
588      *
589      * @return boolean hook value
590      */
591
592     function onUserDeleteRelated($user, &$tables)
593     {
594         $tables[] = 'User_openid';
595         $tables[] = 'User_openid_trustroot';
596         return true;
597     }
598
599     /**
600      * Add an OpenID tab to the admin panel
601      *
602      * @param Widget $nav Admin panel nav
603      *
604      * @return boolean hook value
605      */
606
607     function onEndAdminPanelNav($nav)
608     {
609         if (AdminPanelAction::canAdmin('openid')) {
610
611             $action_name = $nav->action->trimmed('action');
612
613             $nav->out->menuItem(
614                 common_local_url('openidadminpanel'),
615                 _m('OpenID'),
616                 _m('OpenID configuration'),
617                 $action_name == 'openidadminpanel',
618                 'nav_openid_admin_panel'
619             );
620         }
621
622         return true;
623     }
624
625     /**
626      * Add our version information to output
627      *
628      * @param array &$versions Array of version-data arrays
629      *
630      * @return boolean hook value
631      */
632
633     function onPluginVersion(&$versions)
634     {
635         $versions[] = array('name' => 'OpenID',
636                             'version' => STATUSNET_VERSION,
637                             'author' => 'Evan Prodromou, Craig Andrews',
638                             'homepage' => 'http://status.net/wiki/Plugin:OpenID',
639                             'rawdescription' =>
640                             _m('Use <a href="http://openid.net/">OpenID</a> to login to the site.'));
641         return true;
642     }
643 }