Módosítások

MediawikiShibAuthWithPersistentID

1 408 bájt hozzáadva, 2011. április 5., 14:17
nincs szerkesztési összefoglaló
This extension is based on the original [http://www.mediawiki.org/wiki/Extension:Shibboleth_Authentication Extension:Shibboleth Authentication], the basic information will not be copied, here you can find the differences and the explanation of these differences.
 
The main object of this development is to make the extension support opaque persistent-id. Persistent-id could come from the Identity Provider (IdP), where the user has been authenticated, as value of persistent nameid, or as value of eduPersonTargetedID attribute. From the view of the mediawiki the route is, how the persistent-id is coming, irrelevant, for the mediawiki it is given by the Service Provider (SP). [https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPTargetedID More about persistent-id]
 
The main point is that persistent-id meets the privacy requirements much better than e.g. if the mediawiki gets e-mail address of the user, and it is used as local username.
 
== Preparation ==
 
You have to add an SQL table to be able to pair persistent-id and the local-id of the user.
 
<source lang="sql">
 
CREATE TABLE IF NOT EXISTS `wm_user_persistentid` (
`userID` int(10) NOT NULL,
`persistentID` varchar(255) NOT NULL,
UNIQUE KEY `userID` (`userID`,`persistentID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
 
</source>
== LocalSettings.php ==
/**
* Version 1.2.3 (Works out of box with MW 1.13 7 or above)
*
* Authentication Plugin for Shibboleth (http://shibboleth.internet2.edu)
* * D.J. Capelis - Developed initial version of the extension
*/
 
require_once('AuthPlugin.php');
class ShibAuthPlugin extends AuthPlugin {
var $existingUser = false;
/**
* Check whether there exists a user account with the given name.
return true;
}
/**
* Check if a username+password pair is a valid login.
*/
function authenticate( $username, $password) {
 
global $shib_UN;
return $username == $shib_UN;
 
}
/**
* Modify options in the login template.
*/
function modifyUITemplate( &$template ) {
$template->set('useemail', false);
$template->set('remember', false);
$template->set('domain', false);
$template->set( 'usedomain', false );
}
/**
* Set the domain this plugin is supposed to use when authenticating.
$this->domain = $domain;
}
/**
* Check to see if the specific domain is a valid domain.
return true;
}
/**
* When a user logs in, optionally fill in preferences and such.
function updateUser( &$user ) {
wfRunHooks('ShibUpdateUser', array($this->existingUser, $user));
//For security, set password to a non-existant hash.
if ($user->mPassword != "nologin") {
$user->mPassword = "nologin";
}
$user->setOption('rememberpassword', 0);
$user->saveSettings();
return true;
}
/**
* Return true if the wiki should create a new local account automatically
return true;
}
/**
* Can users change their passwords?
function allowPasswordChange() {
global $shib_pretend;
return $shib_pretend;
}
/**
* Set the given password in the authentication database.
function setPassword( $password ) {
global $shib_pretend;
return $shib_pretend;
}
/**
* Update user information in the external authentication database.
return true;
}
/**
* Check to see if external accounts can be created.
return false;
}
/**
* Add a user to the external authentication database.
return false;
}
/**
* Return true to prevent logins that don't authenticate here from being
return false;
}
/**
* When creating a user account, optionally fill in preferences and such.
$this->updateUser($user);
}
/**
* If you want to munge the case of an account name before the final
return $username;
}
}
 
function ShibGetAuthHook() {
 
global $wgVersion;
global $wgUser;
 
if ($wgVersion >= "1.13") {
if ($wgUser) {
// It does not work properly... The object is that if we have
// session from a logged in user, then the mediawiki does not use
// the database, load only from session
return 'UserLoadAfterLoadFromSession';
} else {
return 'UserLoadFromSession';
}
} else {
return 'AutoAuthenticate';
}
 
}
 
/*
* End of AuthPlugin Code, beginning of hook code and auth functions
*/
 
$wgExtensionFunctions[] = 'SetupShibAuth';
$wgExtensionCredits['other'][] = array(
'name' => 'Shibboleth Authentication',
'version' => '1.2.3',
'author' => "Regents of the University of California, Steven Langenaken",
'url' => "http://www.mediawiki.org/wiki/Extension:Shibboleth_Authentication",
'description' => "Allows logging in through Shibboleth",
);
 
function SetupShibAuth() {
global $shib_UN;
global $shib_persistentid;
 
global $wgHooks;
global $wgAuth;
global $wgCookieExpiration;
 
if ( $shib_persistentid == null && $shib_UN == null ) {
 
$wgHooks['PersonalUrls'][] = 'ShibLinkAdd';
 
} else {
 
$wgCookieExpiration = -3600;
$wgHooks[ShibGetAuthHook()][] = "ShibUserLoadFromSession";
$wgHooks['PersonalUrls'][] = 'ShibActive'; /* Disallow logout link */
$wgAuth = new ShibAuthPlugin();
 
}
 
}
function ShibGetUserIDFromPersistentID( $persistentID ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->selectRow( "user_persistentid", array( "userID" ), array( "persistentID" => $persistentID ) );
// If the user already has been here, the return value is the user_id, otherwise false
return ( $res ) ? $res->userID : false;
}
function ShibAddUserPersistentID( $userID, $persistentID ) {
$dbr = wfGetDB( DB_MASTER );
// If adding the persistent-id to the databas was success, return true, otherwise false
return $dbr->insert( "user_persistentid", array( "userID" => $userID, "persistentID" => $persistentID ) );
}
function ifUserAlreadyInLocalDB ( $username ) {
// Does the user already have local account?
return ( User::idFromName( $username ) != null && User::idFromName( $username ) != 0 ) ? User::idFromName( $username ) : false;
}
function setupUserAlreadyInLocalDB ( $username ) {
// We know the user, so it is high time to set him up
global $wgAuth;
$user = User::newFromName( ucfirst( $username ) );
$user->load();
$user->setupSession();
$user->setCookies();
return true;
}
function addUserLocalDBwithBlackMagic ( &$user, $username ) {
global $shib_pretend;
global $wgRedirectOnLogin;
/*We have to add a new local user to the database. We know * Since we only get called when someone should be logged inthe username, if they * aren't let's make that happen. Oddly enough the way MW does all * this is simply to use and have a loginForm class that pretty much does * most of what you needprepared user object. Creating We "make" a loginform is a very very smalllogin * part of this objectform, fill and send it, then set the user up.
*/
require_once('specials/SpecialUserlogin.php');
//This section contains a silly hack for MW
global $wgLang;
global $wgRequest;
$wgLangUnset = false;
if(!isset($wgLang)) {
$wgLang = $wgContLang;
$wgLangUnset = true;
}
ShibKillAA();
//This creates our form that'll do black magic
$lf = new LoginForm($wgRequest);
  //Place the hook back (Not strictly necessarily MW Ver >= 1.9) //ShibBringBackAA(); 
//And now we clean up our hack
if($wgLangUnset == true) {
unset($wgLangUnset);
}
//Okay, kick this up a notch then...
$user->setName( ucfirst( $username ) );
//The mediawiki developers entirely broke use of this the //straightforward way in 1.9, so now we just lie...
$shib_pretend = true;
//Now we _do_ the black magic
$lf->mRemember = false;
$user->loadDefaults( ucfirst( $username ) );
$lf->initUser($user, true);
//Stop pretending now
$shib_pretend = false;
//Finish it off
$user->saveSettings();
}
function ShibKillAA() { global $wgHooks; //Temporarily kill The AutoAuth Hook to prevent recursion foreach ($wgHooks[ShibGetAuthHook()] as $key => $value) { if($value == "Shib".ShibGetAuthHook()) $wgHooks[ShibGetAuthHook()][$key] = 'ShibBringBackAA'; }}/* Puts the auto-auth hook back into the hooks array */function ShibBringBackAA() { global $wgHooks; foreach ($wgHooks[ShibGetAuthHook()] as $key => $value) { if($value == 'ShibBringBackAA') $wgHooks[ShibGetAuthHook()][$key] = "Shib".ShibGetAuthHook(); } return true;} /* Add login link */function ShibLinkAdd(&$personal_urls, $title) { global $shib_WAYF, $shib_LoginHint, $shib_Https, $shib_AssertionConsumerServiceURL; global $shib_WAYFStyle; if (! isset($shib_AssertionConsumerServiceURL) || $shib_AssertionConsumerServiceURL == '') $shib_AssertionConsumerServiceURL = "/Shibboleth.sso"; if (! isset($shib_Https)) $shib_Https = false; if (! isset($shib_WAYFStyle)) $shib_WAYFStyle = 'DS'; if ($shib_WAYFStyle == 'DS') $shib_ConsumerPrefix = 'DS'; else $shib_ConsumerPrefix = ''; $pageurl = $title->getLocalUrl(); if (! isset($shib_LoginHint)) $shib_LoginHint = "Login via Single Sign-on"; $personal_urls['SSOlogin'] = array( 'text' => $shib_LoginHint, 'href' => ($shib_Https ? 'https' : 'http') .'://' . $_SERVER['HTTP_HOST'] . $shib_AssertionConsumerServiceURL . "/" . $shib_ConsumerPrefix . $shib_WAYF . '?target=' . (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $pageurl, ); return true;}  /* Kill logout link */function ShibActive(&$personal_urls) { global $shib_logout; global $shib_RN; if($shib_logout == null) $personal_urls['logout'] = null; else $personal_urls['logout']['href'] = $shib_logout; if ($shib_RN) $personal_urls['userpage']['text'] = $shib_RN; return true;} function ShibAutoAuthenticate(&$user) { ShibUserLoadAfterLoadFromSession($user);} /* * Tries to be magical about when to log in users and when not to. * * This is the main function. **/ 
function ShibUserLoadFromSession($user, &$result = true) {
global $shib_persistentid;
global $shib_UN;
global $wgRedirectOnLogin;
 
ShibKillAA();
//For versions of mediawiki which enjoy calling AutoAuth with null users
if ($user === null) {
$user = User::loadFromSession();
}
//They already with us? If so, nix this function, we're good.
if($user->isLoggedIn()) {
ShibBringBackAA();
return true;
}
if ( $shib_persistentid != null && $shib_UN == null ) {
// If the user only has persistentID, and his persistentID has not been in the DB yet,
// he will be handled as new user, even if he had local id created from username earlier.
// It is not possible to link the new account persID account with the old one without $shib_UN
if ( $userID = ShibGetUserIDFromPersistentID( $shib_persistentid ) ) {
//We know his persistentID and he has a linked local account
return setupUserAlreadyInLocalDB( User::whoIs( $userID ));
} else {
// We have to choose a local name for the new user, or develop
// to be able to modify userCreateTemplate and redirect the user there
$tempName = "TEMPUserName".substr(date("YssHIs")*rand(1,30)+"10101",0,10);
$wgRedirectOnLogin = "Special:RenameUser";
// Add and load the new user
addUserLocalDBwithBlackMagic( $user, $tempName );
// Add the persistentID
ShibAddUserPersistentID( ifUserAlreadyInLocalDB( $tempName ), $shib_persistentid );
return true;
}
} elseif ( $shib_persistentid != null && $shib_UN != null ) {
if ( ShibGetUserIDFromPersistentID( $shib_persistentid ) == ifUserAlreadyInLocalDB( $shib_UN ) ) {
// If so, the user has a local account and it has been already
// linked with his persistentID, so he can log in
return setupUserAlreadyInLocalDB( $shib_UN );
} elseif ( ShibAddUserPersistentID( ifUserAlreadyInLocalDB( $shib_UN ), $shib_persistentid ) ) {
// If we made the link between local account and persistentID
// then the user can log in
return setupUserAlreadyInLocalDB( $shib_UN );
} else {
// A brand new user
addUserLocalDBwithBlackMagic( $user, $shib_UN );
// If we managed to add a new local user, we have to link to the persistentID
ShibAddUserPersistentID( ifUserAlreadyInLocalDB( $shib_UN ), $shib_persistentid );
return true;
}
} elseif ( $shib_persistentid == null && $shib_UN != null ) {
//The old way...
if ( ifUserAlreadyInLocalDB( $shib_UN ) ) {
return setupUserAlreadyInLocalDB( $shib_UN );
} else {
return addUserLocalDBwithBlackMagic ( $user, $shib_UN );
}
}
return false;
}
function ShibKillAAShibGetAuthHook() { global $wgHookswgVersion; global $wgUser; //Temporarily kill The AutoAuth Hook to prevent recursion foreach if ($wgHooks[ShibGetAuthHook()] as $key wgVersion >=> $value"1.13") { if($value == "Shib".ShibGetAuthHook()wgUser){ return 'UserLoadAfterLoadFromSession'; } else { $wgHooks[ShibGetAuthHook()][$key] = return 'UserLoadFromSession'; } } else { return 'ShibBringBackAAAutoAuthenticate';
}
}
/* Puts the auto-auth hook back into the hooks array */function ShibBringBackAASetupShibAuth() { global $shib_UN; global $shib_persistentid;
global $wgHooks;
global $wgAuth; foreach (global $wgHooks[ShibGetAuthHookwgCookieExpiration; if ()] as $key shib_persistentid ==> null && $valueshib_UN == null ) { if( $value =wgHooks['PersonalUrls'][] = 'ShibBringBackAAShibLinkAdd'); } else { $wgCookieExpiration = -3600; $wgHooks[ShibGetAuthHook()][$key] = "ShibShibUserLoadFromSession".ShibGetAuthHook; $wgHooks['PersonalUrls'][] = 'ShibActive'; $wgAuth = new ShibAuthPlugin();
}
return true;} /* Add login link */function ShibLinkAdd(&$personal_urls, $title) { global $shib_WAYF, $shib_LoginHint, $shib_Https, $shib_AssertionConsumerServiceURL; global $shib_WAYFStyle; if (! isset($shib_AssertionConsumerServiceURL) || $shib_AssertionConsumerServiceURL == '') $shib_AssertionConsumerServiceURL = "/Shibboleth.sso"; if (! isset($shib_Https)) $shib_Https = false; if (! isset($shib_WAYFStyle)) $shib_WAYFStyle = 'DS'; if ($shib_WAYFStyle == 'DS') $shib_ConsumerPrefix = 'DS'; else $shib_ConsumerPrefix = ''; $pageurl = $title->getLocalUrl(); if (! isset($shib_LoginHint)) $shib_LoginHint = "Login via Single Sign-on";  $personal_urls['SSOlogin'] = array( 'text' => $shib_LoginHint, 'href' => ($shib_Https ? 'https' : 'http') .'://' . $_SERVER['HTTP_HOST'] . $shib_AssertionConsumerServiceURL . "/" . $shib_ConsumerPrefix . $shib_WAYF . '?target=' . (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $pageurl, ); return true;}  /* Kill logout link */function ShibActive(&$personal_urls, $title) { global $shib_logout; global $shib_RN; global $shib_map_info;  if($shib_logout == null) $personal_urls['logout'] = null; else $personal_urls['logout']['href'] = $shib_logout;  if ($shib_RN && $shib_map_info) $personal_urls['userpage']['text'] = $shib_RN;  return true;} function ShibAutoAuthenticate(&$user) { ShibUserLoadAfterLoadFromSession($user);
}
$wgExtensionFunctions[] = 'SetupShibAuth';
$wgExtensionCredits['other'][] = array(
'name' => 'Shibboleth Authentication',
'version' => '1.2.3',
'author' => "Regents of the University of California, Steven Langenaken",
'url' => "http://www.mediawiki.org/wiki/Extension:Shibboleth_Authentication",
'description' => "Allows logging in through Shibboleth",
);
</source>
 
== ToDo ==
* Testing - I tested only on MediaWiki 1.15.3, it worked properly
* Bugfix - If I modify UserLoadFromSession to UserLoadAfterLoadFromSession, the [http://www.mediawiki.org/wiki/Extension_talk:Shibboleth_Authentication#MW_1.15rc1 "reload bug"] is still with us :S
* Cleaning the code
* Anything else?
==After login==
If the user only has persistent-id, and it is the first time to login, he is given a temporary username, so he will be supposed to change it. To change username mediawiki needs an extension, called RenameUser.
We have to make a small modification on the extension. You can see the patch below, and [https://wiki.aai.niif.hu/images/1/1b/RenameUser-ShibAuth.patch download from here ] for the <code>SpecialRenameuser_body.php</code>.
<source lang="diff">
</source>
 
== ToDo ==
* Testing - I tested only on MediaWiki 1.15.3, it worked properly
* Bugfix - If I modify UserLoadFromSession to UserLoadAfterLoadFromSession, the [http://www.mediawiki.org/wiki/Extension_talk:Shibboleth_Authentication#MW_1.15rc1 "reload bug"] is still with us :S
* Cleaning the code
* Write SQL install script
* Anything else?

Navigációs menü