Hello group,
I have installed Mediawiki 1.8.2 running on Windows 2003, served up by PHP 5.2 on IIS 5. I have attempted to integrate this with Active Directory, with lots of help from Ryan Lane, but have thus far been unsuccessful. It looks like such integration would require Openldap and probably more work that it is worth.
An easier approach, it seems to me, would be to have IIS do the authentication and pass the information to Mediawiki. This is possible with Windows Integrated authentication. The username is available in PHP under $_SERVER['REMOTE_USER'] or get_current_user(). The next trick is to pass this onto Mediawiki.
I tried the code below but it only generates an HTTP 500 error when parsing InitUser(). This might be because of versioning, because the example is for MediaWiki 1.5.5 and I have 1.8.2.
http://meta.wikimedia.org/wiki/User:Otheus/Auto_Login_via_REMOTE_USER
It also seems rather round-about to create an WebRequest and submit it to the form, which then does the login. Maybe I am missing something here, though, for I am rather rough with PHP.
So, in sum, how do I simply and easily modify Mediawiki to use IIS's authentication?
J Wolfgang Goerlich
I have installed Mediawiki 1.8.2 running on Windows 2003, served up by PHP 5.2 on IIS 5. I have attempted to integrate this with Active Directory, with lots of help from Ryan Lane, but have thus far been unsuccessful. It looks like such integration would require Openldap and probably more work that it is worth.
You don't need openldap, you just need to uncomment a few lines in your php.ini, and copy a few files into system32. I gave you a link to the step by step instructions. For SSL or TLS to work you'll need to install an SSL certificate in AD.
I'm using MediaWiki with LdapAuthentication 1.1b and authenticating against AD just fine (a number of other people are too).
An easier approach, it seems to me, would be to have IIS do the authentication and pass the information to Mediawiki. This is possible with Windows Integrated authentication. The username is available in PHP under $_SERVER['REMOTE_USER'] or get_current_user(). The next trick is to pass this onto Mediawiki.
I tried the code below but it only generates an HTTP 500 error when parsing InitUser(). This might be because of versioning, because the example is for MediaWiki 1.5.5 and I have 1.8.2.
http://meta.wikimedia.org/wiki/User:Otheus/Auto_Login_via_REMOTE_USER
You need to contact the developer of this plugin, or fix the plugin yourself. I would imagine it isn't too hard to get this working with the newest version of mediawiki.
It also seems rather round-about to create an WebRequest and submit it to the form, which then does the login. Maybe I am missing something here, though, for I am rather rough with PHP.
It *kind of* feels like a dirty hack, but that is how I see it working in pretty much every plugin that uses the AutoAuthenticate hook; this hook gets called before a large portion of the code is put into memory. Notice you only create a login form when you are creating a user, and that is because that class has the functions you need to create users. If the user already exists, you just log the user in.
So, in sum, how do I simply and easily modify Mediawiki to use IIS's authentication?
If you are feeling adventurous, you can fix the plugin yourself. The first thing I'd do would be to change:
global $wgExtensionFunctions; if (!isset($wgExtensionFunctions)) { $wgExtensionFunctions = array(); } else if (!is_array($wgExtensionFunctions)) { $wgExtensionFunctions = array( $wgExtensionFunctions ); } array_push($wgExtensionFunctions, 'Auth_remote_user_hook');
To:
$wgHooks['AutoAuthenticate'][] = 'Auth_remote_user_hook';
Otherwise, quickly glancing over the plugin, I don't see any other major problems.
V/r,
Ryan Lane
Hello Ryan,
If you are feeling adventurous, you can fix the plugin yourself.
I'm always feeling adventurous! I'm primarily a .Net, SQL 2005 man. Hence, working with this PHP and MySQL setup is a bit of a learning curve. It is good fun, though, and I appreciate the guidance.
I am narrowing in on the problem. InitUser() results in an "HTTP 500 - Internal server error", whether it is called from the custom plugin or the generic create user.
To reframe the issue, the "Create account" form fails with an HTTP 500. This is with a default install of MediaWiki.
Now, I have PHP configured to throw exceptions into the Windows Application log. None are present when this error occurs. This says to me that it is an logical rather than a syntactical error. My thinking is that it has something to do with the MySQL script not executing properly.
Any suggestions on where to look or how to gather more detailed debugging information?
J Wolfgang Goerlich
Hello Ryan et al,
I have Mediawiki's normal account creation and login functions working. I am back on the trail of integrating Windows IIS authentication.
I have the the plugin installed and running without an error. I made the suggested modifications (below). Now, Mediawiki pages default to the "Login Required" prompt. From what I can tell, Auth_remote_user_hook() is never called.
Any suggestions on the next step?
Thanks,
J Wolfgang Goerlich
On 12/14/06, Lane, Ryan Ryan.Lane@ocean.navo.navy.mil wrote:
If you are feeling adventurous, you can fix the plugin yourself. The first thing I'd do would be to change:
global $wgExtensionFunctions; if (!isset($wgExtensionFunctions)) { $wgExtensionFunctions = array(); } else if (!is_array($wgExtensionFunctions)) { $wgExtensionFunctions = array( $wgExtensionFunctions ); } array_push($wgExtensionFunctions, 'Auth_remote_user_hook');
To:
$wgHooks['AutoAuthenticate'][] = 'Auth_remote_user_hook';
I have the the plugin installed and running without an error. I made the suggested modifications (below). Now, Mediawiki pages default to the "Login Required" prompt. From what I can tell, Auth_remote_user_hook() is never called.
Any suggestions on the next step?
Oops, I probably should have told you to make sure you declare the global variable before you use it. It should be:
global $wgHooks; $wgHooks['AutoAuthenticate'][] = 'Auth_remote_user_hook';
Oops, I probably should have told you to make sure you declare the global variable before you use it.
Oh! Yes, of course, the value needs to be global in order for the other modules to access it. Made the change and verified the 'Auth_remote_user_hook' is called. That is one step forward.
Now, the web page does not load. It gets into a continuous redirection loop. If I remove the redirection code (see below), then it comes back to the "Login Required" prompt.
The fun continues.
J Wolfgang Goerlich
// if it worked: refer to login page, otherwise, exit header( "Location: http" . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? "s" : "") . "://" . $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . ( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/" . ( isset($_SERVER['URL']) ? $_SERVER['PATH_INFO'] . ( $_SERVER['QUERY_STRING'] ? "?" . $_SERVER['QUERY_STRING'] : "" ) : "" ) ) );
Hmmm. More information. The problem is in registering the users, I think. Running "select * from user;" in MySQL returns only the users that I have manually created.
On 12/21/06, J Wolfgang Goerlich jwgoerlich@gmail.com wrote:
Oops, I probably should have told you to make sure you declare the global variable before you use it.
Oh! Yes, of course, the value needs to be global in order for the other modules to access it. Made the change and verified the 'Auth_remote_user_hook' is called. That is one step forward.
Now, the web page does not load. It gets into a continuous redirection loop. If I remove the redirection code (see below), then it comes back to the "Login Required" prompt.
The fun continues.
J Wolfgang Goerlich
// if it worked: refer to login page, otherwise, exit header( "Location: http" . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? "s" : "") . "://" . $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . ( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/" . ( isset($_SERVER['URL']) ? $_SERVER['PATH_INFO'] . ( $_SERVER['QUERY_STRING'] ? "?" . $_SERVER['QUERY_STRING'] : "" ) : "" ) ) );
The password cannot be blank. So, we define a universal password for all accounts. The code below will automatically login for accounts that I manually create. It is not yet automatically creating accounts.
J Wolfgang Goerlich
function Auth_remote_user_hook() {
global $wgUser; global $wgRequest; global $_REQUEST;
// Universal Password for all users $pass = "1Some2Secret3Password4"; // 1Some2Secret3Password4
// HTTP refer to login page $httprefer = "Location: http" . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? "s" : "") . "://" . $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . ( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/" . ( isset($_SERVER['URL']) ? $_SERVER['PATH_INFO'] . ( $_SERVER['QUERY_STRING'] ? "?" . $_SERVER['QUERY_STRING'] : "" ) : "" ) );
// For a few special pages, don't do anything. $title = $wgRequest->getVal('title') ; if ($title == 'Special:Userlogout' || $title == 'Special:Userlogin') { return; }
// Do nothing if session is valid $wgUser = User::loadFromSession(); if ($wgUser->isLoggedIn()) { return; }
// Do little if user already exists // (set the _REQUEST variable so that Login knows we're authenticated) $username = get_current_user(); $u = User::newFromName( $username ); if (is_null($u)) { # Invalid username or some other error -- force login, just return return; }
$wgUser = $u; if ($u->getId() != 0) {
// Populate the userlogin form's username and password (Userlogin.php)
$_REQUEST['wpName'] = $username; $_REQUEST['wpPassword'] = $pass; header($httprefer);
// Make call to load session name, otherwise can't save
if( !isset($wgCommandLineMode) && !isset( $_COOKIE[session_name()] ) ) { User::SetupSession(); }
// Set the cookies, save the settings, and return
$wgUser->setCookies(); $wgUser->saveSettings(); return; }
// Ok, now we need to create a user.
$wgUser->setPassword=$pass;
include 'includes/SpecialUserlogin.php'; $form = new LoginForm( $wgRequest ); $form->initUser( $wgUser );
$form->mName = $username; $form->mPassword = $pass; $form->mRetype = $pass; $form->mCreateaccount = true; $form->mRemember = true; $form->mRealName = $username;
header($httprefer);
$wgUser->setCookies(); $wgUser->saveSettings();
return;
}
The password cannot be blank. So, we define a universal password for all accounts. The code below will automatically login for accounts that I manually create. It is not yet automatically creating accounts.
You should use a randomly created password, or blank the password out later in the authentication plugin's initUser() function. It is possible for people to manually log in using SpecialUserlogin and the authenticate function. This would allow people to log in as anyone. To get an idea of how to do this, look at the SSLAuthentication plugin, the Shibboleth plugin, or my plugin. The former two create a random password, the latter blanks out the password.
function Auth_remote_user_hook() {
global $wgUser; global $wgRequest; global $_REQUEST;
// Universal Password for all users $pass = "1Some2Secret3Password4"; // 1Some2Secret3Password4
// HTTP refer to login page $httprefer = "Location: http" . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? "s" : "") . "://" . $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . ( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/" . ( isset($_SERVER['URL']) ? $_SERVER['PATH_INFO'] . ( $_SERVER['QUERY_STRING'] ? "?" . $_SERVER['QUERY_STRING'] : "" ) : "" ) );
I don't really see the need for the httprefer variable, more on that below.
// For a few special pages, don't do anything. $title = $wgRequest->getVal('title') ; if ($title == 'Special:Userlogout' || $title == 'Special:Userlogin') { return; }
// Do nothing if session is valid $wgUser = User::loadFromSession(); if ($wgUser->isLoggedIn()) { return; }
// Do little if user already exists // (set the _REQUEST variable so that Login knows we're authenticated) $username = get_current_user(); $u = User::newFromName( $username ); if (is_null($u)) { # Invalid username or some other error -- force login, just return return; }
$wgUser = $u; if ($u->getId() != 0) {
// Populate the userlogin form's username and password
(Userlogin.php)
$_REQUEST['wpName'] = $username; $_REQUEST['wpPassword'] = $pass; header($httprefer);
I don't get this part... Why are you changing request variables and sending out headers? I don't think you should be doing this.
// Make call to load session name, otherwise can't save if( !isset($wgCommandLineMode) && !isset(
$_COOKIE[session_name()] ) ) { User::SetupSession(); }
// Set the cookies, save the settings, and return
$wgUser->setCookies(); $wgUser->saveSettings(); return; }
// Ok, now we need to create a user.
$wgUser->setPassword=$pass;
include 'includes/SpecialUserlogin.php'; $form = new LoginForm( $wgRequest ); $form->initUser( $wgUser );
$form->mName = $username; $form->mPassword = $pass; $form->mRetype = $pass; $form->mCreateaccount = true; $form->mRemember = true; $form->mRealName = $username;
Why are you setting this stuff after you create the user (form->initUser)? And why aren't you doing it through the authentication plugin's initUser() function? Normally all of this stuff is set before the user is created.
header($httprefer);
Again. It is a little strange to be sending out headers here.
$wgUser->setCookies(); $wgUser->saveSettings();
return;
}
You aren't setting up a session for the new user... Call $wgUser->setupSession(); before you call setCookies().
V/r,
Ryan Lane
Ryan Lane wrote:
Why are you setting this stuff after you create the user (form->initUser)? And why aren't you doing it through the authentication plugin's initUser() function? Normally all of this stuff is set before the user is created.
I tried filling the request variables and calling the form-post because the initUser() did not appear to set the values. They still do not. Perhaps I am missing some necessary code in that block? (See below.)
I agree with you. I should change from a static universal password to a pseudo-random or blank password. First, though, I should get the auto-create function to work.
Regards,
J Wolfgang Goerlich
function initUser(&$user) { // Current user, email domain, and universal password
global $_SERVER; $cu = get_current_user(); $domain = "munder.com"; $pass = "1Some2Secret3Password4";
// Populate information for this user
$user->setEmail($cu . "@" . $domain); $user->setRealName( $cu ); $wgUser->setPassword=$pass;
$user->mEmailAuthenticated = wfTimestampNow(); $user->setToken();
// Disable e-mail notifications by default $user->setOption('enotifwatchlistpages', 0); $user->setOption('enotifusertalkpages', 0); $user->setOption('enotifminoredits', 0); $user->setOption('enotifrevealaddr', 0); }
I tried filling the request variables and calling the form-post because the initUser() did not appear to set the values. They still do not. Perhaps I am missing some necessary code in that block? (See below.)
Have you bothered to look at how I do this in my plugin? My code is working with mediawiki 1.6+, and the part you are having problems with should be almost 100% what you need line for line. (you can ignore the $wgAuth->printDebug statements).
$wgAuth->printDebug("User does not exist in local database; creating.",1);
//Require SpecialUserlogin so that we can get a loginForm require_once('SpecialUserlogin.php');
//This section contains a silly hack for MW global $wgLang; global $wgContLang; global $wgRequest; if(!isset($wgLang)) { $wgLang = $wgContLang; $wgLangUnset = true; }
$wgAuth->printDebug("Creating LoginForm.",1);
//This creates our form that'll let us create a new user in the database $lf = new LoginForm($wgRequest);
//The user we'll be creating... $wgUser = &$tmpuser; $wgUser->setName($wgContLang->ucfirst($mungedUsername));
$wgAuth->printDebug("Creating User.",1);
//Create the user $lf->initUser($wgUser);
//Update the user's settings $wgAuth->updateUser($wgUser);
//Initialize the user $wgUser->setupSession(); $wgUser->setCookies();
Funny enough I just noticed I call my plugin's updateUser() function, and not initUser() (and now looking at SpecialUserlogin.php and initUser(), I don't actually even need to call that...).
In MediaWiki 1.8 and below it looks like $lf->initUser() didn't automatically call $u->saveSettings(), so you'll need to make sure to call it yourself. Since $lf->initUser() calls $wgAuth->initUser(), you should make all changes required in that function, and manually call saveSettings() on the user object. This is pretty confusing behavior between the versions of MediaWiki, especially since $lf->initUser() didn't previously save settings and it does now, and the LoginForm used to save settings after $wgAuth->updateUser() was called and now it isn't.
Best bet is to update user info in updateUser() and initUser() in your plugin, and call saveSettings() in both. Who knows what wacky changes will be made in the future.
V/r,
Ryan Lane
mediawiki-l@lists.wikimedia.org