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