3 * StatusNet, the distributed open-source microblogging tool
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.
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.
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/>.
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/
28 if (!defined('STATUSNET')) {
33 * Plugin for OpenID authentication and identity
35 * This class enables consumer support for OpenID, the distributed authentication
36 * and identity system.
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/
46 class OpenIDPlugin extends Plugin
48 public $openidOnly = false;
52 common_debug("OpenID plugin running with openidonly = {$this->openidOnly}");
56 * Add OpenID-related paths to the router table
58 * Hook for RouterInitialized event.
60 * @param Net_URL_Mapper $m URL mapper
62 * @return boolean hook return
65 function onStartInitializeRouter($m)
67 $m->connect('main/openid', array('action' => 'openidlogin'));
68 $m->connect('main/openidtrust', array('action' => 'openidtrust'));
69 $m->connect('settings/openid', array('action' => 'openidsettings'));
70 $m->connect('index.php?action=finishopenidlogin',
71 array('action' => 'finishopenidlogin'));
72 $m->connect('index.php?action=finishaddopenid',
73 array('action' => 'finishaddopenid'));
74 $m->connect('main/openidserver', array('action' => 'openidserver'));
80 * Public XRDS output hook
82 * Puts the bits of code needed by some OpenID providers to show
83 * we're good citizens.
85 * @param Action $action Action being executed
86 * @param XMLOutputter &$xrdsOutputter Output channel
88 * @return boolean hook return
91 function onEndPublicXRDS($action, &$xrdsOutputter)
93 $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
94 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
96 $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
98 foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) {
99 $xrdsOutputter->showXrdsService(Auth_OpenID_RP_RETURN_TO_URL_TYPE,
100 common_local_url($finish));
103 $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/server',
104 common_local_url('openidserver'),
107 'http://specs.openid.net/auth/2.0/identifier_select');
108 $xrdsOutputter->elementEnd('XRD');
112 * User XRDS output hook
114 * Puts the bits of code needed to discover OpenID endpoints.
116 * @param Action $action Action being executed
117 * @param XMLOutputter &$xrdsOutputter Output channel
119 * @return boolean hook return
122 function onEndUserXRDS($action, &$xrdsOutputter)
124 $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
125 'xml:id' => 'openid',
126 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
127 'version' => '2.0'));
128 $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
131 $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to',
132 common_local_url('finishopenidlogin'));
135 $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon',
136 common_local_url('openidserver'),
139 common_profile_url($action->user->nickname));
140 $xrdsOutputter->elementEnd('XRD');
143 function onStartPrimaryNav($action)
145 if ($this->openidOnly && !common_logged_in()) {
146 // TRANS: Tooltip for main menu option "Login"
147 $tooltip = _m('TOOLTIP', 'Login to the site');
148 // TRANS: Main menu option when not logged in to log in
149 $action->menuItem(common_local_url('openidlogin'),
154 // TRANS: Tooltip for main menu option "Help"
155 $tooltip = _m('TOOLTIP', 'Help me!');
156 // TRANS: Main menu option for help on the StatusNet site
157 $action->menuItem(common_local_url('doc', array('title' => 'help')),
162 if (!common_config('site', 'private')) {
163 // TRANS: Tooltip for main menu option "Search"
164 $tooltip = _m('TOOLTIP', 'Search for people or text');
165 // TRANS: Main menu option when logged in or when the StatusNet instance is not private
166 $action->menuItem(common_local_url('peoplesearch'),
167 _m('MENU', 'Search'), $tooltip, false, 'nav_search');
169 Event::handle('EndPrimaryNav', array($action));
178 * If we're in openidOnly mode, we disable the menu for all other login.
180 * @param Action &$action Action being executed
182 * @return boolean hook return
185 function onStartLoginGroupNav(&$action)
187 if ($this->openidOnly) {
188 $this->showOpenIDLoginTab($action);
189 // Even though we replace this code, we
190 // DON'T run the End* hook, to keep others from
191 // adding tabs. Not nice, but.
199 * Menu item for login
201 * @param Action &$action Action being executed
203 * @return boolean hook return
206 function onEndLoginGroupNav(&$action)
208 $this->showOpenIDLoginTab($action);
214 * Show menu item for login
216 * @param Action $action Action being executed
221 function showOpenIDLoginTab($action)
223 $action_name = $action->trimmed('action');
225 $action->menuItem(common_local_url('openidlogin'),
227 _m('Login or register with OpenID'),
228 $action_name === 'openidlogin');
232 * Show menu item for password
234 * We hide it in openID-only mode
236 * @param Action $menu Widget for menu
237 * @param void &$unused Unused value
242 function onStartAccountSettingsPasswordMenuItem($menu, &$unused) {
243 if ($this->openidOnly) {
250 * Menu item for OpenID settings
252 * @param Action &$action Action being executed
254 * @return boolean hook return
257 function onEndAccountSettingsNav(&$action)
259 $action_name = $action->trimmed('action');
261 $action->menuItem(common_local_url('openidsettings'),
263 _m('Add or remove OpenIDs'),
264 $action_name === 'openidsettings');
272 * Loads our classes if they're requested.
274 * @param string $cls Class requested
276 * @return boolean hook return
279 function onAutoload($cls)
283 case 'OpenidloginAction':
284 case 'FinishopenidloginAction':
285 case 'FinishaddopenidAction':
287 case 'PublicxrdsAction':
288 case 'OpenidsettingsAction':
289 case 'OpenidserverAction':
290 case 'OpenidtrustAction':
291 require_once INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
294 require_once INSTALLDIR.'/plugins/OpenID/User_openid.php';
296 case 'User_openid_trustroot':
297 require_once INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php';
307 * These actions should use https when SSL support is 'sometimes'
309 * @param Action $action Action to form an URL for
310 * @param boolean &$ssl Whether to mark it for SSL
312 * @return boolean hook return
315 function onSensitiveAction($action, &$ssl)
319 case 'finishopenidlogin':
320 case 'finishaddopenid':
331 * These actions should be visible even when the site is marked private
333 * @param Action $action Action to show
334 * @param boolean &$login Whether it's a login action
336 * @return boolean hook return
339 function onLoginAction($action, &$login)
344 case 'finishopenidlogin':
354 * We include a <meta> element linking to the userxrds page, for OpenID
355 * client-side authentication.
357 * @param Action $action Action being shown
362 function onEndShowHeadElements($action)
364 if ($action instanceof ShowstreamAction) {
365 $action->element('link', array('rel' => 'openid2.provider',
366 'href' => common_local_url('openidserver')));
367 $action->element('link', array('rel' => 'openid2.local_id',
368 'href' => $action->profile->profileurl));
369 $action->element('link', array('rel' => 'openid.server',
370 'href' => common_local_url('openidserver')));
371 $action->element('link', array('rel' => 'openid.delegate',
372 'href' => $action->profile->profileurl));
378 * Redirect to OpenID login if they have an OpenID
380 * @param Action $action Action being executed
381 * @param User $user User doing the action
383 * @return boolean whether to continue
386 function onRedirectToLogin($action, $user)
388 if ($this->openidOnly || (!empty($user) && User_openid::hasOpenID($user->id))) {
389 common_redirect(common_local_url('openidlogin'), 303);
396 * Show some extra instructions for using OpenID
398 * @param Action $action Action being executed
400 * @return boolean hook value
403 function onEndShowPageNotice($action)
405 $name = $action->trimmed('action');
410 if (common_logged_in()) {
411 $instr = '(Have an [OpenID](http://openid.net/)? ' .
412 '[Add an OpenID to your account](%%action.openidsettings%%)!';
414 $instr = '(Have an [OpenID](http://openid.net/)? ' .
415 'Try our [OpenID registration]'.
416 '(%%action.openidlogin%%)!)';
420 $instr = '(Have an [OpenID](http://openid.net/)? ' .
421 'Try our [OpenID login]'.
422 '(%%action.openidlogin%%)!)';
428 $output = common_markup_to_html($instr);
429 $action->raw($output);
434 * Load our document if requested
436 * @param string &$title Title to fetch
437 * @param string &$output HTML to output
439 * @return boolean hook value
442 function onStartLoadDoc(&$title, &$output)
444 if ($title == 'openid') {
445 $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid';
447 $c = file_get_contents($filename);
448 $output = common_markup_to_html($c);
449 return false; // success!
456 * Add our document to the global menu
458 * @param string $title Title being fetched
459 * @param string &$output HTML being output
461 * @return boolean hook value
464 function onEndLoadDoc($title, &$output)
466 if ($title == 'help') {
467 $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service';
469 $output .= common_markup_to_html($menuitem);
478 * Assure that our data objects are available in the DB
480 * @return boolean hook value
483 function onCheckSchema()
485 $schema = Schema::get();
486 $schema->ensureTable('user_openid',
487 array(new ColumnDef('canonical', 'varchar',
488 '255', false, 'PRI'),
489 new ColumnDef('display', 'varchar',
490 '255', false, 'UNI'),
491 new ColumnDef('user_id', 'integer',
493 new ColumnDef('created', 'datetime',
495 new ColumnDef('modified', 'timestamp')));
496 $schema->ensureTable('user_openid_trustroot',
497 array(new ColumnDef('trustroot', 'varchar',
498 '255', false, 'PRI'),
499 new ColumnDef('user_id', 'integer',
501 new ColumnDef('created', 'datetime',
503 new ColumnDef('modified', 'timestamp')));
508 * Add our tables to be deleted when a user is deleted
510 * @param User $user User being deleted
511 * @param array &$tables Array of table names
513 * @return boolean hook value
516 function onUserDeleteRelated($user, &$tables)
518 $tables[] = 'User_openid';
519 $tables[] = 'User_openid_trustroot';
524 * Add our version information to output
526 * @param array &$versions Array of version-data arrays
528 * @return boolean hook value
531 function onPluginVersion(&$versions)
533 $versions[] = array('name' => 'OpenID',
534 'version' => STATUSNET_VERSION,
535 'author' => 'Evan Prodromou, Craig Andrews',
536 'homepage' => 'http://status.net/wiki/Plugin:OpenID',
538 _m('Use <a href="http://openid.net/">OpenID</a> to login to the site.'));