Index: branches/extensions-realtime/IdentityApi/IdentityApi.i18n.php |
— | — | @@ -0,0 +1,19 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Internationalization file for IdentityApi extension. |
| 6 | + * |
| 7 | + * @file IdentityApi.i18n.php |
| 8 | + * |
| 9 | + * @licence GNU GPL v2 or later |
| 10 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 11 | + */ |
| 12 | + |
| 13 | +$messages = array(); |
| 14 | + |
| 15 | +/** English |
| 16 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 17 | + */ |
| 18 | +$messages['en'] = array( |
| 19 | + 'identityapi-desc' => 'Provides API methods that help a remote service verify the identity of a user on this wiki' |
| 20 | +); |
Property changes on: branches/extensions-realtime/IdentityApi/IdentityApi.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 21 | + native |
Index: branches/extensions-realtime/IdentityApi/IdentityApi.php |
— | — | @@ -0,0 +1,58 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Provides a means for users to verify their identity on this wiki to a remote service. |
| 6 | + * |
| 7 | + * For example: imagine if you wanted to associate a chat service with your wiki, and users should have the same names on |
| 8 | + * the chat service as they do on the wiki. This isn't as complicated of a case as OpenID or oAuth, we just want two well-known services |
| 9 | + * run by the same organization to verify identity. |
| 10 | + * |
| 11 | + * This could also be done with a shared secret, but this way we can avoid having to distribute that secret or keep it up to date, and there |
| 12 | + * aren't any consequences if the secret is discovered. |
| 13 | + * |
| 14 | + * Documentation: http://www.mediawiki.org/wiki/Extension:IdentityApi |
| 15 | + * Source code: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/IdentityApi |
| 16 | + * |
| 17 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 18 | + * @license GPL v2 or later |
| 19 | + */ |
| 20 | + |
| 21 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 22 | + die( 'Not an entry point.' ); |
| 23 | +} |
| 24 | + |
| 25 | +if ( version_compare( $wgVersion, '1.17', '<' ) ) { |
| 26 | + die( 'This extension requires MediaWiki 1.17 or above.' ); |
| 27 | +} |
| 28 | + |
| 29 | +if ( ! $wgSessionsInMemcached ) { |
| 30 | + die( 'This extension requires you to be storing sessions in Memcached (for now)' ); |
| 31 | +} |
| 32 | + |
| 33 | + |
| 34 | +define( 'IdentityApi_VERSION', '0.1' ); |
| 35 | + |
| 36 | +$wgExtensionCredits['other'][] = array( |
| 37 | + 'path' => __FILE__, |
| 38 | + 'name' => 'IdentityApi', |
| 39 | + 'version' => IdentityApi_VERSION, |
| 40 | + 'author' => array( |
| 41 | + '[http://www.mediawiki.org/wiki/User:NeilK NeilK]', |
| 42 | + ), |
| 43 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:IdentityApi', |
| 44 | + 'descriptionmsg' => 'identityapi-desc' |
| 45 | +); |
| 46 | + |
| 47 | +$wgExtensionMessagesFiles['IdentityApi'] = dirname( __FILE__ ) . '/IdentityApi.i18n.php'; |
| 48 | + |
| 49 | +$wgAutoloadClasses['IdentityApi'] = dirname( __FILE__ ) . '/IdentityApi.class.php'; |
| 50 | + |
| 51 | +$wgAutoloadClasses['ApiQueryVerifyIdentity'] = dirname( __FILE__ ) . '/api/ApiQueryVerifyIdentity.php'; |
| 52 | +$wgAPIModules['verifyidentity'] = 'ApiQueryVerifyIdentity'; |
| 53 | +$wgAutoloadClasses['ApiQueryIdentity'] = dirname( __FILE__ ) . '/api/ApiQueryIdentity.php'; |
| 54 | +$wgAPIModules['identity'] = 'ApiQueryIdentity'; |
| 55 | + |
| 56 | + |
| 57 | +$wgIdentityApiConfig = array( |
| 58 | + 'timeoutSeconds' => 60 * 60 * 24 // one day |
| 59 | +); |
Property changes on: branches/extensions-realtime/IdentityApi/IdentityApi.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 60 | + native |
Index: branches/extensions-realtime/IdentityApi/api/ApiQueryIdentity.php |
— | — | @@ -0,0 +1,79 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * API module to allow a non-mediawiki service to know that a certain user is who s/he claims to be on this wiki. |
| 6 | + * See the README for what this extension is and isn't. |
| 7 | + * |
| 8 | + * @file ApiQueryIdentity.php |
| 9 | + * |
| 10 | + * @licence GNU GPL v2 or later |
| 11 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 12 | + */ |
| 13 | +class ApiQueryIdentity extends ApiQueryBase { |
| 14 | + public function __construct( $main, $action ) { |
| 15 | + parent::__construct( $main, $action, 'vi' ); |
| 16 | + } |
| 17 | + |
| 18 | + /** |
| 19 | + * Obtain data we can pass for the purposes of verifying a user later |
| 20 | + */ |
| 21 | + public function execute() { |
| 22 | + $auth = IdentityApi::getAuthParams( $user ); |
| 23 | + |
| 24 | + if ( $auth === null ) { |
| 25 | + $this->dieUsage( 'Could not obtain auth credentials - are you logged in?', 'mustbeloggedin' ); |
| 26 | + } |
| 27 | + |
| 28 | + // a wiki is identified by the root URL of the API -- everything else about a wiki is |
| 29 | + // dependent on Apache configuration |
| 30 | + foreach ( $auth as $key => $val ) { |
| 31 | + $this->getResult()->addValue( 'authentication', $key, $val ); |
| 32 | + } |
| 33 | + |
| 34 | + } |
| 35 | + |
| 36 | + /** |
| 37 | + * ???? do we need this? XSRF? TODO |
| 38 | + * Indicates whether this module must be called with a POST request |
| 39 | + * @return bool |
| 40 | + */ |
| 41 | + public function mustBePosted() { |
| 42 | + return false; |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * Probably needed to avoid XSRF -- TODO |
| 47 | + * Returns whether this module requires a Token to execute |
| 48 | + * @return bool |
| 49 | + */ |
| 50 | + public function needsToken() { |
| 51 | + return false; |
| 52 | + } |
| 53 | + |
| 54 | + public function getAllowedParams() { |
| 55 | + return array(); |
| 56 | + } |
| 57 | + |
| 58 | + public function getParamDescription() { |
| 59 | + return array(); |
| 60 | + } |
| 61 | + |
| 62 | + public function getDescription() { |
| 63 | + return 'API module to allow a non-mediawiki service to know that a certain user is who s/he claims to be on this wiki'; |
| 64 | + } |
| 65 | + |
| 66 | + public function getPossibleErrors() { |
| 67 | + return array(); |
| 68 | + } |
| 69 | + |
| 70 | + protected function getExamples() { |
| 71 | + return array ( |
| 72 | + 'api.php?action=identity' |
| 73 | + ); |
| 74 | + } |
| 75 | + |
| 76 | + public function getVersion() { |
| 77 | + return __CLASS__ . ': $Id: $'; |
| 78 | + } |
| 79 | + |
| 80 | +} |
Property changes on: branches/extensions-realtime/IdentityApi/api/ApiQueryIdentity.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 81 | + native |
Index: branches/extensions-realtime/IdentityApi/api/ApiQueryVerifyIdentity.php |
— | — | @@ -0,0 +1,89 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * API module to allow a non-mediawiki service to know that a certain user is who s/he claims to be on this wiki. |
| 6 | + * See the README for what this extension is and isn't. |
| 7 | + * |
| 8 | + * @file ApiQueryVerifyIdentity.php |
| 9 | + * |
| 10 | + * @licence GNU GPL v2 or later |
| 11 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 12 | + */ |
| 13 | +class ApiQueryVerifyIdentity extends ApiQueryBase { |
| 14 | + public function __construct( $main, $action ) { |
| 15 | + parent::__construct( $main, $action, 'vi' ); |
| 16 | + } |
| 17 | + |
| 18 | + /** |
| 19 | + * Verify that a user has a certain active login session. |
| 20 | + */ |
| 21 | + public function execute() { |
| 22 | + $params = $this->extractRequestParams(); |
| 23 | + $result = array( 'verified' => false ); |
| 24 | + |
| 25 | + // check if this user is a user who can login |
| 26 | + if ( User::isUsableName( $params['user'] ) ) { |
| 27 | + // and even exists |
| 28 | + $userId = User::idFromName( $params['user'] ); |
| 29 | + if ( $userId !== null ) { |
| 30 | + $user = User::newFromId( $userId ); |
| 31 | + $token = $params['token']; |
| 32 | + $extras = preg_split( '/\|/', $params['extras'] ); |
| 33 | + $result = IdentityApi::verifyAuthParams( $user, $token, $extras ); |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + foreach ( $result as $key => $val ) { |
| 38 | + $this->getResult()->addValue( 'authentication', $key, $val ); |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + public function getAllowedParams() { |
| 43 | + return array ( |
| 44 | + 'user' => array( |
| 45 | + ApiBase::PARAM_TYPE => 'string', |
| 46 | + ApiBase::PARAM_REQUIRED => true, |
| 47 | + ), |
| 48 | + 'token' => array( |
| 49 | + ApiBase::PARAM_TYPE => 'string', |
| 50 | + ApiBase::PARAM_REQUIRED => true, |
| 51 | + ), |
| 52 | + 'extras' => array( |
| 53 | + ApiBase::PARAM_TYPE => 'string', |
| 54 | + ApiBase::PARAM_REQUIRED => false, |
| 55 | + ApiBase::PARAM_DFLT => '', |
| 56 | + ) |
| 57 | + ); |
| 58 | + } |
| 59 | + |
| 60 | + public function getParamDescription() { |
| 61 | + return array ( |
| 62 | + 'token' => 'The token that the user offers as proof of their identity; obtained through an identity API query', |
| 63 | + 'user' => 'The username the user claims to have on this wiki', |
| 64 | + 'extras' => 'If verified, extra information to return about the user or the wiki, delimited by pipe (|) characters.' |
| 65 | + . ' Possible values: avatarSrc, chat, cookie, isLoggedIn, isStaff, username, wgServer, wgArticlePath', |
| 66 | + ); |
| 67 | + } |
| 68 | + |
| 69 | + public function getDescription() { |
| 70 | + return 'API module to allow a non-mediawiki service to know that a certain user is who s/he claims to be on this wiki'; |
| 71 | + } |
| 72 | + |
| 73 | + public function getPossibleErrors() { |
| 74 | + return array_merge( parent::getPossibleErrors(), array( |
| 75 | + array( 'missingparam', 'token' ), |
| 76 | + array( 'missingparam', 'user' ) |
| 77 | + ) ); |
| 78 | + } |
| 79 | + |
| 80 | + protected function getExamples() { |
| 81 | + return array ( |
| 82 | + 'api.php?action=verifyidentity&viuser=NeilK&vitoken=abcdef123456&viextras=avatarSrc|chat|cookie|isStaff|username', |
| 83 | + ); |
| 84 | + } |
| 85 | + |
| 86 | + public function getVersion() { |
| 87 | + return __CLASS__ . ': $Id: $'; |
| 88 | + } |
| 89 | + |
| 90 | +} |
Property changes on: branches/extensions-realtime/IdentityApi/api/ApiQueryVerifyIdentity.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 91 | + native |
Index: branches/extensions-realtime/IdentityApi/IdentityApi.class.php |
— | — | @@ -0,0 +1,116 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Static class for general functions of the IdentityApi extension. |
| 6 | + * |
| 7 | + * @file IdentityApi.classphp |
| 8 | + * |
| 9 | + * @licence GNU GPL v2 or later |
| 10 | + * @author Neil Kandalgaonkar <neilk@wikimedia.org> |
| 11 | + */ |
| 12 | + |
| 13 | +class IdentityApi { |
| 14 | + |
| 15 | + /** |
| 16 | + * Return authentication parameters for currently logged in user, or null |
| 17 | + * @return {Null|Array} |
| 18 | + */ |
| 19 | + public static function getAuthParams() { |
| 20 | + global $wgUser, $wgServer, $wgScriptPath, $wgMemc, $wgIdentityApiConfig; |
| 21 | + |
| 22 | + if ( !( $wgUser && $wgUser->isLoggedIn() ) ) { |
| 23 | + return null; |
| 24 | + } |
| 25 | + |
| 26 | + $data = array( |
| 27 | + 'user_id' => $wgUser->getId(), |
| 28 | + 'cookie' => $_COOKIE, |
| 29 | + ); |
| 30 | + $token = $wgUser->generateToken(); |
| 31 | + |
| 32 | + $wgMemc->set( wfMemcKey( $token ), $data, $wgIdentityApiConfig['timeoutSeconds'] ); |
| 33 | + |
| 34 | + return array( |
| 35 | + 'apiurl' => $wgServer . $wgScriptPath . '/api.php', |
| 36 | + 'username' => $wgUser->getName(), |
| 37 | + 'token' => $token |
| 38 | + ); |
| 39 | + } |
| 40 | + |
| 41 | + /** |
| 42 | + * Verifies that this user is logged in -- using memcached |
| 43 | + * @param User |
| 44 | + * @param String token, which we created in getAuth() |
| 45 | + * @param Array extra queries, array of strings that represent what other info to return |
| 46 | + * @return Array key-val, including 'verified' => boolean, and any other properties that may have |
| 47 | + * been asked for in $extra |
| 48 | + */ |
| 49 | + public static function verifyAuthParams( $user, $token, $extras ) { |
| 50 | + global $wgMemc; |
| 51 | + $ret = array( 'verified' => false ); |
| 52 | + $data = $wgMemc->get( wfMemcKey( $token ) ); |
| 53 | + if ( isset( $data ) and $data !== null and $data['user_id'] ) { |
| 54 | + $ret['verified'] = ( intval( $user->getId() ) === intval( $data['user_id'] ) ); |
| 55 | + } |
| 56 | + if ( $ret['verified'] ) { |
| 57 | + $ret = array_merge( $ret, IdentityApi::getExtraInfo( $user, $data, $extras ) ); |
| 58 | + } |
| 59 | + return $ret; |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Gets additional information, as specified in $extras |
| 64 | + * @param User |
| 65 | + * @param Array (data as was stored) |
| 66 | + * @param Array (array of strings, options for what data to return); |
| 67 | + */ |
| 68 | + public static function getExtraInfo( $user, $data, $extras ) { |
| 69 | + global $wgServer, $wgArticlePath; |
| 70 | + $ret = array(); |
| 71 | + foreach ( $extras as $extra ) { |
| 72 | + $extraInfo = array(); |
| 73 | + if ( preg_match( '/^avatarSrc=(\\d+)$/', $extra, $matches ) ) { |
| 74 | + if ( class_exists( 'AvatarService' ) ) { |
| 75 | + $avatarSize = intval( $matches[1] ); |
| 76 | + $ret['avatarSrc'] = AvatarService::getAvatarUrl( $user->getName(), $avatarSize ); |
| 77 | + } |
| 78 | + } |
| 79 | + switch ( $extra ) { |
| 80 | + case 'chat': |
| 81 | + if ( class_exists( 'Chat' ) ) { |
| 82 | + $canChat = Chat::canChat( $user ); |
| 83 | + if ( $canChat ) { |
| 84 | + $ret['canChat'] = $canChat; |
| 85 | + $ret['isChatMod'] = $user->isAllowed( 'chatmoderator' ); |
| 86 | + $userChangeableGroups = $user->changeableGroups(); // php makes us use a temp variable here, sigh |
| 87 | + $ret['isCanGiveChatMode'] = in_array( 'chatmoderator', $userChangeableGroups['add'] ); |
| 88 | + // TODO if still relevant, check for cross-wiki chat hack (see https://svn.wikia-code.com/wikia/trunk/extensions/wikia/Chat/ChatAjax.class.php ) |
| 89 | + // TODO get user stats for chat (see https://svn.wikia-code.com/wikia/trunk/extensions/wikia/Chat/ChatAjax.class.php ) |
| 90 | + } |
| 91 | + } |
| 92 | + break; |
| 93 | + case 'cookie': |
| 94 | + $ret['cookie'] = $data['cookie']; |
| 95 | + break; |
| 96 | + case 'isLoggedIn': |
| 97 | + $ret['isLoggedIn'] = $user->isLoggedIn(); |
| 98 | + break; |
| 99 | + case 'isStaff': |
| 100 | + // Wikia-specific |
| 101 | + $ret['isStaff'] = $user->isAllowed( 'staff' ); |
| 102 | + break; |
| 103 | + case 'username': |
| 104 | + $ret['username'] = $user->getName(); |
| 105 | + break; |
| 106 | + case 'wgServer': |
| 107 | + $ret['wgServer'] = $wgServer; |
| 108 | + case 'wgArticlePath': |
| 109 | + $ret['wgArticlePath'] = $wgArticlePath; |
| 110 | + default: |
| 111 | + break; |
| 112 | + } |
| 113 | + } |
| 114 | + return $ret; |
| 115 | + } |
| 116 | + |
| 117 | +} |
Property changes on: branches/extensions-realtime/IdentityApi/IdentityApi.class.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 118 | + native |
Index: branches/extensions-realtime/IdentityApi/README |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +This extension is experimental. It should not be used by anyone in a production environment. |
| 3 | + |
| 4 | +The purpose of this extension is to act as a sort of identity helper for other non-MediaWiki services that might be |
| 5 | +run in the same hosting environment. An example might be a chat server, where it is desirable that users have the same |
| 6 | +user name on the chat as they do on the wiki (and that users cannot impersonate one another). |
| 7 | + |
| 8 | +This implementation is based on Wikia's Chat module AJAX functions[1], but tries to be more general. |
| 9 | + |
| 10 | +-- Neil Kandalgaonkar <neilk@wikimedia.org> 10 Aug 2011 |
| 11 | + |
| 12 | + |
| 13 | +[1] See https://svn.wikia-code.com/wikia/trunk/extensions/wikia/Chat/ChatAjax.class.php |