Manual talk:Hooks/UserLoadFromSession

And 15:01, 3 February 2009 (UTC) WARNING - there's a problem with user admin rights using this code - bug 17339 submitted and this page will be updated when it's fixed.

And 16:21, 21 January 2009 (UTC) I had some problems getting this to work and would have appreciated a sample, so now that I've done it, here's a bowdlerised version of my code. <?php function fnUserAuth($user, &$result) {

/* This is a slightly modified version of the code that I have implemented. I had a particular problem with group membership, in that this code was based on 		a modified version of User.php for MW 1.8.2 and it took a while to find out the problem, which was that you need to call saveToCache after loadFromDatabase as the group credentials are emptied in the latter.

There's code here that you won't need or will need amending, but I hope that it will serve as a useful example. */ /*	This function performs the user authentication using our SSO. It requires the following fields to be added to the default {prefix}_user table in the database: user_employee_id	tinytext user_market_group	tinytext

Ref:	http://www.mediawiki.org/wiki/Manual:Hooks/UserLoadFromSession http://svn.wikimedia.org/doc/classUser.html */ $fname  = "UserAuthSample::fnUserAuth"; $SSO   = "https://xxxxxxxxxxxx.com/login/sso/SSOService?app=wiki&returnURL="; $errpage = "http://wiki.xxxxxxx.com/wikiaccess.php"; $allowed = 300; # Five minutes diff is allowed. logFrodo("Page: " . $_SERVER['REQUEST_URI']); if (isset($_COOKIE['FrodoWiki'])) { $Frodocookie = $_COOKIE['FrodoWiki']; logFrodo("FrodoWiki cookie found");

if (isset($_REQUEST['digest'])) { logFrodo("digest exists as well - ignoring it."); }

loadFromDatabaseFrodo($user, $Frodocookie); $result = 1; // This causes the rest of the authentication process to be skipped. return(false);  // As should this, according to the internal error report: // Detected bug in an extension! Hook fnUserAuthFrodo failed to return a value; should return true to                // continue hook processing or false to abort.

} elseif (isset($_REQUEST['digest'])) { # Just back from SSO - validate the result. $digest     = $_REQUEST['digest']; $empid      = $_REQUEST['uid']; $email      = $_REQUEST['email']; $firstname  = $_REQUEST['firstname']; $lastname   = $_REQUEST['lastname']; $marketgroup = $_REQUEST['marketgroup']; $intime     = $_REQUEST['time']; $returnurl  = $_REQUEST['returnURL']; $thispage   = curPageURL; $mytime     = date("Y:m:d:H:i:s", time);

// Get the secret ... $file = fopen("/usr/local/apache2/htdocs/wiki/Frodo/conf/.SSOkey", "r") or exit("Unable to open file!"); $secret = chop(fgets($file),"\n"); # Drop trailing LF		fclose($file);

$md5string = "$empid$intime$secret"; $mydigest = md5($md5string); if ( "$digest" != "$mydigest" ) { // Problem! Log secure info to file: logFrodo("Digests don't match for $email - unable to proceed."); logFrodo(" digest=$digest"); logFrodo(" mydigest=$mydigest"); logFrodo(" md5string=>$md5string<"); // ... and tell the user something interesting: $errpage .= "?error=Digest%20mismatch"; $errpage .= "&digest=$digest&empid=$empid&email=$email&first=$firstname&last=$lastname&mg= \                                    $marketgroup&intime=$intime"; $errpage .= "&mytime=$mytime&returnurl=$returnurl"; // $errpage .= "&timediff=&timediff"; // redirect to report error ... http_redirect($errpage, array, true, HTTP_REDIRECT); }

$diff = valtime($intime); if ( $diff > $allowed ) { logFrodo("Time difference ($diff) is too great for $email"); logFrodo(" intime=$intime"); logFrodo(" mytime=$mytime"); // ... and tell the user something interesting: $errpage .= "?error=Time%20difference%20too%20great."; $errpage .= "&digest=$digest&empid=$empid&email=$email&first=$firstname&last=$lastname& \                                   mg=$marketgroup&intime=$intime"; $errpage .= "&mytime=$mytime&returnurl=$returnurl"; $errpage .= "&timediff=&diff"; // redirect to report error ... http_redirect($errpage, array, true, HTTP_REDIRECT); // $result not set so that authentication continues - and should fail. return(true); // This should have the same effect. }		logFrodo("SSO validation successful for $email"); # Validation successful - create cookie with all SSO fields. $Frodocookie = "$empid|$email|$firstname|$lastname|$marketgroup"; $Frodocookie = str_replace(' ', '%20', $Frodocookie); # We'll start with a week ... if ( setrawcookie("FrodoWiki", $Frodocookie, time+7*24*60*60, '/', $_SERVER['SERVER_NAME']) ) { logFrodo(" cookie set successfully."); } else { logFrodo(" cookie setting failed."); }		// Now see if the user is already in the database ... $user = loadFromDatabaseFrodo($user, $Frodocookie); $result = 1; // This causes the rest of the authentication process to be skipped. return(false); // Ditto (see above) } else {	// No cookie, so we go to SSO. //		logFrodo("$fname: No Frodocookie found - redirecting to SSO."); logFrodo("FrodoWiki cookie not found - redirecting to SSO.");

$SSO .= curPageURL; // Append this page's name http_redirect($SSO, array, true, HTTP_REDIRECT); }	// No cookie }

function loadFromDatabaseFrodo($user, $Frodocookie) { $fname  = "UserAuthFrodo::loadFromDatabaseFrodo"; // Check whether user is in the database - if so, complete User. logFrodo("Entering $fname ..."); // Explode the cookie: list ($empid, $email, $first, $last, $marketgroup) = explode("|", $Frodocookie, 5); logFrodo("Cookie exploded: $empid, $email, $first, $last, $marketgroup"); // Now see if the user is known ... $dbr =& wfGetDB( DB_SLAVE ); $s = $dbr->selectRow( 'user', array('user_id'), array('user_employee_id' => $empid), $fname); if ($s === false) { logFrodo("No entry found in db for employee id $empid - creating one ..."); $user = new User; // MediaWiki requires names to start with a capital, so we have a stab at a reasonably formed name: $temp = explode(".", substr($email,0,strpos($email,'@'))); $i  = 0; $lim = sizeof($temp); while ( $i < $lim) { $temp[$i] = ucwords($temp[$i]); $i++; }		$userName = implode(".", $temp); $user->loadDefaults($userName);        // Added as it's done this way in CentralAuth. $user->mEmail             = $email; $user->mName              = $userName; // Redundant given use of loadDefaults...? $user->mEmployeeId        = $empid; $user->mRealName          = "$first $last"; $user->mMarketGroup       = $marketgroup; $user->mEmailAuthenticated = wfTimestamp; $user->mTouched           = wfTimestamp; logFrodo(" mName               = $user->mName"); logFrodo(" mEmployeeId         = $user->mEmployeeId"); logFrodo(" mRealName           = $user->mRealName"); logFrodo(" mMarketGroup        = $user->mMarketGroup"); logFrodo(" mEmailAuthenticated = $user->mEmailAuthenticated"); logFrodo(" mTouched            = $user->mTouched"); $user->addToDatabase; logFrodo("User added to database with ID $user->mId."); } else { $user->mId = $s->user_id; logFrodo("DB entry found for employee id $empid with user id $user->mId"); } 	// Load the existing or newly-created user from the database ... if ( !$user->loadFromDatabase ) { logFrodo("loadFromDatabase failed for user ID $user-mId"); } else { // Additional debugging ... $user->saveToCache ; # This loads the user's group membership! logFrodo("loadFromDatabase succeeded for user ID $user->mId"); logFrodo(" real name: $user->mRealName"); foreach( $user->mGroups as $group ) { logFrodo(" group: $group"); }	}	return $user; }

function curPageURL { $pageURL = 'http'; if (isset($_SERVER['HTTPS'])) {$pageURL .= "s";} $pageURL .= "://"; if ($_SERVER['SERVER_PORT'] != "80") { $pageURL .= $_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI']; } else { $pageURL .= $_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']; } return $pageURL; }

function curPageName { return substr($_SERVER['SCRIPT_NAME'],strrpos($_SERVER['SCRIPT_NAME'],'/')+1); }

function valtime($intime) {

// Check whether supplied time is within five minutes of now.

$time1  = implode(explode(":", $intime)); // Colonic irrigation!

// Play at being a Time Lord here for testing.

$inepoch = strtotime($time1); // Convert to epoch $myepoch = time;

$diff = $myepoch - $inepoch; return($diff); }

function logFrodo($message) {

// Log significant events during authentication if the log file exists.

$day = gmdate("Ymd"); $authlog = "/xxxxxxx/local/log/FrodoWiki.authlog.$day"; $now = gmdate("Y-m-d H:i:s "); if (file_exists($authlog)) { if ($file = fopen($authlog, "a")) { fputs($file, $now . $message . "\n"); fclose($file); return(true); } else { return(false); }	} else { return(true); } } ?>