r94219 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r94218‎ | r94219 | r94220 >
Date:01:50, 11 August 2011
Author:neilk
Status:deferred
Tags:
Comment:
experimental identity api, so chat server / etherpad can authenticate a user as being logged into the wiki via MediaWiki api
Modified paths:
  • /branches/extensions-realtime/IdentityApi (added) (history)
  • /branches/extensions-realtime/IdentityApi/IdentityApi.class.php (added) (history)
  • /branches/extensions-realtime/IdentityApi/IdentityApi.i18n.php (added) (history)
  • /branches/extensions-realtime/IdentityApi/IdentityApi.php (added) (history)
  • /branches/extensions-realtime/IdentityApi/README (added) (history)
  • /branches/extensions-realtime/IdentityApi/api (added) (history)
  • /branches/extensions-realtime/IdentityApi/api/ApiQueryIdentity.php (added) (history)
  • /branches/extensions-realtime/IdentityApi/api/ApiQueryVerifyIdentity.php (added) (history)

Diff [purge]

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
121 + 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
160 + 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
181 + 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
191 + 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
1118 + 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

Status & tagging log