Single Signon Between Mediawiki And Rails
I was fully anticipating this to be a really nasty endaevor. As it turns out, it really wasnt all that bad. Not that I got the gist of how to do this from a page on the case.edu wiki, which described how to do it between two php apps, and made it happen on Rails. First step is to make a media wiki authentication module. Something like:
<?php
require_once('AuthPlugin.php');
class RailsAuthPlugin extends AuthPlugin {
//return whether $username is a valid username
function userExists($username) {
//since the username will be passed from our external source, this will probably
//always be true
//however, the security paranoid says to check the data
//you could do an LDAP verify here, just to be safe
return true; //or return false if the username is invalid
}
//whether the given username and password authenticate
function authenticate($username, $password) {
//the external authentication actually handles this part, but we still need a security
//check
//this form element will be set by our login script. this security check is important!
global $wgLoginFormKey;
return isset($_POST[$wgLoginFormKey]);
}
//The authorization is external, so autocreate accounts as necessary
function autoCreate() {
return true;
}
//tell MediaWiki to not look in its database for user authentication and that our
//authentication method is all that counts
function strict() {
return true;
}
//this function gets called when the user is created
//$user is an instance of the User class (see includes/User.php)
function initUser(&$user) {
//unless you want the person to be nameless, you should probably populate info about
//this user here
//we do some LDAP queries to populate their name and e-mail
$this_username = trim($user->getName());
$theData = ""; # this could be some callback to Rails to get the email address of the user.
$user->setEmail($theData);
$theData = ""; # this could be some callback to Rails to get the name of the user
$user->setRealName($theData);
//if using MediaWiki 1.5, we can set some e-mail options
$user->mEmailAuthenticated = wfTimestampNow();
//turn on e-mail notifications by default
$user->setOption('enotifwatchlistpages', 1);
$user->setOption('enotifusertalkpages', 1);
$user->setOption('enotifminoredits', 1);
$user->setOption('enotifrevealaddr', 1);
}
//if using MediaWiki 1.5, we have a new function to modify the UI template!
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", true);
}
}
?>
Then add these lines to LocalSettings.php:
$wgCookieDomain = '.mydomain.com';
$wgLoginFormKey = "insert_secret_key_here";
require_once("extensions/RailsAuthPlugin.php"); # or whatever you named your extention
$wgAuth = new RailsAuthPlugin(); # or whatever you named the class
Then on the rails app, you want it to essentially authenticate on the backend whenever a user authenticates to the rails application. I have an after_filter on login that handles this, and here’s what it looks like:
def check_wiki if logged_in? begin require 'net/http' require 'uri' require 'cgi' cookie_domain = '.my_domain.com' # # Note that wpPassword can be anything, and the value of the my_secret_key # is irrelevant as well that parameter name needs to equal the value # of $wgLoginFormKey from LocalSettings.php data = "wpName=#{CGI::escape(current_user.login)}" data = data + "&\wpPassword=lygernoob" data = data + "&wpLoginattempt=Log%20in" data = data + "&my_secret_key=true" headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } http = Net::HTTP.new('wiki.mydomain.com', 80) path = "/index.php?title=Special:UserLogin&returnto=Main_Page" resp, data = http.post(path,data,headers) returned_cookies = resp['set-cookie'].split(',') returned_cookies.each do |b| b.strip! if b =~ /^([A-Za-z0-9_]+)\=([A-Za-z0-9_]+)/ cookie_name, cookie_value = [$1, $2] cookies[cookie_name] = {:value => cookie_value, :expires => 30.days.from_now, :domain => cookie_domain, :path => '/'} end end rescue end end endYou’ll also want to add some stuff to the rails logout routine:
cookies.delete 'wikiToken', {:domain => '.mydomain.com'} cookies.delete 'wiki_session', {:domain => '.mydomain.com'} cookies.delete 'wikiUserID', {:domain => '.mydomain.com'} cookies.delete 'wikiUserName', {:domain => '.mydomain.com'}And you’ll probably want to redirect the login and logout pages on your mediawiki to your Rails instance. But that’s really about all it takes. Single-signon between your Rails user database and Mediawiki. Fun fun fun.