3
* SpecialOpenID.body.php -- Superclass for all
4
* Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
5
* Copyright 2008 by Evan Prodromou (http://evan.prodromou.name/)
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 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 General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
* @author Evan Prodromou <evan@prodromou.name>
22
* @addtogroup Extensions
25
# FIXME: for login(); figure out better way to share this code
26
# between Login and Convert
28
require_once("Auth/OpenID/Consumer.php");
29
require_once("Auth/OpenID/SReg.php");
30
require_once("Auth/OpenID/FileStore.php");
32
class SpecialOpenID extends SpecialPage {
34
function getOpenIDStore($storeType, $prefix, $options) {
37
# FIXME: support other kinds of store
38
# XXX: used to support memc, now use memcached from php-openid
43
# Auto-create path if it doesn't exist
44
if (!is_dir($options['path'])) {
45
if (!mkdir($options['path'], 0770, true)) {
46
$wgOut->showErrorPage('openidconfigerror', 'openidconfigerrortext');
50
return new Auth_OpenID_FileStore($options['path']);
53
$wgOut->showErrorPage('openidconfigerror', 'openidconfigerrortext');
57
function xriBase($xri) {
58
if (substr($xri, 0, 6) == 'xri://') {
59
return substr($xri, 6);
65
function xriToUrl($xri) {
66
return 'http://xri.net/' . OpenIDXriBase($xri);
69
static function OpenIDToUrl($openid) {
70
/* ID is either an URL already or an i-name */
71
if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
72
return OpenIDXriToUrl($openid);
78
function interwikiExpand($openid_url) {
79
# try to make it into a title object
80
$nt = Title::newFromText($openid_url);
81
# If it's got an iw, return that
82
if (!is_null($nt) && !is_null($nt->getInterwiki())
83
&& strlen($nt->getInterwiki()) > 0) {
84
return $nt->getFullUrl();
90
static function getUserUrl($user) {
93
if ( isset( $user ) && $user->getId() != 0 ) {
94
global $wgSharedDB, $wgDBprefix;
95
if ( isset( $wgSharedDB ) ) {
96
$tableName = "`${wgSharedDB}`.${wgDBprefix}user_openid";
98
$tableName = 'user_openid';
101
$dbr = wfGetDB( DB_SLAVE );
104
array( 'uoi_openid' ),
105
array( 'uoi_user' => $user->getId() ),
109
# This should return 0 or 1 result, since user is unique
112
while ( $row = $res->fetchObject() ) {
113
$openid_url = $row->uoi_openid;
122
function getConsumer() {
123
global $wgOpenIDConsumerStoreType, $wgOpenIDConsumerStorePath;
125
$store = $this->getOpenIDStore($wgOpenIDConsumerStoreType,
127
array('path' => $wgOpenIDConsumerStorePath));
129
return new Auth_OpenID_Consumer($store);
132
function fullUrl($title) {
133
$nt = Title::makeTitleSafe(NS_SPECIAL, $title);
135
return $nt->getFullURL();
141
function scriptUrl($title) {
142
global $wgServer, $wgScript;
143
$nt = Title::makeTitleSafe(NS_SPECIAL, $title);
145
$dbkey = wfUrlencode( $nt->getPrefixedDBkey() );
146
return "{$wgServer}{$wgScript}?title={$dbkey}";
152
function canLogin($openid_url) {
154
global $wgOpenIDConsumerDenyByDefault, $wgOpenIDConsumerAllow, $wgOpenIDConsumerDeny;
156
if ($this->isLocalUrl($openid_url)) {
160
if ($wgOpenIDConsumerDenyByDefault) {
162
foreach ($wgOpenIDConsumerAllow as $allow) {
163
if (preg_match($allow, $openid_url)) {
164
wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
166
foreach ($wgOpenIDConsumerDeny as $deny) {
167
if (preg_match($deny, $openid_url)) {
168
wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
178
foreach ($wgOpenIDConsumerDeny as $deny) {
179
if (preg_match($deny, $openid_url)) {
180
wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
182
foreach ($wgOpenIDConsumerAllow as $allow) {
183
if (preg_match($allow, $openid_url)) {
184
wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
196
function isLocalUrl($url) {
198
global $wgServer, $wgArticlePath;
200
$pattern = $wgServer . $wgArticlePath;
201
$pattern = str_replace('$1', '(.*)', $pattern);
202
$pattern = str_replace('?', '\?', $pattern);
204
return preg_match('|^' . $pattern . '$|', $url);
207
# Find the user with the given openid, if any
209
function getUser($openid) {
210
global $wgSharedDB, $wgDBprefix;
212
if (isset($wgSharedDB)) {
213
$tableName = "`$wgSharedDB`.${wgDBprefix}user_openid";
215
$tableName = 'user_openid';
218
$dbr =& wfGetDB( DB_SLAVE );
219
$id = $dbr->selectField($tableName, 'uoi_user',
220
array('uoi_openid' => $openid));
222
$name = User::whoIs($id);
223
return User::newFromName($name);
228
function login($openid_url, $finish_page = 'OpenIDFinish') {
230
global $wgUser, $wgTrustRoot, $wgOut;
232
# If it's an interwiki link, expand it
234
$openid_url = $this->interwikiExpand($openid_url);
236
# Check if the URL is allowed
238
if (!$this->canLogin($openid_url)) {
239
$wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
243
$sk = $wgUser->getSkin();
245
if (isset($wgTrustRoot)) {
246
$trust_root = $wgTrustRoot;
248
global $wgArticlePath, $wgServer;
249
$root_article = str_replace('$1', '', $wgArticlePath);
250
$trust_root = $wgServer . $root_article;
253
$consumer = $this->getConsumer();
256
$wgOut->showErrorPage('openiderror', 'openiderrortext');
260
# Make sure the user has a session!
262
global $wgSessionStarted;
264
if (!$wgSessionStarted) {
265
$wgUser->SetupSession();
268
$auth_request = $consumer->begin($openid_url);
270
// Handle failure status return values.
271
if (!$auth_request) {
272
$wgOut->showErrorPage('openiderror', 'openiderrortext');
276
# Check the processed URLs, too
278
$endpoint = $auth_request->endpoint;
280
if (isset($endpoint)) {
281
# Check if the URL is allowed
283
if (isset($endpoint->identity_url) && !$this->canLogin($endpoint->identity_url)) {
284
$wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
288
if (isset($endpoint->delegate) && !$this->canLogin($endpoint->delegate)) {
289
$wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
294
$sreg_request = Auth_OpenID_SRegRequest::build(
298
array('nickname','email',
299
'fullname','language','timezone'));
302
$auth_request->addExtension($sreg_request);
305
$process_url = $this->scriptUrl($finish_page);
307
if ($auth_request->shouldSendRedirect()) {
308
$redirect_url = $auth_request->redirectURL($trust_root,
310
if (Auth_OpenID::isFailure($redirect_url)) {
311
displayError("Could not redirect to server: " . $redirect_url->message);
314
$wgOut->redirect($redirect_url);
317
// Generate form markup and render it.
318
$form_id = 'openid_message';
319
$form_html = $auth_request->formMarkup($trust_root, $process_url,
320
false, array('id' => $form_id));
322
// Display an error if the form markup couldn't be generated;
323
// otherwise, render the HTML.
324
if (Auth_OpenID::isFailure($form_html)) {
325
displayError("Could not redirect to server: " . $form_html->message);
327
$wgOut->addHTML("<p>" . wfMsg("openidautosubmit") . "</p>");
328
$wgOut->addHTML($form_html);
329
$wgOut->addInlineScript("function submitOpenIDForm() {\n document.getElementById(\"".$form_id."\").submit()\n }\nhookEvent(\"load\", submitOpenIDForm);\n");
334
function setUserUrl($user, $url) {
335
$other = $this->getUserUrl($user);
337
$this->updateUserUrl($user, $url);
339
$this->insertUserUrl($user, $url);
343
function insertUserUrl($user, $url) {
344
global $wgSharedDB, $wgDBname;
345
$dbw =& wfGetDB( DB_MASTER );
347
if (isset($wgSharedDB)) {
348
# It would be nicer to get the existing dbname
349
# and save it, but it's not possible
350
$dbw->selectDB($wgSharedDB);
353
$dbw->insert('user_openid', array('uoi_user' => $user->getId(),
354
'uoi_openid' => $url));
356
if (isset($wgSharedDB)) {
357
$dbw->selectDB($wgDBname);
361
function updateUserUrl($user, $url) {
362
global $wgSharedDB, $wgDBname;
363
$dbw =& wfGetDB( DB_MASTER );
365
if (isset($wgSharedDB)) {
366
# It would be nicer to get the existing dbname
367
# and save it, but it's not possible
368
$dbw->selectDB($wgSharedDB);
371
$dbw->set('user_openid', 'uoi_openid', $url,
372
'uoi_user = ' . $user->getID());
374
if (isset($wgSharedDB)) {
375
$dbw->selectDB($wgDBname);