Extension:AutomaticCAS USER

From MediaWiki.org
Jump to: navigation, search

Other languages:
English • ‎español • ‎日本語 • ‎polski
MediaWiki extensions manual
Crystal Clear action run.png
Automatic CAS Authentication

Release status: beta

Implementation User identity
Description Automatically logs users using CAS Server. Also, a Single sign out is configurable.
Author(s) Ramon Perez, iEcolab
Latest version 1.0
MediaWiki 1.9+
License GNU General Public License 2.0 or later
Download see below

Translate the AutomaticCAS USER extension if it is available at translatewiki.net

Check usage and version matrix; code metrics

Automatic CAS Authentication extension automatically logs users using the CAS Server. It is possible to do a single sign out configuration.

Save the file as /extensions/Auth_cas/Auth_cas.php

Download phpCAS library and save in /extensions/Auth_cas. So, you can download a package with all files in iEcolab

Then place the following two lines in your LocalSettings.php file:

require_once('extensions/Auth_cas/Auth_cas.php');
$wgAuth = new Auth_cas();

Note: Using this extension sets $wgMinimalPasswordLength to zero

Code[edit | edit source]

<?php
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Copyright 2010 Ramon Perez
//
// In 2009, the copyright holders determined that the original publishing of this code
// under GPLv3 was legally and logistically in error, and re-licensed it under GPLv2.
//
// See http://www.mediawiki.org/wiki/Extension:AutomaticCAS_USER
//
// Adapted by Rusty (REMOTE_USER) to do a CAS Authentication for USER. 
//
// Add these lines to your LocalSettings.php
// /* This is required for Auth_cas operation
// require_once('extensions/Auth_cas.php');
// $wgAuth = new Auth_cas();
//
// The constructor of Auth_cas registers a hook to do the automatic
// login.  Storing the Auth_cas object in $wgAuth tells mediawiki to use
// that object as the AuthPlugin.  This way the login attempts by the hook will
// be handled by us.
//

//Extension credits that show up on Special:Version
$wgExtensionCredits['other'][] = array(
	'name' => 'Authenticate with CAS Server',
	'version' => '1.0',
	'author' => array('Ramón Pérez'),
	'url' => 'http://www.mediawiki.org/wiki/Extension:AutomaticCAS_USER',
'description' => 'Authenticate for user with a CAS Server.',
	);


// Setup hooks
global $wgHooks;
$wgHooks["UserLogoutComplete"][] = "casLogout";

//--------------------------------------------------------------------------
// Configuration Variables
//--------------------------------------------------------------------------

$CASAuth = array(
	"phpCAS"         => "$IP/extensions/CASAuth", // Path to phpCAS directory.
"Server"         => "login.example.es",        // Address to CAS server.
"Port"           => 443,                          // Port to CAS server. Default: 443.
"Url"            => "",              // Subdir to CAS authentication.
"Version"        => "2.0",                        // CAS version, should be either 1.0 or 2.0.
"PwdSecret"      => "a letters", // A random string that is used when generating the MediaWiki password for this user. YOU SHOULD EDIT THIS TO A VRY RANDOM STRING! YOU SHOULD ALSO KEEP THIS A SECRET!
"logoutCAS"		=> array("server1", "server2"),
	);

//this is necessary to catch the logout request from CAS Server

include_once($CASAuth["phpCAS"]."/CAS.php");

phpCAS::setDebug();
phpCAS::client($CASAuth["Version"], $CASAuth["Server"], $CASAuth["Port"], $CASAuth["Url"]);
phpCAS::setNoCasServerValidation();

if ($_REQUEST['service']) :
phpCAS::setFixedServiceURL(urlencode($_REQUEST['service']));
endif;

phpCAS::handleLogoutRequests(true, $CASAuth["logoutCAS"]);

//Check if the user is authenticate
phpCAS::CheckAuthentication();

// The Auth_cas class is an AuthPlugin so make sure we have this
// included.
require_once('AuthPlugin.php');

/**
* This hook is registered by the Auth_cas constructor.  It will be
* called on every page load.  It serves the function of automatically logging
* in the user.  The Auth_cas class is an AuthPlugin and handles the
* actual authentication, user creation, etc.
*
*/
function Auth_cas_user_hook() {
	global $wgUser;
	global $wgRequest;
	global $_REQUEST;
	global $wgAuthRemoteuserDomain;

	// For a few special pages, don't do anything.
	$title = $wgRequest->getVal('title');	
	if ($title == Title::makeName(NS_SPECIAL, 'Userlogout')) {
		wfDebugLog('CASAuth', 'Logout request');		
		return;
	}

	//If you want more method to login a user, comment this if.
	//When the requested page is UserLogin, we force to register in CAS Server.
	if 	($title == Title::makeName(NS_SPECIAL, 'Userlogin')){
		wfDebugLog('CASAuth', 'Login request');

		phpCAS::forceAuthentication(); //Will redirect to CAS server if not logged in

		wfDebugLog('CASAuth', 'User authenticate in CAS Server');

		$returnto = "Portada";  //Name of principal page
		$target = Title::newFromText($returnto);
		wfDebugLog('CASAuth', 'redirect to main page');		
		global $wgOut;
		$wgOut->redirect($target->getFullUrl());				
	}


	// Do nothing if session is valid	
	$user = User::newFromSession();
	if ((!$user->isAnon()) && (phpCAS::isSessionAuthenticated())) {
		wfDebugLog('CASAuth', 'User is not anonymous');		
		return;  // User is already logged in and not anonymous.
	}else{
		wfDebugLog('CASAuth', 'User is anonymous or he is not authenticated in CAS Server');		
		//if it is not anonymous, we have to logout the user, because logout in another application. Single sign out.
		if (!$user->isAnon()){
			wfDebugLog('CASAuth', 'Logout user, because');					
			global $wgUser;
			$wgUser->doLogout();			
			return;		
		}			

	}

	// Copied from includes/SpecialUserlogin.php
	if(!isset($wgCommandLineMode) && !isset($_COOKIE[session_name()])) {
		wfSetupSession();
	}

	wfDebugLog('CASAuth', 'Check if there is a user authenticate in CAS Server');

	if (phpCAS::isSessionAuthenticated()){
		//gets the data about user in CAS Server		
		$email = phpCAS::getUser();

		//Gets the extra attributes send by CAS Server
		$extra_attributes = phpCAS::getAttributes();

			// Submit a fake login form to authenticate the user.
			$params = new FauxRequest(array(
				'wpName' => $extra_attributes["login"],
				'wpPassword' => '',
				'wpDomain' => '',
				'wpRemember' => ''
				));

			$loginForm = new LoginForm($params);
			$result = $loginForm->authenticateUserData();
			switch ($result) {
				case LoginForm :: SUCCESS :
				$wgUser->setOption('rememberpassword', 1);
				$wgUser->setCookies();
				break;
				case LoginForm :: NO_NAME :
				$errormessage = 'NoName';
				break;
				case LoginForm :: ILLEGAL :
				$errormessage = 'Illegal';
				break;
				case LoginForm :: WRONG_PLUGIN_PASS :
				$errormessage = 'WrongPluginPass';
				break;
				case LoginForm :: NOT_EXISTS :
				$errormessage = 'NotExists';
				break;
				case LoginForm :: WRONG_PASS :
				$errormessage = 'WrongPass';
				break;
				case LoginForm :: EMPTY_PASS :
				$errormessage = 'EmptyPass';
				break;
				default:
				$errormessage = 'Unknown';
				break;

			}

			if ($result != LoginForm::SUCCESS) {
				error_log('Unexpected CAS user authentication failure. Login Error was: '.$errormessage);
			}

		return;

	}

}

class Auth_cas extends AuthPlugin {

	function Auth_cas() {
		// Register our hook function.  This hook will be executed on every page
		// load.  Its purpose is to automatically log the user in, if necessary.
		global $wgExtensionFunctions;
		if (!isset($wgExtensionFunctions)) {
			$wgExtensionFunctions = array();
		}
		else if (!is_array($wgExtensionFunctions)) {
			$wgExtensionFunctions = array( $wgExtensionFunctions );
		}
		array_push($wgExtensionFunctions, 'Auth_cas_user_hook');
		return;
	}

	/**
	* Disallow password change.
	*
	* @return bool
	*/
function allowPasswordChange() {
	return false;
}

/**
* This should not be called because we do not allow password change.  Always
* fail by returning false.
*
* @param $user User object.
* @param $password String: password.
* @return bool
* @public
*/
function setPassword($user, $password) {
	return false;
}

/**
* We don't support this but we have to return true for preferences to save.
*
* @param $user User object.
* @return bool
* @public
*/
function updateExternalDB($user) {
	return true;
}

/**
* We can't create external accounts so return false.
*
* @return bool
* @public
*/
function canCreateAccounts() {
	return false;
}

/**
* We don't support adding users to whatever service provides REMOTE_USER, so
* fail by always returning false.
*
* @param User $user
* @param string $password
* @return bool
* @public
*/
function addUser($user, $password) {
	return false;
}

/**
* Pretend all users exist.  This is checked by authenticateUserData to
* determine if a user exists in our 'db'.  By returning true we tell it that
* it can create a local wiki user automatically.
*
* @param $username String: username.
* @return bool
* @public
*/
function userExists($username) {
	return true;
}

/**
* Check whether the given name matches REMOTE_USER.
* The name will be normalized to MediaWiki's requirements, so
* lower it and the REMOTE_USER before checking.
*
* @param $username String: username.
* @param $password String: user password.
* @return bool
* @public
*/
function authenticate($username, $password) {

	if (!phpCAS::isSessionAuthenticated()){
		phpCAS::forceAuthentication(); //Will redirect to CAS server if not logged in
		return false;
	}	
	

	return true;
}

/**
* Check to see if the specific domain is a valid domain.
*
* @param $domain String: authentication domain.
* @return bool
* @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
* @public
*/
function updateUser(&$user) {
	// We only set this stuff when accounts are created.
	return true;
}

/**
* Return true because 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.
*
* @return bool
* @public
*/
function autoCreate() {
	return true;
}

/**
* Return true to prevent logins that don't authenticate here from being
* checked against the local database's password fields.
*
* @return bool
* @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.
*
* @param $user User object.
* @public
*/
function initUser(&$user) {

	//get parameters from cas server
	$email = phpCAS::getUser();
	$extra_attributes = phpCAS::getAttributes();

	$username = $extra_attributtes["login"];
	$user->setRealName($extra_attributes["name"]);
	$user->setEmail($email);
	$user->mEmailAuthenticated = wfTimestampNow();
	$user->setToken();

	//turn on e-mail notifications
	if (isset($wgAuthRemoteuserNotify) && $wgAuthRemoteuserNotify) {
		$user->setOption('enotifwatchlistpages', 1);
		$user->setOption('enotifusertalkpages', 1);
		$user->setOption('enotifminoredits', 1);
		$user->setOption('enotifrevealaddr', 1);
	}

	$user->saveSettings();
}

/**
* Modify options in the login template.  This shouldn't be very important
* because no one should really be bothering with the login page.
*
* @param $template UserLoginTemplate object.
* @public
*/
function modifyUITemplate(&$template) {
	//disable the mail new password box
	$template->set('useemail', false);
	//disable 'remember me' box
	$template->set('remember', false);
	$template->set('create', false);
	$template->set('domain', false);
	$template->set('usedomain', false);
}

/**
* Normalize user names to the MediaWiki standard to prevent duplicate
* accounts.
*
* @param $username String: username.
* @return string
* @public
*/
function getCanonicalName($username) {
	// lowercase the username
	$username = strtolower($username);
	// uppercase first letter to make MediaWiki happy
	$username = ucfirst($username);
	return $username;
}
}

// Function to logout in mediawiki and CAS Server
function casLogout() {
	global $CASAuth;
	global $wgUser, $wgRequest;

	// Logout from MediaWiki
	$wgUser->doLogout();

	// Get returnto value
	$returnto = $wgRequest->getVal("returnto");
	if ($returnto) {
		$target = Title::newFromText($returnto);
		if ($target) {
			$redirecturl = $target->getFullUrl();
		}
	}

	if (isset($redirecturl)) {
		phpCAS::logoutWithRedirectServiceAndUrl($redirecturl, $redirecturl);
	}
	else {
		phpCAS::logout();
	}

	return true; // We won't get here
}

?>