Skip to content

Instantly share code, notes, and snippets.

@Fannon
Last active October 9, 2015 13:04
Show Gist options
  • Save Fannon/ab4c83668ec8de071fec to your computer and use it in GitHub Desktop.
Save Fannon/ab4c83668ec8de071fec to your computer and use it in GitHub Desktop.
<?php
/**
* Version 1.1.3 (Works out of box with MW 1.7 or above)
* - Works out with MW 1.15.1 (tested)
*
* Authentication Plugin for Siteminder
* Derived from ShibAuthPlugin.php
* Much of the commenting comes straight from AuthPlugin.php
* Had to add return statements to several routines because a
* return value is required from hook routines.
*
* Portions Copyright 2006, 2007 Regents of the University of California.
* Portions Copyright 2007 Steven Langenaken
* Released under the GNU General Public License
*
* Extension Maintainer:
* * Virgil Green <virgil.green AT bunge DOT com>
* Extension Developers:
* * Virgil Green - Converted from Shibbeleth to Siteminder
*
* This extension assumes that the only method for signing on will
* be SiteMinder. Local signon is not allowed. Since validation always
* occurs before any part of the wiki is displayed, there are no
* provisions made for selecting the style (local or Siteminder) and any
* function that would be used for validation or synchronizatin is
* set to simply return as though successful (or failure, if needed/appropriate).
*
* Suggested LocalSettings.php code
* ##Siteminder Authentication
* #Load SiteminderPlugin
* require_once('extensions/SiteminderAuthPlugin.php');
*
* #Map data from Siteminder to local user data
* $siteminder_map_info = "true";
*
* #Ssssh.... quiet down errors
* $olderror = error_reporting(E_ALL ^ E_NOTICE);
*
* #Map Siteminder variables to extension variable
* # In this example, Siteminder is setting http headers named
* # full_name, user_name, and email when then appear in the $_SERVER array
* # in any of the other data collections that expose the headers to code.
* $siteminder_real_name = $_SERVER['HTTP_FULL_NAME'];
* $siteminder_user_name = ucfirst(strtolower($_SERVER['HTTP_USER_NAME']));
* $siteminder_email = $_SERVER['HTTP_EMAIL'];
*
* #Siteminder logoff URL (uncomment and set proper URL if you want a logout link)
* #This is expected to be an abslute URL, including the protocol
* #$siteminder_logout = "<URL to Siteminder logout page>";
*
* #Turn error reporting back on
* error_reporting($olderror);
*
* #Activate Siteminder Plugin
* SetupSiteminderAuth();
*
*/
require_once("$IP/includes/AuthPlugin.php");
class SiteminderAuthPlugin extends AuthPlugin {
var $existingUser = false;
/**
* Check whether there exists a user account with the given name.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* @param string $username
* @return bool
* @access public
*/
function userExists($username) {
return true;
}
/**
* Check if a username+password pair is a valid login.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* @param string $username
* @param string $password
* @return bool
* @access public
*/
function authenticate($username, $password) {
global $siteminder_user_name;
return $username == $siteminder_user_name;
}
/**
* Modify options in the login template.
*
* @param UserLoginTemplate $template
* @access public
*/
function modifyUITemplate(&$template, &$type) {
return;
}
/**
* Set the domain this plugin is supposed to use when authenticating.
*
* @param string $domain
* @access public
*/
function setDomain($domain) {
return;
}
/**
* Check to see if the specific domain is a valid domain.
*
* @param string $domain
* @return bool
* @access public
*/
function validDomain($domain) {
return true;
}
/**
* When a user logs in, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* @param User $user
* @access public
*/
function updateUser(&$user) {
global $siteminder_map_info;
global $siteminder_email;
global $siteminder_real_name;
if ($siteminder_map_info) {
if ($siteminder_email != null)
$user->setEmail($siteminder_email);
if ($siteminder_real_name != null)
$user->setRealName($siteminder_real_name);
}
//For security, set password to a non-existant hash.
if ($user->mPassword != "nologin") {
$user->mPassword = "nologin";
$user->saveSettings();
}
return true;
}
/**
* Return true if the wiki should create a new local account automatically
* when asked to login a user who doesn't exist locally but does in the
* external auth database.
*
* If you don't automatically create accounts, you must still create
* accounts in some way. It's not possible to authenticate without
* a local account.
*
* This is just a question, and shouldn't perform any actions.
*
* @return bool
* @access public
*/
function autoCreate() {
return true;
}
/**
* Can users change their passwords?
*
* @return bool
*/
function allowPasswordChange() {
global $siteminder_pretend;
return $siteminder_pretend;
}
/**
* Set the given password in the authentication database.
* Return true if successful.
*
* @param string $password
* @return bool
* @access public
*/
function setPassword($user, $password) {
global $siteminder_pretend;
return $siteminder_pretend;
}
/**
* Update user information in the external authentication database.
* Return true if successful.
*
* @param User $user
* @return bool
* @access public
*/
function updateExternalDB($user) {
return true;
}
/**
* Check to see if external accounts can be created.
* Return true if external accounts can be created.
* @return bool
* @access public
*/
function canCreateAccounts() {
return false;
}
/**
* Add a user to the external authentication database.
* Return true if successful.
*
* @param User $user
* @param string $password
* @return bool
* @access public
*/
function addUser($user, $password, $email = '', $realname = '') {
return true;
}
/**
* Return true to prevent logins that don't authenticate here from being
* checked against the local database's password fields.
*
* This is just a question, and shouldn't perform any actions.
*
* @return bool
* @access public
*/
function strict() {
return true;
}
/**
* When creating a user account, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* @param User $user
* @access public
*/
function initUser(&$user, $autocreate = false) {
$this->updateUser($user);
}
/**
* If you want to munge the case of an account name before the final
* check, now is your chance.
*/
function getCanonicalName($username) {
return $username;
}
}
function SiteminderGetAuthHook() {
global $wgVersion;
if (strcmp($wgVersion, "1.13") >= 0) {
return 'UserLoadAfterLoadFromSession';
} else {
return 'AutoAuthenticate';
}
}
/*
* End of AuthPlugin Code, beginning of hook code and auth functions
*/
function SetupSiteMinderAuth() {
global $siteminder_user_name;
global $wgHooks;
global $wgAuth;
if ($siteminder_user_name != null) {
$wgHooks[SiteminderGetAuthHook()][] = "Siteminder" . SiteminderGetAuthHook();
/* Hook for magical authN */
$wgHooks['PersonalUrls'][] = 'SiteminderSSOActive';
/* Disallow logout link */
$wgAuth = new SiteminderAuthPlugin();
}
}
/* Kill or replace logout link */
function SiteminderSSOActive(&$personal_urls, $title) {
global $siteminder_logout;
global $siteminder_real_name;
global $siteminder_map_info;
if ($siteminder_logout == null)
# $personal_urls['logout'] = null;
unset($personal_urls['logout']);
else
$personal_urls['logout']['href'] = $siteminder_logout;
if ($siteminder_real_name && $siteminder_map_info)
$personal_urls['userpage']['text'] = $siteminder_real_name;
return true;
}
function SiteminderAuthAuthenticate(&$user) {
SiteminderShibUserLoadFromSession($user, true);
}
/* Tries to be magical about when to log in users and when not to. */
function SiteminderUserLoadAfterLoadFromSession($user) {
global $wgContLang;
global $wgAuth;
global $siteminder_user_name;
global $wgHooks;
global $siteminder_map_info;
global $siteminder_pretend;
global $IP;
SiteminderKillAA();
// $context = $this->getContext();
// $request = $context->getRequest();
//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()) {
BringBackAA();
return true;
}
//Is the user already in the database?
$localId = User::idFromName($siteminder_user_name);
/* if ( $localId != null ) {
$user = User::newFromId( $localId );
$user->setID( $localId );
$user->loadFromId();
$user->setCookies();
$wgAuth->updateUser( $user );
wfSetupSession();
return true;
}
*/
//print_r($user->getRequest());
if (User::idFromName($siteminder_user_name) != null) {
$user = User::newFromName($siteminder_user_name);
$smi = $siteminder_map_info;
$siteminder_map_info = false;
$user->load();
$wgAuth->existingUser = true;
$wgAuth->updateUser($user); //Make sure password is nologin
$siteminder_map_info = $smi;
//$user->SetupSession();
wfSetupSession();
$user->setCookies();
// $user->invalidateCache(); // invalidating Cache has no effect (GEA)
/* print_r($user->getRequest()); /* uncomment for debugging (GEA) */
// Calculate 302 redirect URL
$redirecturl = $target->getFullUrl(Title::newMainPage());
// Get returnto value
$returnto = $wgRequest->getVal("returnto"); // TODO: Get this through $context
if ($returnto) {
$target = Title::newFromText($returnto);
if ($target) {
$redirecturl = $target->getFullUrl();
}
}
// Provide a 302 Redirect
// Otherwise the user will have to refresh one time manually
// in order to view the wiki in a logged in state
header('Location: ' . $redirecturl);
return true;
}
//Place the hook back (Not strictly necessarily MW Ver >= 1.9)
BringBackAA();
//Okay, kick this up a notch then...
$user->setName($wgContLang->ucfirst($siteminder_user_name));
/*
* Since we only get called when someone should be logged in, if they
* aren't let's make that happen. Oddly enough the way MW does all
* this is simply to use a loginForm class that pretty much does
* most of what you need. Creating a loginform is a very very small
* part of this object.
*/
require_once("$IP/includes/specials/SpecialUserlogin.php");
//This section contains a silly hack for MW
global $wgLang;
global $wgContLang;
global $wgRequest;
$wgLangUnset = false;
if (!isset($wgLang)) {
$wgLang = $wgContLang;
$wgLangUnset = true;
}
SiteminderKillAA();
// --> ext: hh[2014-07-18]
// --> make property mRemember accessible by using ReflectionClass:
$loginFormClass = new ReflectionClass("LoginForm");
$property = $loginFormClass->getProperty("mRemember");
$property->setAccessible(true);
// <-- ext: hh[2014-07-18]
//This creates our form that'll do black magic
$lf = new LoginForm($wgRequest);
//Place the hook back (Not strictly necessarily MW Ver >= 1.9)
BringBackAA();
//And now we clean up our hack
if ($wgLangUnset == true) {
unset($wgLang);
unset($wgLangUnset);
}
//The mediawiki developers entirely broke use of this the
//straightforward way in 1.9, so now we just lie...
$siteminder_pretend = true;
//Now we _do_ the black magic
// hh: $lf->mRemember = false;
// replaced by the following line ->
$property->setValue($lf, false);
// analyze class User:
// -> remove after debugging
/*$userClass = new ReflectionClass("User");
$methods = $userClass->getMethods();
foreach($methods as $m) {
print $m->name;
$m->isPrivate() ? print "Private " : print "";
$m->isPublic() ? print "Public " : print "";
$params = $m->getParameters();
foreach($params as $p) {
print $p->getName();
}
print "</br>\r\n";
}*/
// <- remove after debugging
// <-
$user->loadDefaults($siteminder_user_name);
$lf->initUser($user);
//Stop pretending now
$siteminder_pretend = false;
//Finish it off
$user->saveSettings();
// hh: guess the method setupSession should be SetupSession, wrong guess...
// uncomment following line, check what happens then
//$user->setupSession();
wfSetupSession();
$user->setCookies();
return true;
}
function SiteminderKillAA() {
global $wgHooks;
global $wgAuth;
//Temporarily kill The AutoAuth Hook to prevent recursion
foreach ($wgHooks[SiteminderGetAuthHook()] as $key => $value) {
if ($value == "Siteminder" . SiteminderGetAuthHook())
$wgHooks[SiteminderGetAuthHook()][$key] = 'BringBackAA';
}
}
/* Puts the auto-auth hook back into the hooks array */
function BringBackAA() {
global $wgHooks;
global $wgAuth;
foreach ($wgHooks[SiteminderGetAuthHook()] as $key => $value) {
if ($value == 'BringBackAA')
$wgHooks[SiteminderGetAuthHook()][$key] = "Siteminder" . SiteminderGetAuthHook();
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment