Quick Recipe for MediaWiki with AD logins

Here’s a quick recipe for getting a server up with MediaWiki running and having Active Directory Logins working with it… I’ll warn you, there are other ways of doing this. I find this to satisfy my needs, but if you manage to cause the world to implode, I won’t be held responsible.

Step 1: Set up your server

I created an Ubuntu 10.10 server VM for this, use whatever if you have some sort of issue against doing the same as me.

Step 2: Get Centrify Going

My friend told me about “Centrify” the other day. It’s a package you can install to help make Active Directory Logins simple. To install it, you have to first uncomment the partner repos in /etc/apt/sources.list – then install it via “apt-get install centrifydc“.

To activate Centrify, run “adjoin -w AD-NAME“, if you need more options, use “adjoin –help” to learn more.

Reboot your machine and confirm you can login with your credentials in Active Directory.

Step 3: Install & Configure MediaWiki

Go through the regular fun of setting up MediaWiki.. So install Apache2, php5 and whatever database you like.

Once you’ve got the base install of MediaWiki going, add the following lines to your LocalSettings.php

require_once("./extensions/PwAuthPlugin.php");
$wgAuth = new PwAuthPlugin();

$wgGroupPermissions['*']['createaccount']   = false;
$wgGroupPermissions['*']['read']            = true;
$wgGroupPermissions['*']['edit']            = false;
$wgGroupPermissions['*']['createpage']      = false;
$wgGroupPermissions['*']['createtalk']      = false;

$wgShowIPinHeader = false; # For non-logged in users


Then, dump the following file to ./extentions/PwAuthPlugin.php

<?php

/**
 * Version 1.0
 *
 * Authentication Plugin for pwauth
 * Derived from AuthPlugin.php
 *
 * Much of the commenting comes straight from AuthPlugin.php
 *
 * Copyright 2006 Nicholas J. Humfrey
 * Released under the GNU General Public License
 *
 * pwauth is available from http://www.unixpapa.com/pwauth/
 *
 * LocalSettings configuration:
 *  require_once("./extensions/PwAuthPlugin.php");
 *  $wgAuth = new PwAuthPlugin();
 *
 *
 */

require_once('AuthPlugin.php');

$pwauth_email_domain = "";
$pwauth_bin_path = "/usr/local/libexec/pwauth";

error_reporting(E_ALL); // Debug

// First check if class has already been defined.
if (!class_exists('AuthPlugin')) {

        /**
         * Auth Plugin
         *
         */
        require_once './includes/AuthPlugin.php';

} // End: if (!class_exists('AuthPlugin')) {

class PwAuthPlugin extends AuthPlugin {

	/**
	 * 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 ) {
		$user = posix_getpwnam( strtolower($username) );
		return is_array($user);
	}

	/**
	 * 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 $pwauth_bin_path;

		$username = strtolower( $username );

		$handle = popen($pwauth_bin_path, 'w');
		if ($handle === FALSE) {
			error_log("Error opening pipe to pwauth");
			return false;
		}

		if (fwrite($handle, "$username\n$password\n") === FALSE) {
			error_log("Error writing to pwauth pipe");
			return false;
		}

		# Is the password valid?
		$result = pclose( $handle );
		if ($result==0) return TRUE;

		#0  -  Login OK.
		#1  -  Nonexistant login or (for some configurations) incorrect password.
		#2  -  Incorrect password (for some configurations).
		#3  -  Uid number is below MIN_UNIX_UID value configured in config.h.
		#4  -  Login ID has expired.
		#5  -  Login's password has expired.
		#6  -  Logins to system have been turned off (usually by /etc/nologin file).
		#7  -  Limit on number of bad logins exceeded.
		#50 -  pwauth was not run with real uid SERVER_UID.  If you get this
		#      this error code, you probably have SERVER_UID set incorrectly
		#      in pwauth's config.h file.
		#51 -  pwauth was not given a login & password to check.  The means
		#      the passing of data from mod_auth_external to pwauth is messed
		#      up.  Most likely one is trying to pass data via environment
		#      variables, while the other is trying to pass data via a pipe.
		#52 -  one of several possible internal errors occured.
		error_log("pwauth returned $result for username $username");

		return false;
	}

	/**
	 * Modify options in the login template.
	 *
	 * @param UserLoginTemplate $template
	 * @access public
	 */
	function modifyUITemplate( &$template ) {
		$template->set('usedomain', false );
		$template->set('useemail', false);	// Disable the mail new password box.
		$template->set('create', false);	// Remove option to create new accounts from the wiki.
	}

	/**
	 * Check to see if the specific domain is a valid domain.
	 *
	 * @param string $domain
	 * @return bool
	 * @access public
	 */
	function validDomain( $domain ) {
		# We ignore domains, so erm, yes?
		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 $pwauth_email_domain;

		// Lookup information about user
		$username = strtolower( $user->getName() );
		$account = posix_getpwnam( $username );
		$gecos = split( ',', $account['gecos'] );

		// Set users real name
		$user->setRealName( $gecos[0] );

		// Set email if domain is configured
		if (!empty( $pwauth_email_domain ) )  {
			// Set the email address
			$user->setEmail( $username.'@'.$pwauth_email_domain );

			// We set the email address, therefore it is valid
			$user->confirmEmail();
		}

		// For security, scramble the password to ensure the user can
		// only login using system password.
		// This set the password to a 15 byte random string.
		$pass = '';
		for($i=0; $i<15;++$i) $pass .= chr(mt_rand(0,255));
		//$user->setPassword($pass); //Removed by James for those using Centrify, will cause ugliness

		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() {
		# We can't change users system passwords
		return false;
	}

	/**
	 * Set the given password in the authentication database.
	 * Return true if successful.
	 *
	 * @param string $password
	 * @return bool
	 * @access public
	 */
	function setPassword( $password ) {
		# We can't change users system passwords
		return false;
	}

	/**
	 * Update user information in the external authentication database.
	 * Return true if successful.
	 *
	 * @param User $user
	 * @return bool
	 * @access public
	 */
	function updateExternalDB( $user ) {
		# We can't change users details
		return false;
	}

	/**
	 * Check to see if external accounts can be created.
	 * Return true if external accounts can be created.
	 * @return bool
	 * @access public
	 */
	function canCreateAccounts() {
		# We can't create accounts
		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 ) {
		# We can't create accounts
		return false;
	}

	/**
	 * 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() {
		# Only allow authentication from system database
		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) {
		# We do everything in updateUser
	}

}

/**
 * Some extension information init
 */
$wgExtensionCredits['other'][] = array(
   'name' => 'PWAuthPlugin',
   'version' => '1.0',
   'author' => 'Nicholas Humfrey',
   'description' => 'Automagic login with system accounts, using pwauth',
   'url' => 'http://www.mediawiki.org/wiki/Extension:PwAuthPlugin'
);

?>


Basically all of that plugin is from here. But I found I had to disable saving of passwords in the function “updateUser()” in order for this to work with Centrify in my environment.

Step 4: Send me Beer

Now that you’re happily up and running with AD auths in MediaWiki send me a beer via Paypal: