Extension:Wget Authentication

From MediaWiki.org
Jump to navigation Jump to search
MediaWiki extensions manual
OOjs UI icon advanced.svg

Release status: stable
Implementation User identity
Description Allows you to securely use wget to authenticate against a pre-existing HTTP authentication login system.
Author(s) (darkbeethoventalk)
Latest version 1.0
MediaWiki 1.13.1
License GNU General Public License 2.0 or later
Download http://hinespot.net/content/WgetAuthentication.txt
Translate the Wget Authentication extension if it is available at translatewiki.net
Check usage and version matrix.

What can this extension do?[edit]

This allows you to securely exec() a wget url within mediawiki to authenticate your users (by way of HTTP authentication, as you've probably guessed). Once the username and password are received, this extension creates a pipe between php and a fork'd wget and proceeds to retreive a URL to the effect of: https://username:password@yourwebserver.com. Once wget returns, the extension will check wget's exit code and return success only if the code is zero.

Installation[edit]

First edit the file extensions/WgetAuthentication.php and paste the code that you see below.

To install this extension, add the following to LocalSettings.php :

require_once("$IP/extensions/WgetAuthentication.php");

/* This line is critical. Make sure you include it */
$wgAuth = new WgetAuthentication();

Configuration parameters[edit]

Now, add the following configuration parameters to your LocalSettings.php file....

/* Provide a list of possible domains that you would like the plugin to allow
 * the user to select from. They're just chosen names to be displayed on the
 * login form and used to index into the next two arrays.
 */
$wgetDomains = array("Domain One", "Domain Two");

/* For each domain, provide the actual URL to which the user's username and
 * password should be sent. Note: this is not just a domain name, it's the full
 * URL of the HTTP-authenticated location in which you would normally get a
 * pop-up login box if you were using a web browser.
 */

$wgetDomainURLS = array(
                "Domain One" => "yourwebserverone.com/login/at/this/url",
                "Domain Two" => "yourwebservertwo.com/login/at/another/url"
                );

/* Indicate whether or not you want 'https' login or 'http' logins for each
 * domain 
 */
$wgetSecurity = array(
                "Domain One" => "https",
                "Domain Two" => "http"
                );

/* Optional: you may specify the domain names of the above URLS to use for
 * filling in the email address of each user if a user is logging in for the
 * very first time. The plugin will set their email address as already confirmed
 * automatically when the account is created.
 */
$wgetEmailDomains = array(
                "Domain One" => "yourwebdomainone.com",
                "Domain Two" => "yourwebdomaintwo.com"
                );

/* Optional: this plugin doesn't support local authentication right now, so
 * you might find the following configuration setting useful:
 */

$wgGroupPermissions['*'    ]['createaccount']    = false;

Code[edit]

<?php
# Copyright (C) 2009 Michael R. Hines
# <http://www.mediawiki.org/wiki/User:Darkbeethoven>
#
# 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html

/**
 *  Wget Authentication plugin.
 *  Does not support local accounts right now. Does not support NEW usernames,
 *  BUT will auto-create an account if the user has been
 *  authenticated when logging in for the first time.
 */

$wgExtensionCredits['other'][] = array(
	'name' => 'Wget Authentication Plugin',
	'version' => '1.0',
	'author' => 'Michael R. Hines',
	'description' => 'Allows you to securely use wget to authenticate against a pre-existing HTTP authentication login system.',
	'url' => 'http://hinespot.net/content/WgetAuthenticationtxt',
	);

class WgetAuthentication extends AuthPlugin {
	function WgetAuthentication() {
	}
	/**
	 * Check whether there exists a user account with the given name.
	 *
	 * @param $username String: username.
	 * @return bool
	 * @public
	 */
	function userExists( $username ) {
		/* Just assume the user exists and try to authenticate.
		 * We have no way to test otherwise.
		 */
		return true;
	}

	/**
	 * Check if a username+password pair is a valid login.
	 *
	 * @param $username String: username.
	 * @param $password String: user password.
	 * @return bool
	 * @public
	 */
	function authenticate( $username, $password ) {
	    global $_SERVER, $wgOut;
	    global $wgetDomains, $wgetDomainURLS, $wgetSecurity;

		/* Disallow empty passwords */

		if ( '' == $password ) {
			$wgOut->addHTML("<h2>Empty passwords not allowed.</h2>");
			return false;
		}


		/* convert all usernames to lowercase first */
		$username = strtolower( $username );

		$rc = "";
		$auth = "";
		$descriptorspec = array(
			0 => array("pipe", "r"),  // stdin
			1 => array("pipe", "w"));  // stdout

		/* 
		 * Setup a two-way pipe to talk between php and the fork/exec'd
		 * wget process later. Wget will take the authentication URL
		 * from standard input and the shell will spit out the exit code
		 * on standard output.
		 */
		$process = proc_open("wget -i - -o /dev/null -O /dev/null; echo $?", $descriptorspec, $pipes);

		/* Make sure the fork/exec succeeded and the pipes are setup */
		if (!is_resource($process)) {
			$wgOut->addHTML("<h2>Failed to internally setup the authentication process please contact your system administrator.</h2>");
			return false;
		}

		/* Grab the selected domain name */
		$dom = $_SESSION['wsDomain'];

		/* Make sure the domain is valid (could be 'invaliddomain' */
		if(!$this->validDomain($dom)) {
			$wgOut->addHTML("<h2>Invalid Domain name: $dom");
			return false;
		}

		/* Setup the authentication URL based on LocalSetting parameters
		 */
		$auth = "";
		$auth .= $wgetSecurity[$dom]."://";
		$auth .= urlencode($username).":".urlencode($password)."@";
		$auth .= $wgetDomainURLS[$dom];

		/* wget is blocking. feed the beast. */
		$err = fwrite($pipes[0], $auth);

		if(!err)
			return false;

		fclose($pipes[0]);

		/* wget is still blocking with the exit code. he needs to use the bathroom. */
		while($s = fgets($pipes[1], 1024))
			$rc .= $s;

		fclose($pipes[1]);

		/* Check exit code. Success if zero */
		if($rc != 0)
			return false;

		return true;
	}

	/**
	 * Modify options in the login template.
	 *
	 * @param $template UserLoginTemplate object.
	 * @public
	 */
	function modifyUITemplate( &$template ) {
		global $wgetDomainURLS, $wgetSecurity, $wgetDomains, $wgAuth, $wgOut;

		/* Did you setup LocalSettings.php properly?? */

		if(!isset($wgetDomainURLS) || !isset($wgetSecurity) || !isset($wgetDomains)) {
			$wgAuth = new AuthPlugin();
			$wgOut->addHTML("<h2>NOTE: WgetAuthentication is not enabled. You are missing configuration parameters in LocalSettings.php</h2>");

			return;
		}

		/* Support for multiple domains */
		$template->set( 'usedomain', true );
		$template->set( 'domainnames', $wgetDomains );

		/* Disable new user account creation */
		$template->set( 'create', false );

	        $template->set('useemail', false);
	        $template->set('remember', false);
	}

	/**
	 * Set the domain this plugin is supposed to use when authenticating.
	 *
	 * @param $domain String: authentication domain.
	 * @public
	 */
	function setDomain( $domain ) {
		$_SESSION['wsDomain'] = $domain;
	}

	/**
	 * Check to see if the specific domain is a valid domain.
	 *
	 * @param $domain String: authentication domain.
	 * @return bool
	 * @public
	 */
	function validDomain( $domain ) {
		global $wgetDomainURLS, $wgetSecurity, $wgetDomains, $wgAuth, $wgOut;

		/* Make sure the domain is valid */
		if(in_array($domain, $wgetDomains))
			return true;

		return false;
	}

	/**
	 * 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 ) {
		/* Nothing to do here, really. We've setup initUser, though
		 * later */
		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
	 * @public
	 */
	function autoCreate() {
		return true;
	}

	/**
	 * Can users change their passwords?
	 *
	 * @return bool
	 */
	function allowPasswordChange() {
		/* HTTP-authenticated users should change their passwords
		 * elsewhere.
		 */
		return false;
	}

	/**
	 * Set the given password in the authentication database.
	 * As a special case, the password may be set to null to request
	 * locking the password to an unusable value, with the expectation
	 * that it will be set later through a mail reset or other method.
	 *
	 * Return true if successful.
	 *
	 * @param $user User object.
	 * @param $password String: password.
	 * @return bool
	 * @public
	 */
	function setPassword( $user, $password ) {
		/* HTTP-authenticated users should change their passwords
		 * elsewhere.
		 */
		return false;
	}

	/**
	 * Update user information in the external authentication database.
	 * Return true if successful.
	 *
	 * @param $user User object.
	 * @return bool
	 * @public
	 */
	function updateExternalDB( $user ) {
		/* Just assume it worked. */
		return true;
	}

	/**
	 * Check to see if external accounts can be created.
	 * Return true if external accounts can be created.
	 * @return bool
	 * @public
	 */
	function canCreateAccounts() {
		return false;
	}

	/**
	 * Add a user to the external authentication database.
	 * Return true if successful.
	 *
	 * @param User $user - only the name should be assumed valid at this point
	 * @param string $password
	 * @param string $email
	 * @param string $realname
	 * @return bool
	 * @public
	 */
	function addUser( $user, $password, $email='', $realname='' ) {
		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
	 * @public
	 */
	function strict() {
		/* local authentication not supported right now */
		return true;
	}

	/**
	 * Check if a user should authenticate locally if the global authentication fails.
	 * If either this or strict() returns true, local authentication is not used.
	 *
	 * @param $username String: username.
	 * @return bool
	 * @public
	 */
	function strictUserAuth( $username ) {
		/* local authentication not supported right now */
		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 object.
	 * @param $autocreate bool True if user is being autocreated on login
	 * @public
	 */
	function initUser( &$user, $autocreate=false ) {
	    global $_SESSION, $wgetDomainURLS, $wgetSecurity, $wgetDomains,
		   $wgAuth, $wgOut, $wgetEmailDomains;

	    /* Fill in the email address on first login? */
	    if(!isset($wgetEmailDomains)) {
		return;	
	    }


	    $dom = $_SESSION['wsDomain'];

	    if(!$this->validDomain($dom)) {
		    return;
	    }

	    $user->setEmail(strtolower($user->getName()) 
			    . "@" .  $wgetEmailDomains[$dom]);

	    $user->mEmailAuthenticated = wfTimestampNow();
	    $user->setToken();
	    $user->saveSettings();
	}

	/**
	 * If you want to munge the case of an account name before the final
	 * check, now is your chance.
	 */
	function getCanonicalName( $username ) {
	    /* prevent multiple account names */
	    $username = strtolower( $username );
	    // uppercase first letter to make MediaWiki happy
	    $username = ucfirst($username);
		return $username;
	}
}

See also[edit]