diff options
author | Zach Copley <zach@status.net> | 2009-11-19 20:12:46 -0800 |
---|---|---|
committer | Zach Copley <zach@status.net> | 2009-11-19 20:12:46 -0800 |
commit | 4b98edf75f4e255f8c61087bd1525d89653a521f (patch) | |
tree | b2a7eb6d77429eadb1beabe2d5e6ae1c1a2831d6 /plugins/OpenID | |
parent | f92574dbcb1f2d7cd0aaf3c9362db46fa066e888 (diff) | |
parent | c213477081afefb1720c8ae729d1965e7a1dac63 (diff) |
Merge branch '0.9-release'
* 0.9-release: (874 commits)
Removed call to NewDirectMessage() until IE return is fixed i.e.,
Don't show flag user button your own profile
Fixed HXR response for flag user
Using the right form class name
Using common_redirect
Left a form_data class of a <ul> in the user admin panel
Added validation to fields in user admin panel
Added a user admin panel
Added mobile logos for default and identica themes
Changed gif to png
Changed this to action. THANKS zach!
Doing content negotiation only once
Add execute bit to pingqueuehandler
Localisation updates for !StatusNet from !translatewiki.net
Use the browser's geolocation API to set the location on the notice form
Add geometa library, and include it.
Add location form elements to the noticeform, and save their values on submission
Use the $user object nickname, as login name doesnt have to == nickname anymore with plugins such as ldap/etc
Revert "Re added NICKNAME_FMT constant to router.php."
Moved most path and server settings to a new paths admin panel
...
Conflicts:
js/util.js
locale/it_IT/LC_MESSAGES/statusnet.mo
locale/mk_MK/LC_MESSAGES/statusnet.mo
locale/mk_MK/LC_MESSAGES/statusnet.po
locale/pt_BR/LC_MESSAGES/statusnet.mo
locale/vi_VN/LC_MESSAGES/statusnet.mo
plugins/InfiniteScroll/infinitescroll.js
plugins/Realtime/realtimeupdate.js
Diffstat (limited to 'plugins/OpenID')
-rw-r--r-- | plugins/OpenID/OpenIDPlugin.php | 295 | ||||
-rw-r--r-- | plugins/OpenID/User_openid.php | 54 | ||||
-rw-r--r-- | plugins/OpenID/User_openid_trustroot.php | 45 | ||||
-rw-r--r-- | plugins/OpenID/doc-src/openid | 11 | ||||
-rw-r--r-- | plugins/OpenID/finishaddopenid.php | 185 | ||||
-rw-r--r-- | plugins/OpenID/finishopenidlogin.php | 495 | ||||
-rw-r--r-- | plugins/OpenID/openid.php | 293 | ||||
-rw-r--r-- | plugins/OpenID/openidlogin.php | 137 | ||||
-rw-r--r-- | plugins/OpenID/openidserver.php | 151 | ||||
-rw-r--r-- | plugins/OpenID/openidsettings.php | 240 | ||||
-rw-r--r-- | plugins/OpenID/openidtrust.php | 142 |
11 files changed, 2048 insertions, 0 deletions
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php new file mode 100644 index 000000000..6dd8a3f5a --- /dev/null +++ b/plugins/OpenID/OpenIDPlugin.php @@ -0,0 +1,295 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin for OpenID authentication and identity + * + * This class enables consumer support for OpenID, the distributed authentication + * and identity system. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * @link http://openid.net/ + */ + +class OpenIDPlugin extends Plugin +{ + /** + * Initializer for the plugin. + */ + + function __construct() + { + parent::__construct(); + } + + /** + * Add OpenID-related paths to the router table + * + * Hook for RouterInitialized event. + * + * @return boolean hook return + */ + + function onStartInitializeRouter($m) + { + $m->connect('main/openid', array('action' => 'openidlogin')); + $m->connect('main/openidtrust', array('action' => 'openidtrust')); + $m->connect('settings/openid', array('action' => 'openidsettings')); + $m->connect('index.php?action=finishopenidlogin', array('action' => 'finishopenidlogin')); + $m->connect('index.php?action=finishaddopenid', array('action' => 'finishaddopenid')); + $m->connect('main/openidserver', array('action' => 'openidserver')); + + return true; + } + + function onEndPublicXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + //consumer + foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) { + $xrdsOutputter->showXrdsService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, + common_local_url($finish)); + } + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/server', + common_local_url('openidserver'), + null, + null, + 'http://specs.openid.net/auth/2.0/identifier_select'); + $xrdsOutputter->elementEnd('XRD'); + } + + function onEndUserXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'openid', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + + //consumer + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to', + common_local_url('finishopenidlogin')); + + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon', + common_local_url('openidserver'), + null, + null, + common_profile_url($action->user->nickname)); + $xrdsOutputter->elementEnd('XRD'); + } + + function onEndLoginGroupNav(&$action) + { + $action_name = $action->trimmed('action'); + + $action->menuItem(common_local_url('openidlogin'), + _('OpenID'), + _('Login or register with OpenID'), + $action_name === 'openidlogin'); + + return true; + } + + function onEndAccountSettingsNav(&$action) + { + $action_name = $action->trimmed('action'); + + $action->menuItem(common_local_url('openidsettings'), + _('OpenID'), + _('Add or remove OpenIDs'), + $action_name === 'openidsettings'); + + return true; + } + + function onAutoload($cls) + { + switch ($cls) + { + case 'OpenidloginAction': + case 'FinishopenidloginAction': + case 'FinishaddopenidAction': + case 'XrdsAction': + case 'PublicxrdsAction': + case 'OpenidsettingsAction': + case 'OpenidserverAction': + case 'OpenidtrustAction': + require_once(INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); + return false; + case 'User_openid': + require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php'); + return false; + case 'User_openid_trustroot': + require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + return false; + default: + return true; + } + } + + function onSensitiveAction($action, &$ssl) + { + switch ($action) + { + case 'finishopenidlogin': + case 'finishaddopenid': + $ssl = true; + return false; + default: + return true; + } + } + + function onLoginAction($action, &$login) + { + switch ($action) + { + case 'openidlogin': + case 'finishopenidlogin': + case 'openidserver': + $login = true; + return false; + default: + return true; + } + } + + /** + * We include a <meta> element linking to the publicxrds page, for OpenID + * client-side authentication. + * + * @return void + */ + + function onEndShowHeadElements($action) + { + if($action instanceof ShowstreamAction){ + $action->element('link', array('rel' => 'openid2.provider', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid2.local_id', + 'href' => $action->profile->profileurl)); + $action->element('link', array('rel' => 'openid.server', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid.delegate', + 'href' => $action->profile->profileurl)); + } + return true; + } + + /** + * Redirect to OpenID login if they have an OpenID + * + * @return boolean whether to continue + */ + + function onRedirectToLogin($action, $user) + { + if (!empty($user) && User_openid::hasOpenID($user->id)) { + common_redirect(common_local_url('openidlogin'), 303); + return false; + } + return true; + } + + function onEndShowPageNotice($action) + { + $name = $action->trimmed('action'); + + switch ($name) + { + case 'register': + $instr = '(Have an [OpenID](http://openid.net/)? ' . + 'Try our [OpenID registration]'. + '(%%action.openidlogin%%)!)'; + break; + case 'login': + $instr = '(Have an [OpenID](http://openid.net/)? ' . + 'Try our [OpenID login]'. + '(%%action.openidlogin%%)!)'; + break; + default: + return true; + } + + $output = common_markup_to_html($instr); + $action->raw($output); + return true; + } + + function onStartLoadDoc(&$title, &$output) + { + if ($title == 'openid') + { + $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid'; + + $c = file_get_contents($filename); + $output = common_markup_to_html($c); + return false; // success! + } + + return true; + } + + function onEndLoadDoc($title, &$output) + { + if ($title == 'help') + { + $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service'; + + $output .= common_markup_to_html($menuitem); + } + + return true; + } + + function onCheckSchema() { + $schema = Schema::get(); + $schema->ensureDataObject('User_openid'); + $schema->ensureDataObject('User_openid_trustroot'); + return true; + } + + function onUserDeleteRelated($user, &$tables) + { + $tables[] = 'User_openid'; + $tables[] = 'User_openid_trustroot'; + return true; + } +} diff --git a/plugins/OpenID/User_openid.php b/plugins/OpenID/User_openid.php new file mode 100644 index 000000000..c3624118e --- /dev/null +++ b/plugins/OpenID/User_openid.php @@ -0,0 +1,54 @@ +<?php +/** + * Table Definition for user_openid + */ +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; + +class User_openid extends Plugin_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_openid'; // table name + public $canonical; // varchar(255) primary_key not_null + public $display; // varchar(255) unique_key not_null + public $user_id; // int(4) not_null + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User_openid',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + static function hasOpenID($user_id) + { + $oid = new User_openid(); + + $oid->user_id = $user_id; + + $cnt = $oid->find(); + + return ($cnt > 0); + } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('canonical', 'varchar', + '255', false, 'PRI'), + new ColumnDef('display', 'varchar', + '255', false), + new ColumnDef('user_id', 'integer', + null, false, 'MUL'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + } +} diff --git a/plugins/OpenID/User_openid_trustroot.php b/plugins/OpenID/User_openid_trustroot.php new file mode 100644 index 000000000..b208dddfd --- /dev/null +++ b/plugins/OpenID/User_openid_trustroot.php @@ -0,0 +1,45 @@ +<?php +/** + * Table Definition for user_openid_trustroot + */ +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; + +class User_openid_trustroot extends Plugin_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_openid_trustroot'; // table name + public $trustroot; // varchar(255) primary_key not_null + public $user_id; // int(4) primary_key not_null + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User_openid_trustroot',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('User_openid_trustroot', $kv); + } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('trustroot', 'varchar', + '255', false, 'PRI'), + new ColumnDef('user_id', 'integer', + null, false, 'PRI'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + } +} diff --git a/plugins/OpenID/doc-src/openid b/plugins/OpenID/doc-src/openid new file mode 100644 index 000000000..c741e3674 --- /dev/null +++ b/plugins/OpenID/doc-src/openid @@ -0,0 +1,11 @@ +%%site.name%% supports the [OpenID](http://openid.net/) standard for single signon between Web sites. OpenID lets you log into many different Web sites without using a different password for each. (See [Wikipedia's OpenID article](http://en.wikipedia.org/wiki/OpenID) for more information.) + +If you already have an account on %%site.name%%, you can [login](%%action.login%%) with your username and password as usual. +To use OpenID in the future, you can [add an OpenID to your account](%%action.openidsettings%%) after you have logged in normally. + +There are many [Public OpenID providers](http://wiki.openid.net/Public_OpenID_providers), and you may already have an OpenID-enabled account on another service. + +* On wikis: If you have an account on an OpenID-enabled wiki, like [Wikitravel](http://wikitravel.org/), [wikiHow](http://www.wikihow.com/), [Vinismo](http://vinismo.com/), [AboutUs](http://aboutus.org/) or [Keiki](http://kei.ki/), you can log in to %%site.name%% by entering the **full URL** of your user page on that other wiki in the box above. For example, *http://kei.ki/en/User:Evan*. +* [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID in the box above. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*. +* [AOL](http://dev.aol.com/aol-and-63-million-openids) : If you have an account with [AOL](http://www.aol.com/), like an [AIM](http://www.aim.com/) account, you can log in to %%site.name%% by entering your AOL-provided OpenID in the box above. AOL OpenID URLs have the form *http://openid.aol.com/yourusername*. Your username should be all lowercase, no spaces. +* [Blogger](http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html), [Wordpress.com](http://faq.wordpress.com/2007/03/06/what-is-openid/), [LiveJournal](http://www.livejournal.com/openid/about.bml), [Vox](http://bradfitz.vox.com/library/post/openid-for-vox.html) : If you have a blog on any of these services, enter your blog URL in the box above. For example, *http://yourusername.blogspot.com/*, *http://yourusername.wordpress.com/*, *http://yourusername.livejournal.com/*, or *http://yourusername.vox.com/*. diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php new file mode 100644 index 000000000..6e889205d --- /dev/null +++ b/plugins/OpenID/finishaddopenid.php @@ -0,0 +1,185 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Complete adding an OpenID + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; + +/** + * Complete adding an OpenID + * + * Handle the return from an OpenID verification + * + * @category Settings + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class FinishaddopenidAction extends Action +{ + var $msg = null; + + /** + * Handle the redirect back from OpenID confirmation + * + * Check to see if the user's logged in, and then try + * to use the OpenID login system. + * + * @param array $args $_REQUEST arguments + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + if (!common_logged_in()) { + $this->clientError(_('Not logged in.')); + } else { + $this->tryLogin(); + } + } + + /** + * Try to log in using OpenID + * + * Check the OpenID for validity; potentially store it. + * + * @return void + */ + + function tryLogin() + { + $consumer =& oid_consumer(); + + $response = $consumer->complete(common_local_url('finishaddopenid')); + + if ($response->status == Auth_OpenID_CANCEL) { + $this->message(_('OpenID authentication cancelled.')); + return; + } else if ($response->status == Auth_OpenID_FAILURE) { + // Authentication failed; display the error message. + $this->message(sprintf(_('OpenID authentication failed: %s'), + $response->message)); + } else if ($response->status == Auth_OpenID_SUCCESS) { + + $display = $response->getDisplayIdentifier(); + $canonical = ($response->endpoint && $response->endpoint->canonicalID) ? + $response->endpoint->canonicalID : $display; + + $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); + + if ($sreg_resp) { + $sreg = $sreg_resp->contents(); + } + + $cur =& common_current_user(); + + $other = oid_get_user($canonical); + + if ($other) { + if ($other->id == $cur->id) { + $this->message(_('You already have this OpenID!')); + } else { + $this->message(_('Someone else already has this OpenID.')); + } + return; + } + + // start a transaction + + $cur->query('BEGIN'); + + $result = oid_link_user($cur->id, $canonical, $display); + + if (!$result) { + $this->message(_('Error connecting user.')); + return; + } + if ($sreg) { + if (!oid_update_user($cur, $sreg)) { + $this->message(_('Error updating profile')); + return; + } + } + + // success! + + $cur->query('COMMIT'); + + oid_set_last($display); + + common_redirect(common_local_url('openidsettings'), 303); + } + } + + /** + * Show a failure message + * + * Something went wrong. Save the message, and show the page. + * + * @param string $msg Error message to show + * + * @return void + */ + + function message($msg) + { + $this->message = $msg; + $this->showPage(); + } + + /** + * Title of the page + * + * @return string title + */ + + function title() + { + return _('OpenID Login'); + } + + /** + * Show error message + * + * @return void + */ + + function showPageNotice() + { + if ($this->message) { + $this->element('p', 'error', $this->message); + } + } +} diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php new file mode 100644 index 000000000..ff0b451d3 --- /dev/null +++ b/plugins/OpenID/finishopenidlogin.php @@ -0,0 +1,495 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; + +class FinishopenidloginAction extends Action +{ + var $error = null; + var $username = null; + var $message = null; + + function handle($args) + { + parent::handle($args); + if (common_is_real_login()) { + $this->clientError(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. Try again, please.')); + return; + } + if ($this->arg('create')) { + if (!$this->boolean('license')) { + $this->showForm(_('You can\'t register if you don\'t agree to the license.'), + $this->trimmed('newname')); + return; + } + $this->createNewUser(); + } else if ($this->arg('connect')) { + $this->connectUser(); + } else { + common_debug(print_r($this->args, true), __FILE__); + $this->showForm(_('Something weird happened.'), + $this->trimmed('newname')); + } + } else { + $this->tryLogin(); + } + } + + function showPageNotice() + { + if ($this->error) { + $this->element('div', array('class' => 'error'), $this->error); + } else { + $this->element('div', 'instructions', + sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name'))); + } + } + + function title() + { + return _('OpenID Account Setup'); + } + + function showForm($error=null, $username=null) + { + $this->error = $error; + $this->username = $username; + + $this->showPage(); + } + + function showContent() + { + if (!empty($this->message_text)) { + $this->element('div', array('class' => 'error'), $this->message_text); + return; + } + + $this->elementStart('form', array('method' => 'post', + 'id' => 'account_connect', + 'action' => common_local_url('finishopenidlogin'))); + $this->hidden('token', common_session_token()); + $this->element('h2', null, + _('Create new account')); + $this->element('p', null, + _('Create a new user with this nickname.')); + $this->input('newname', _('New nickname'), + ($this->username) ? $this->username : '', + _('1-64 lowercase letters or numbers, no punctuation or spaces')); + $this->elementStart('p'); + $this->element('input', array('type' => 'checkbox', + 'id' => 'license', + 'name' => 'license', + 'value' => 'true')); + $this->text(_('My text and files are available under ')); + $this->element('a', array('href' => common_config('license', 'url')), + common_config('license', 'title')); + $this->text(_(' except this private data: password, email address, IM address, phone number.')); + $this->elementEnd('p'); + $this->submit('create', _('Create')); + $this->element('h2', null, + _('Connect existing account')); + $this->element('p', null, + _('If you already have an account, login with your username and password to connect it to your OpenID.')); + $this->input('nickname', _('Existing nickname')); + $this->password('password', _('Password')); + $this->submit('connect', _('Connect')); + $this->elementEnd('form'); + } + + function tryLogin() + { + $consumer = oid_consumer(); + + $response = $consumer->complete(common_local_url('finishopenidlogin')); + + if ($response->status == Auth_OpenID_CANCEL) { + $this->message(_('OpenID authentication cancelled.')); + return; + } else if ($response->status == Auth_OpenID_FAILURE) { + // Authentication failed; display the error message. + $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message)); + } else if ($response->status == Auth_OpenID_SUCCESS) { + // This means the authentication succeeded; extract the + // identity URL and Simple Registration data (if it was + // returned). + $display = $response->getDisplayIdentifier(); + $canonical = ($response->endpoint->canonicalID) ? + $response->endpoint->canonicalID : $response->getDisplayIdentifier(); + + $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); + + if ($sreg_resp) { + $sreg = $sreg_resp->contents(); + } + + $user = oid_get_user($canonical); + + if ($user) { + oid_set_last($display); + # XXX: commented out at @edd's request until better + # control over how data flows from OpenID provider. + # oid_update_user($user, $sreg); + common_set_user($user); + common_real_login(true); + if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { + common_rememberme($user); + } + unset($_SESSION['openid_rememberme']); + $this->goHome($user->nickname); + } else { + $this->saveValues($display, $canonical, $sreg); + $this->showForm(null, $this->bestNewNickname($display, $sreg)); + } + } + } + + function message($msg) + { + $this->message_text = $msg; + $this->showPage(); + } + + function saveValues($display, $canonical, $sreg) + { + common_ensure_session(); + $_SESSION['openid_display'] = $display; + $_SESSION['openid_canonical'] = $canonical; + $_SESSION['openid_sreg'] = $sreg; + } + + function getSavedValues() + { + return array($_SESSION['openid_display'], + $_SESSION['openid_canonical'], + $_SESSION['openid_sreg']); + } + + function createNewUser() + { + # FIXME: save invite code before redirect, and check here + + if (common_config('site', 'closed')) { + $this->clientError(_('Registration not allowed.')); + return; + } + + $invite = null; + + if (common_config('site', 'inviteonly')) { + $code = $_SESSION['invitecode']; + if (empty($code)) { + $this->clientError(_('Registration not allowed.')); + return; + } + + $invite = Invitation::staticGet($code); + + if (empty($invite)) { + $this->clientError(_('Not a valid invitation code.')); + return; + } + } + + $nickname = $this->trimmed('newname'); + + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => NICKNAME_FMT))) { + $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.')); + return; + } + + if (!User::allowed_nickname($nickname)) { + $this->showForm(_('Nickname not allowed.')); + return; + } + + if (User::staticGet('nickname', $nickname)) { + $this->showForm(_('Nickname already in use. Try another one.')); + return; + } + + list($display, $canonical, $sreg) = $this->getSavedValues(); + + if (!$display || !$canonical) { + $this->serverError(_('Stored OpenID not found.')); + return; + } + + # Possible race condition... let's be paranoid + + $other = oid_get_user($canonical); + + if ($other) { + $this->serverError(_('Creating new account for OpenID that already has a user.')); + return; + } + + $location = ''; + if (!empty($sreg['country'])) { + if ($sreg['postcode']) { + # XXX: use postcode to get city and region + # XXX: also, store postcode somewhere -- it's valuable! + $location = $sreg['postcode'] . ', ' . $sreg['country']; + } else { + $location = $sreg['country']; + } + } + + if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) { + $fullname = $sreg['fullname']; + } else { + $fullname = ''; + } + + if (!empty($sreg['email']) && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { + $email = $sreg['email']; + } else { + $email = ''; + } + + # XXX: add language + # XXX: add timezone + + $args = array('nickname' => $nickname, + 'email' => $email, + 'fullname' => $fullname, + 'location' => $location); + + if (!empty($invite)) { + $args['code'] = $invite->code; + } + + $user = User::register($args); + + $result = oid_link_user($user->id, $canonical, $display); + + oid_set_last($display); + common_set_user($user); + common_real_login(true); + if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { + common_rememberme($user); + } + unset($_SESSION['openid_rememberme']); + common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)), + 303); + } + + function connectUser() + { + $nickname = $this->trimmed('nickname'); + $password = $this->trimmed('password'); + + if (!common_check_user($nickname, $password)) { + $this->showForm(_('Invalid username or password.')); + return; + } + + # They're legit! + + $user = User::staticGet('nickname', $nickname); + + list($display, $canonical, $sreg) = $this->getSavedValues(); + + if (!$display || !$canonical) { + $this->serverError(_('Stored OpenID not found.')); + return; + } + + $result = oid_link_user($user->id, $canonical, $display); + + if (!$result) { + $this->serverError(_('Error connecting user to OpenID.')); + return; + } + + oid_update_user($user, $sreg); + oid_set_last($display); + common_set_user($user); + common_real_login(true); + if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { + common_rememberme($user); + } + unset($_SESSION['openid_rememberme']); + $this->goHome($user->nickname); + } + + function goHome($nickname) + { + $url = common_get_returnto(); + if ($url) { + # We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $nickname)); + } + common_redirect($url, 303); + } + + function bestNewNickname($display, $sreg) + { + + # Try the passed-in nickname + + if (!empty($sreg['nickname'])) { + $nickname = $this->nicknamize($sreg['nickname']); + if ($this->isNewNickname($nickname)) { + return $nickname; + } + } + + # Try the full name + + if (!empty($sreg['fullname'])) { + $fullname = $this->nicknamize($sreg['fullname']); + if ($this->isNewNickname($fullname)) { + return $fullname; + } + } + + # Try the URL + + $from_url = $this->openidToNickname($display); + + if ($from_url && $this->isNewNickname($from_url)) { + return $from_url; + } + + # XXX: others? + + return null; + } + + function isNewNickname($str) + { + if (!Validate::string($str, array('min_length' => 1, + 'max_length' => 64, + 'format' => NICKNAME_FMT))) { + return false; + } + if (!User::allowed_nickname($str)) { + return false; + } + if (User::staticGet('nickname', $str)) { + return false; + } + return true; + } + + function openidToNickname($openid) + { + if (Auth_Yadis_identifierScheme($openid) == 'XRI') { + return $this->xriToNickname($openid); + } else { + return $this->urlToNickname($openid); + } + } + + # We try to use an OpenID URL as a legal StatusNet user name in this order + # 1. Plain hostname, like http://evanp.myopenid.com/ + # 2. One element in path, like http://profile.typekey.com/EvanProdromou/ + # or http://getopenid.com/evanprodromou + + function urlToNickname($openid) + { + static $bad = array('query', 'user', 'password', 'port', 'fragment'); + + $parts = parse_url($openid); + + # If any of these parts exist, this won't work + + foreach ($bad as $badpart) { + if (array_key_exists($badpart, $parts)) { + return null; + } + } + + # We just have host and/or path + + # If it's just a host... + if (array_key_exists('host', $parts) && + (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0)) + { + $hostparts = explode('.', $parts['host']); + + # Try to catch common idiom of nickname.service.tld + + if ((count($hostparts) > 2) && + (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au + (strcmp($hostparts[0], 'www') != 0)) + { + return $this->nicknamize($hostparts[0]); + } else { + # Do the whole hostname + return $this->nicknamize($parts['host']); + } + } else { + if (array_key_exists('path', $parts)) { + # Strip starting, ending slashes + $path = preg_replace('@/$@', '', $parts['path']); + $path = preg_replace('@^/@', '', $path); + if (strpos($path, '/') === false) { + return $this->nicknamize($path); + } + } + } + + return null; + } + + function xriToNickname($xri) + { + $base = $this->xriBase($xri); + + if (!$base) { + return null; + } else { + # =evan.prodromou + # or @gratis*evan.prodromou + $parts = explode('*', substr($base, 1)); + return $this->nicknamize(array_pop($parts)); + } + } + + function xriBase($xri) + { + if (substr($xri, 0, 6) == 'xri://') { + return substr($xri, 6); + } else { + return $xri; + } + } + + # Given a string, try to make it work as a nickname + + function nicknamize($str) + { + $str = preg_replace('/\W/', '', $str); + return strtolower($str); + } +} diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php new file mode 100644 index 000000000..dd628e773 --- /dev/null +++ b/plugins/OpenID/openid.php @@ -0,0 +1,293 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php'); + +require_once('Auth/OpenID.php'); +require_once('Auth/OpenID/Consumer.php'); +require_once('Auth/OpenID/Server.php'); +require_once('Auth/OpenID/SReg.php'); +require_once('Auth/OpenID/MySQLStore.php'); + +# About one year cookie expiry + +define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60)); +define('OPENID_COOKIE_KEY', 'lastusedopenid'); + +function oid_store() +{ + static $store = null; + if (!$store) { + # Can't be called statically + $user = new User(); + $conn = $user->getDatabaseConnection(); + $store = new Auth_OpenID_MySQLStore($conn); + } + return $store; +} + +function oid_consumer() +{ + $store = oid_store(); + $consumer = new Auth_OpenID_Consumer($store); + return $consumer; +} + +function oid_server() +{ + $store = oid_store(); + $server = new Auth_OpenID_Server($store, common_local_url('openidserver')); + return $server; +} + +function oid_clear_last() +{ + oid_set_last(''); +} + +function oid_set_last($openid_url) +{ + common_set_cookie(OPENID_COOKIE_KEY, + $openid_url, + time() + OPENID_COOKIE_EXPIRY); +} + +function oid_get_last() +{ + if (empty($_COOKIE[OPENID_COOKIE_KEY])) { + return null; + } + $openid_url = $_COOKIE[OPENID_COOKIE_KEY]; + if ($openid_url && strlen($openid_url) > 0) { + return $openid_url; + } else { + return null; + } +} + +function oid_link_user($id, $canonical, $display) +{ + + $oid = new User_openid(); + $oid->user_id = $id; + $oid->canonical = $canonical; + $oid->display = $display; + $oid->created = DB_DataObject_Cast::dateTime(); + + if (!$oid->insert()) { + $err = PEAR::getStaticProperty('DB_DataObject','lastError'); + common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); + return false; + } + + return true; +} + +function oid_get_user($openid_url) +{ + $user = null; + $oid = User_openid::staticGet('canonical', $openid_url); + if ($oid) { + $user = User::staticGet('id', $oid->user_id); + } + return $user; +} + +function oid_check_immediate($openid_url, $backto=null) +{ + if (!$backto) { + $action = $_REQUEST['action']; + $args = common_copy_args($_GET); + unset($args['action']); + $backto = common_local_url($action, $args); + } + common_debug('going back to "' . $backto . '"', __FILE__); + + common_ensure_session(); + + $_SESSION['openid_immediate_backto'] = $backto; + common_debug('passed-in variable is "' . $backto . '"', __FILE__); + common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__); + + oid_authenticate($openid_url, + 'finishimmediate', + true); +} + +function oid_authenticate($openid_url, $returnto, $immediate=false) +{ + + $consumer = oid_consumer(); + + if (!$consumer) { + common_server_error(_('Cannot instantiate OpenID consumer object.')); + return false; + } + + common_ensure_session(); + + $auth_request = $consumer->begin($openid_url); + + // Handle failure status return values. + if (!$auth_request) { + return _('Not a valid OpenID.'); + } else if (Auth_OpenID::isFailure($auth_request)) { + return sprintf(_('OpenID failure: %s'), $auth_request->message); + } + + $sreg_request = Auth_OpenID_SRegRequest::build(// Required + array(), + // Optional + array('nickname', + 'email', + 'fullname', + 'language', + 'timezone', + 'postcode', + 'country')); + + if ($sreg_request) { + $auth_request->addExtension($sreg_request); + } + + $trust_root = common_root_url(true); + $process_url = common_local_url($returnto); + + if ($auth_request->shouldSendRedirect()) { + $redirect_url = $auth_request->redirectURL($trust_root, + $process_url, + $immediate); + if (!$redirect_url) { + } else if (Auth_OpenID::isFailure($redirect_url)) { + return sprintf(_('Could not redirect to server: %s'), $redirect_url->message); + } else { + common_redirect($redirect_url, 303); + } + } else { + // Generate form markup and render it. + $form_id = 'openid_message'; + $form_html = $auth_request->formMarkup($trust_root, $process_url, + $immediate, array('id' => $form_id)); + + # XXX: This is cheap, but things choke if we don't escape ampersands + # in the HTML attributes + + $form_html = preg_replace('/&/', '&', $form_html); + + // Display an error if the form markup couldn't be generated; + // otherwise, render the HTML. + if (Auth_OpenID::isFailure($form_html)) { + common_server_error(sprintf(_('Could not create OpenID form: %s'), $form_html->message)); + } else { + $action = new AutosubmitAction(); // see below + $action->form_html = $form_html; + $action->form_id = $form_id; + $action->prepare(array('action' => 'autosubmit')); + $action->handle(array('action' => 'autosubmit')); + } + } +} + +# Half-assed attempt at a module-private function + +function _oid_print_instructions() +{ + common_element('div', 'instructions', + _('This form should automatically submit itself. '. + 'If not, click the submit button to go to your '. + 'OpenID provider.')); +} + +# update a user from sreg parameters + +function oid_update_user(&$user, &$sreg) +{ + + $profile = $user->getProfile(); + + $orig_profile = clone($profile); + + if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { + $profile->fullname = $sreg['fullname']; + } + + if ($sreg['country']) { + if ($sreg['postcode']) { + # XXX: use postcode to get city and region + # XXX: also, store postcode somewhere -- it's valuable! + $profile->location = $sreg['postcode'] . ', ' . $sreg['country']; + } else { + $profile->location = $sreg['country']; + } + } + + # XXX save language if it's passed + # XXX save timezone if it's passed + + if (!$profile->update($orig_profile)) { + common_server_error(_('Error saving the profile.')); + return false; + } + + $orig_user = clone($user); + + if ($sreg['email'] && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { + $user->email = $sreg['email']; + } + + if (!$user->update($orig_user)) { + common_server_error(_('Error saving the user.')); + return false; + } + + return true; +} + +class AutosubmitAction extends Action +{ + var $form_html = null; + var $form_id = null; + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + function title() + { + return _('OpenID Auto-Submit'); + } + + function showContent() + { + $this->raw($this->form_html); + } + + function showScripts() + { + parent::showScripts(); + $this->element('script', null, + '$(document).ready(function() { ' . + ' $(\'#'. $this->form_id .'\').submit(); '. + '});'); + } +} diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php new file mode 100644 index 000000000..29e89234e --- /dev/null +++ b/plugins/OpenID/openidlogin.php @@ -0,0 +1,137 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; + +class OpenidloginAction extends Action +{ + function handle($args) + { + parent::handle($args); + if (common_is_real_login()) { + $this->clientError(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $openid_url = $this->trimmed('openid_url'); + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. Try again, please.'), $openid_url); + return; + } + + $rememberme = $this->boolean('rememberme'); + + common_ensure_session(); + + $_SESSION['openid_rememberme'] = $rememberme; + + $result = oid_authenticate($openid_url, + 'finishopenidlogin'); + + if (is_string($result)) { # error message + unset($_SESSION['openid_rememberme']); + $this->showForm($result, $openid_url); + } + } else { + $openid_url = oid_get_last(); + $this->showForm(null, $openid_url); + } + } + + function getInstructions() + { + if (common_logged_in() && !common_is_real_login() && + common_get_returnto()) { + // rememberme logins have to reauthenticate before + // changing any profile settings (cookie-stealing protection) + return _('For security reasons, please re-login with your ' . + '[OpenID](%%doc.openid%%) ' . + 'before changing your settings.'); + } else { + return _('Login with an [OpenID](%%doc.openid%%) account.'); + } + } + + function showPageNotice() + { + if ($this->error) { + $this->element('div', array('class' => 'error'), $this->error); + } else { + $instr = $this->getInstructions(); + $output = common_markup_to_html($instr); + $this->elementStart('div', 'instructions'); + $this->raw($output); + $this->elementEnd('div'); + } + } + + function showScripts() + { + parent::showScripts(); + $this->autofocus('openid_url'); + } + + function title() + { + return _('OpenID Login'); + } + + function showForm($error=null, $openid_url) + { + $this->error = $error; + $this->openid_url = $openid_url; + $this->showPage(); + } + + function showContent() { + $formaction = common_local_url('openidlogin'); + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_openid_login', + 'class' => 'form_settings', + 'action' => $formaction)); + $this->elementStart('fieldset'); + $this->element('legend', null, _('OpenID login')); + $this->hidden('token', common_session_token()); + + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->input('openid_url', _('OpenID URL'), + $this->openid_url, + _('Your OpenID URL')); + $this->elementEnd('li'); + $this->elementStart('li', array('id' => 'settings_rememberme')); + $this->checkbox('rememberme', _('Remember me'), false, + _('Automatically login in the future; ' . + 'not for shared computers!')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->submit('submit', _('Login')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } + + function showLocalNav() + { + $nav = new LoginGroupNav($this); + $nav->show(); + } +} diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php new file mode 100644 index 000000000..dab97c93e --- /dev/null +++ b/plugins/OpenID/openidserver.php @@ -0,0 +1,151 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Settings for OpenID + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/action.php'; +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; +require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + +/** + * Settings for OpenID + * + * Lets users add, edit and delete OpenIDs from their account + * + * @category Settings + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class OpenidserverAction extends Action +{ + var $oserver; + + function prepare($args) + { + parent::prepare($args); + $this->oserver = oid_server(); + return true; + } + + function handle($args) + { + parent::handle($args); + $request = $this->oserver->decodeRequest(); + if (in_array($request->mode, array('checkid_immediate', + 'checkid_setup'))) { + $user = common_current_user(); + if(!$user){ + if($request->immediate){ + //cannot prompt the user to login in immediate mode, so answer false + $response = $this->generateDenyResponse($request); + }else{ + /* Go log in, and then come back. */ + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + return; + } + }else if(common_profile_url($user->nickname) == $request->identity || $request->idSelect()){ + $user_openid_trustroot = User_openid_trustroot::pkeyGet( + array('user_id'=>$user->id, 'trustroot'=>$request->trust_root)); + if(empty($user_openid_trustroot)){ + if($request->immediate){ + //cannot prompt the user to trust this trust root in immediate mode, so answer false + $response = $this->generateDenyResponse($request); + }else{ + common_ensure_session(); + $_SESSION['openid_trust_root'] = $request->trust_root; + $allowResponse = $this->generateAllowResponse($request, $user); + $this->oserver->encodeResponse($allowResponse); //sign the response + $denyResponse = $this->generateDenyResponse($request); + $this->oserver->encodeResponse($denyResponse); //sign the response + $_SESSION['openid_allow_url'] = $allowResponse->encodeToUrl(); + $_SESSION['openid_deny_url'] = $denyResponse->encodeToUrl(); + //ask the user to trust this trust root + common_redirect(common_local_url('openidtrust')); + return; + } + }else{ + //user has previously authorized this trust root + $response = $this->generateAllowResponse($request, $user); + //$response = $request->answer(true, null, common_profile_url($user->nickname)); + } + } else if ($request->immediate) { + $response = $this->generateDenyResponse($request); + } else { + //invalid + $this->clientError(sprintf(_('You are not authorized to use the identity %s'),$request->identity),$code=403); + } + } else { + $response = $this->oserver->handleRequest($request); + } + + if($response){ + $response = $this->oserver->encodeResponse($response); + if ($response->code != AUTH_OPENID_HTTP_OK) { + header(sprintf("HTTP/1.1 %d ", $response->code), + true, $response->code); + } + + if($response->headers){ + foreach ($response->headers as $k => $v) { + header("$k: $v"); + } + } + $this->raw($response->body); + }else{ + $this->clientError(_('Just an OpenID provider. Nothing to see here, move along...'),$code=500); + } + } + + function generateAllowResponse($request, $user){ + $response = $request->answer(true, null, common_profile_url($user->nickname)); + + $profile = $user->getProfile(); + $sreg_data = array( + 'fullname' => $profile->fullname, + 'nickname' => $user->nickname, + 'email' => $user->email, + 'language' => $user->language, + 'timezone' => $user->timezone); + $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($request); + $sreg_response = Auth_OpenID_SRegResponse::extractResponse( + $sreg_request, $sreg_data); + $sreg_response->toMessage($response->fields); + return $response; + } + + function generateDenyResponse($request){ + $response = $request->answer(false); + return $response; + } +} diff --git a/plugins/OpenID/openidsettings.php b/plugins/OpenID/openidsettings.php new file mode 100644 index 000000000..3ad46f5f5 --- /dev/null +++ b/plugins/OpenID/openidsettings.php @@ -0,0 +1,240 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Settings for OpenID + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/accountsettingsaction.php'; +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; + +/** + * Settings for OpenID + * + * Lets users add, edit and delete OpenIDs from their account + * + * @category Settings + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class OpenidsettingsAction extends AccountSettingsAction +{ + /** + * Title of the page + * + * @return string Page title + */ + + function title() + { + return _('OpenID settings'); + } + + /** + * Instructions for use + * + * @return string Instructions for use + */ + + function getInstructions() + { + return _('[OpenID](%%doc.openid%%) lets you log into many sites' . + ' with the same user account.'. + ' Manage your associated OpenIDs from here.'); + } + + function showScripts() + { + parent::showScripts(); + $this->autofocus('openid_url'); + } + + /** + * Show the form for OpenID management + * + * We have one form with a few different submit buttons to do different things. + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_settings_openid_add', + 'class' => 'form_settings', + 'action' => + common_local_url('openidsettings'))); + $this->elementStart('fieldset', array('id' => 'settings_openid_add')); + $this->element('legend', null, _('Add OpenID')); + $this->hidden('token', common_session_token()); + $this->element('p', 'form_guide', + _('If you want to add an OpenID to your account, ' . + 'enter it in the box below and click "Add".')); + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->element('label', array('for' => 'openid_url'), + _('OpenID URL')); + $this->element('input', array('name' => 'openid_url', + 'type' => 'text', + 'id' => 'openid_url')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->element('input', array('type' => 'submit', + 'id' => 'settings_openid_add_action-submit', + 'name' => 'add', + 'class' => 'submit', + 'value' => _('Add'))); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + + $oid = new User_openid(); + + $oid->user_id = $user->id; + + $cnt = $oid->find(); + + if ($cnt > 0) { + + $this->element('h2', null, _('Remove OpenID')); + + if ($cnt == 1 && !$user->password) { + + $this->element('p', 'form_guide', + _('Removing your only OpenID '. + 'would make it impossible to log in! ' . + 'If you need to remove it, '. + 'add another OpenID first.')); + + if ($oid->fetch()) { + $this->elementStart('p'); + $this->element('a', array('href' => $oid->canonical), + $oid->display); + $this->elementEnd('p'); + } + + } else { + + $this->element('p', 'form_guide', + _('You can remove an OpenID from your account '. + 'by clicking the button marked "Remove".')); + $idx = 0; + + while ($oid->fetch()) { + $this->elementStart('form', + array('method' => 'POST', + 'id' => 'form_settings_openid_delete' . $idx, + 'class' => 'form_settings', + 'action' => + common_local_url('openidsettings'))); + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->element('a', array('href' => $oid->canonical), + $oid->display); + $this->element('input', array('type' => 'hidden', + 'id' => 'openid_url'.$idx, + 'name' => 'openid_url', + 'value' => $oid->canonical)); + $this->element('input', array('type' => 'submit', + 'id' => 'remove'.$idx, + 'name' => 'remove', + 'class' => 'submit remove', + 'value' => _('Remove'))); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + $idx++; + } + } + } + } + + /** + * Handle a POST request + * + * Muxes to different sub-functions based on which button was pushed + * + * @return void + */ + + function handlePost() + { + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + if ($this->arg('add')) { + $result = oid_authenticate($this->trimmed('openid_url'), + 'finishaddopenid'); + if (is_string($result)) { // error message + $this->showForm($result); + } + } else if ($this->arg('remove')) { + $this->removeOpenid(); + } else { + $this->showForm(_('Something weird happened.')); + } + } + + /** + * Handles a request to remove an OpenID from the user's account + * + * Validates input and, if everything is OK, deletes the OpenID. + * Reloads the form with a success or error notification. + * + * @return void + */ + + function removeOpenid() + { + $openid_url = $this->trimmed('openid_url'); + + $oid = User_openid::staticGet('canonical', $openid_url); + + if (!$oid) { + $this->showForm(_('No such OpenID.')); + return; + } + $cur = common_current_user(); + if (!$cur || $oid->user_id != $cur->id) { + $this->showForm(_('That OpenID does not belong to you.')); + return; + } + $oid->delete(); + $this->showForm(_('OpenID removed.'), true); + return; + } +} diff --git a/plugins/OpenID/openidtrust.php b/plugins/OpenID/openidtrust.php new file mode 100644 index 000000000..29c7bdc23 --- /dev/null +++ b/plugins/OpenID/openidtrust.php @@ -0,0 +1,142 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; +require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + +class OpenidtrustAction extends Action +{ + var $trust_root; + var $allowUrl; + var $denyUrl; + var $user; + + /** + * Is this a read-only action? + * + * @return boolean false + */ + + function isReadOnly($args) + { + return false; + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return _('OpenID Identity Verification'); + } + + function prepare($args) + { + parent::prepare($args); + common_ensure_session(); + $this->user = common_current_user(); + if(empty($this->user)){ + /* Go log in, and then come back. */ + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + return; + } + $this->trust_root = $_SESSION['openid_trust_root']; + $this->allowUrl = $_SESSION['openid_allow_url']; + $this->denyUrl = $_SESSION['openid_deny_url']; + if(empty($this->trust_root) || empty($this->allowUrl) || empty($this->denyUrl)){ + $this->clientError(_('This page should only be reached during OpenID processing, not directly.')); + return; + } + return true; + } + + function handle($args) + { + parent::handle($args); + if($_SERVER['REQUEST_METHOD'] == 'POST'){ + $this->handleSubmit(); + }else{ + $this->showPage(); + } + } + + function handleSubmit() + { + unset($_SESSION['openid_trust_root']); + unset($_SESSION['openid_allow_url']); + unset($_SESSION['openid_deny_url']); + if($this->arg('allow')) + { + //save to database + $user_openid_trustroot = new User_openid_trustroot(); + $user_openid_trustroot->user_id = $this->user->id; + $user_openid_trustroot->trustroot = $this->trust_root; + $user_openid_trustroot->created = DB_DataObject_Cast::dateTime(); + if (!$user_openid_trustroot->insert()) { + $err = PEAR::getStaticProperty('DB_DataObject','lastError'); + common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); + } + common_redirect($this->allowUrl, $code=302); + }else{ + common_redirect($this->denyUrl, $code=302); + } + } + + /** + * Show page notice + * + * Display a notice for how to use the page, or the + * error if it exists. + * + * @return void + */ + + function showPageNotice() + { + $this->element('p',null,sprintf(_('%s has asked to verify your identity. Click Continue to verify your identity and login without creating a new password.'),$this->trust_root)); + } + + /** + * Core of the display code + * + * Shows the login form. + * + * @return void + */ + + function showContent() + { + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_openidtrust', + 'class' => 'form_settings', + 'action' => common_local_url('openidtrust'))); + $this->elementStart('fieldset'); + $this->submit('allow', _('Continue')); + $this->submit('deny', _('Cancel')); + + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } +} |