Extension:Siteminder Authentication

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manualManual:Extensions
Crystal Clear action run.png
SiteMinder Authentication

Release status:Extension status stable

ImplementationTemplate:Extension#type User identity
DescriptionTemplate:Extension#description Allows integration of mediawiki with the SiteMinder facility from CA.
Author(s)Template:Extension#username Virgil Green
Latest versionTemplate:Extension#version 1.0 (28 Nov 2007)
MediaWikiTemplate:Extension#mediawiki 1.7 or higher
LicenseTemplate:Extension#license No license specified
Download Extension Page

Translate the Siteminder Authentication extension if it is available at translatewiki.net

Check usage and version matrix.

Overview[edit]

SiteMinder is a centralized Web access management system that enables user authentication and single sign-on, policy-based authorization, identity federation, and auditing of access to Web applications and portals.

This extension (which was derived from the work on the Shibboleth Authentication extension) is designed to aid in synchronizing Mediawiki internal authorization with the credentials supplied via SiteMinder.

Current Version: 1.0

Installation Instructions[edit]

Compatibility[edit]

This extension is designed for MediaWiki 1.7 and up. However, that compatibility is based solely upon the heritage of the extension, being derived from the Shibboleth Authentication extension. It has only been tested by the author on MediaWiki 1.11.

SiteMinder Configuration[edit]

The extension requires that a SiteMinder agent be configured and set up on the web server that the wiki is hosted on. Once SiteMinder is running on the web server make sure your configuration file is correctly set-up. As shown in the sample configuration for this extension, the SiteMinder agent should supply HTTP headers named

  • full_name
  • user_name
  • email

You may also choose to use other SiteMinder capabilities for communicating from Siteminder to the extension, such as cookies, but you'll need to modify the LocalSettings.php configuration accordingly.

Download the Extension[edit]

The code for the extension is at the bottom of this page. Place it in the extensions folder, in a file called "SiteminderAuthPlugin.php."

Extension Configuration[edit]

Now it's time to configure and load the extension. To do that, just add the following lines to LocalSettings.php in the root of the mediawiki directory.

Most of the configuration directives are of the form:

$siteminder_SOMELETTERS = data_manipulation_functions($_SERVER['HEADER_FOR_SITEMINDER_DATA']);

This allows you to map whatever SiteMinder identifiers you choose to a variety of fields for each user as well as use the standard PHP functions to massage the data as it enters.

At the very minimum you'll need to make the following changes:

  1. Place the following code into the LocalSettings.php file.
  2. Map a valid piece of Siteminder data for the username.
  3. Look over the rest of the variables and ensure that you don't want to make any more changes.
##Siteminder Authentication
#Load SiteminderPlugin
require_once('extensions/SiteminderAuthPlugin.php');

#Map data from Siteminder to local user data
$siteminder_map_info = "true";

#Ssssh.... quiet down errors
$olderror = error_reporting(E_ALL ^ E_NOTICE);

#Map Siteminder variables to extension variables
$siteminder_real_name = $_SERVER['HTTP_FULL_NAME'];
$siteminder_user_name = ucfirst(strtolower($_SERVER['HTTP_USER_NAME']));
$siteminder_email = $_SERVER['HTTP_EMAIL'];

#Siteminder logoff URL (uncomment and set proper URL if you want a logout link)
#$siteminder_logout = "<URL to Siteminder logout page>";

#Turn error reporting back on
error_reporting($olderror);

#Activate Siteminder Plugin
SetupSiteminderAuth();

Using the Extension[edit]

The extension is designed to be fairly simple to use. Once everything is configured and SiteMinder is running the user should be challenged before seeing any of the wiki screens, unless they have already been authenticated elsewhere for some other SiteMinder-protected application.

Tips, Tricks and Known Bugs[edit]

Adding more Metadata[edit]

If you wish to add more mappings from Mediawiki data to SiteMinder data than is available, you can simply add them in the configuration file. The only code changes you will have to do are in the updateUser() function of the SiteminderAuthPlugin.php file.

Logout Functionality[edit]

By default logout is disabled, relying on the closing of the browser or possibly logging out via an enclosing portal's authentication scheme. Alternatively you may point logout to a url provided by your SiteMinder administrator which will allow logout.

Username Munging[edit]

While the extension will enforce the rule that all usernames must begin with a capital letter, it will not enforce any of the rest of mediawiki's usual rules for usernames. If your SiteMinder data will break other mediawiki username rules you should filter that out at the configuration level when $siteminder_user_name gets set. In addition, if your SiteMinder data includes sets of usernames that may only be separated by difference of upper and lower-case initial letters (Like "Nick@example.org" and "nick@example.org") then the plugin will treat these two as if they were not distinct. Again the solution is to provide additional filtering in LocalSettings.php. By default we simply use the SiteMinder user name converting any letters to lowercase and then pass those in. (Again, the extension takes care of converting the initial letter to uppercase.)

Extension Code[edit]

<?php
/**
 * Version 1.0 (Works out of box with MW 1.7 or above)
 *
 * Authentication Plugin for Siteminder
 * Derived from ShibAuthPlugin.php
 * Much of the commenting comes straight from AuthPlugin.php
 * Had to add return statements to several routines because a
 * return value is required from hook routines.
 *
 * Portions Copyright 2006, 2007 Regents of the University of California.
 * Portions Copyright 2007 Steven Langenaken
 * Released under the GNU General Public License
 *
 * Extension Maintainer:
 *         * Virgil Green <virgil.green AT bunge DOT com>
 * Extension Developers:
 *         * Virgil Green - Converted from Shibbeleth to Siteminder
 *
 * This extension assumes that the only method for signing on will
 * be SiteMinder. Local signon is not allowed. Since validation always
 * occurs before any part of the wiki is displayed, there are no
 * provisions made for selecting the style (local or Siteminder) and any
 * function that would be used for validation or synchronizatin is
 * set to simply return as though successful (or failure, if needed/appropriate).
 *
 * Suggested LocalSettings.php code
 *   ##Siteminder Authentication
 *   #Load SiteminderPlugin
 *   require_once('extensions/SiteminderAuthPlugin.php');
 *
 *   #Map data from Siteminder to local user data
 *   $siteminder_map_info = "true";
 *
 *   #Ssssh.... quiet down errors
 *   $olderror = error_reporting(E_ALL ^ E_NOTICE);
 *
 *   #Map Siteminder variables to extension variables
 *   # In this example, Siteminder is setting http headers named
 *   # full_name, user_name, and email when then appear in the $_SERVER array
 *   # in any of the other data collections that expose the headers to code.
 *   $siteminder_real_name = $_SERVER['HTTP_FULL_NAME'];
 *   $siteminder_user_name = ucfirst(strtolower($_SERVER['HTTP_USER_NAME']));
 *   $siteminder_email = $_SERVER['HTTP_EMAIL'];
 *
 *   #Siteminder logoff URL (uncomment and set proper URL if you want a logout link)
 *   #This is expected to be an abslute URL, including the protocol
 *   #$siteminder_logout = "<URL to Siteminder logout page>";
 *
 *   #Turn error reporting back on
 *   error_reporting($olderror);
 *
 *   #Activate Siteminder Plugin
 *   SetupSiteminderAuth();
 *
 */

require_once('AuthPlugin.php');

class SiteminderAuthPlugin 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 ) {
                return true;
        }
        /**
         * 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 $siteminder_user_name;

                return $username == $siteminder_user_name;
        }

        /**
         * Modify options in the login template.
         *
         * @param UserLoginTemplate $template
         * @access public
         */
        function modifyUITemplate( &$template ) {
                return;
        }

        /**
         * Set the domain this plugin is supposed to use when authenticating.
         *
         * @param string $domain
         * @access public
         */
        function setDomain( $domain ) {
                return;
        }

        /**
         * Check to see if the specific domain is a valid domain.
         *
         * @param string $domain
         * @return bool
         * @access 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
         * @access public
         */
        function updateUser( &$user ) {
                global $siteminder_map_info;
                global $siteminder_email;
                global $siteminder_real_name;

                if ($siteminder_map_info)
                {
                        if($siteminder_email != null)
                                $user->setEmail($siteminder_email);
                        if($siteminder_real_name != null)
                                $user->setRealName($siteminder_real_name);
                }

                //For security, set password to a non-existant hash.
                if($user->mPassword != "nologin")
                {
                        $user->mPassword = "nologin";
                        $user->saveSettings();
                }

                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() {
                return false;
        }

        /**
         * Set the given password in the authentication database.
         * Return true if successful.
         *
         * @param string $password
         * @return bool
         * @access public
         */
        function setPassword( $password ) {
                return true;
        }

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

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


        /**
         * 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() {
                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 ) {
                $this->updateUser($user);
        }

        /**
         * If you want to munge the case of an account name before the final
         * check, now is your chance.
         */
        function getCanonicalName( $username ) {
                return $username;
        }
}

/*
 * End of AuthPlugin Code, beginning of hook code and auth functions
 */
function SetupSiteMinderAuth()
{
        global $siteminder_user_name;
        global $wgHooks;
        global $wgAuth;

        if($siteminder_user_name != null)
        {
                $wgHooks['AutoAuthenticate'][] = 'SiteminderAutoAuth'; /* Hook for magical authN */
                $wgHooks['PersonalUrls'][] = 'SiteminderSSOActive'; /* Disallow logout link */
                $wgAuth = new SiteminderAuthPlugin();
        }
}

/* Kill or replace logout link */
function SiteminderSSOActive(&$personal_urls, $title)
{
        global $siteminder_logout;
        global $siteminder_real_name;
        global $siteminder_map_info;

        if($siteminder_logout == null)
                $personal_urls['logout'] = null;
        else
                $personal_urls['logout']['href'] = $siteminder_logout;

        if($siteminder_real_name && $siteminder_map_info)
                $personal_urls['userpage']['text'] = $siteminder_real_name;
        return true;
}

/* Tries to be magical about when to log in users and when not to. */
function SiteminderAutoAuth(&$user)
{
        global $wgContLang;
        global $wgAuth;
        global $siteminder_user_name;
        global $wgHooks;
        global $siteminder_map_info;

        //Temporarily kill The SiteminderAutoAuth Hook to prevent recursion
        foreach ($wgHooks['AutoAuthenticate'] as $key => $value)
        {
                if($value == 'SiteminderAutoAuth')
                    $wgHooks['AutoAuthenticate'][$key] = 'BringBackAA';
        }

        //For versions of mediawiki which enjoy calling AutoAuth with null users
        if ($user === null) {
                $user = User::loadFromSession();
        }

        //They already with us?  If so, nix this function, we're good.
        if($user->isLoggedIn())
        {
                BringBackAA();
                return true;
        }

        //Is the user already in the database?
        if (User::idFromName($siteminder_user_name) != null)
        {
                $user = User::newFromName($siteminder_user_name);
                $smi = $siteminder_map_info;
                $siteminder_map_info = false;
                $wgAuth->updateUser($user); //Make sure password is nologin
                $siteminder_map_info = $smi;
                $user->SetupSession();
                $user->setCookies();
                return true;
        }

        //Place the hook back (Not strictly necessarily MW Ver >= 1.9)
        BringBackAA();

        //Okay, kick this up a notch then...
        $user->setName($wgContLang->ucfirst($siteminder_user_name));

        /*
         * Since we only get called when someone should be logged in, if they
         * aren't let's make that happen.  Oddly enough the way MW does all
         * this is simply to use a loginForm class that pretty much does
         * most of what you need.  Creating a loginform is a very very small
         * part of this object.
         */
        require_once('SpecialUserlogin.php');

        //This section contains a silly hack for MW
        global $wgLang;
        global $wgContLang;
        global $wgRequest;
        $wgLangUnset = false;
        if(!isset($wgLang))
        {
                $wgLang = $wgContLang;
                $wgLangUnset = true;
        }

        //Temporarily kill The AutoAuth Hook to prevent recursion
        foreach ($wgHooks['AutoAuthenticate'] as $key => $value)
        {
                if($value == 'SiteminderAutoAuth')
                    $wgHooks['AutoAuthenticate'][$key] = 'BringBackAA';
        }

        //This creates our form that'll do black magic
        $lf = new LoginForm($wgRequest);

        //Place the hook back (Not strictly necessarily MW Ver >= 1.9)
        BringBackAA();

        //And now we clean up our hack
        if($wgLangUnset == true)
        {
                unset($wgLang);
                unset($wgLangUnset);
        }

        //Now we _do_ the black magic
        $lf->mRemember = false;
        $lf->initUser($user);

        //Finish it off
        $user->saveSettings();
        $user->setupSession();
        $user->setCookies();
        return true;
}

/* Puts the auto-auth hook back into the hooks array */
function BringBackAA()
{
        global $wgHooks;

        foreach ($wgHooks['AutoAuthenticate'] as $key => $value)
        {
            if($value == 'BringBackAA')
                $wgHooks['AutoAuthenticate'][$key] = 'SiteminderAutoAuth';
        }
        return true;
}
?>

Missing Features[edit]

There are several features I'd like to incorporate, but haven't yet. Having used the LDAP Authentication extension before, I'd like to borrow some of its features. Note that references below to LDAP Group would be replaced with what ever ACL mechanism your authentication facility provides:

  • Restrictions by LDAP groups
    Presently, the Siteminder facility itself can control access by examining LDAP groups and allowing/denying access to the site. However, I'd like to provide the flexibility to do that within the wiki so that a more generic SiteMinder definition might be used on a given server.
  • Synchronization with LDAP groups
    Essentially, I'd like to be able to populate permission groups via the external authentication facility's features and have those flow into the wiki implementation.
  • Disable password change
    Changing of passwords within the wiki is meaningless if the site is protected only by Siteminder. I'd like to add code to remove the option of changing your password, but I simply haven't done any research into that area.

Suggestions[edit]

I loaded the plugin with the latest version of MediaWiki (1.13). When I log in, I can see the user's full name at the top right, but am NOT automatically logged in. What needs to be changed in the extension code to get this to auto-login?

Changelog[edit]

No changes yet.

Acknowledgements[edit]

Heritage[edit]

Based upon the Shibboleth extension authored by D.J. Capelis and Steven Langenake

Bug Reporters[edit]

1) We have Media WIKI 1.24.1 and Apache 2.4.9 and PHP 5.5.12 . I have followed this page guidelines and see error:

Strict standards: Declaration of SiteminderAuthPlugin::modifyUITemplate() should be compatible with AuthPlugin::modifyUITemplate(&$template, &$type) in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php on line 60

Strict standards: Declaration of SiteminderAuthPlugin::setPassword() should be compatible with AuthPlugin::setPassword($user, $password) in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php on line 60

Strict standards: Declaration of SiteminderAuthPlugin::addUser() should be compatible with AuthPlugin::addUser($user, $password, $email = '', $realname = '') in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php on line 60

Strict standards: Declaration of SiteminderAuthPlugin::initUser() should be compatible with AuthPlugin::initUser(&$user, $autocreate = false) in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php on line 60

Warning: Cannot modify header information - headers already sent by (output started at D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php:60) in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\includes\WebResponse.php on line 37

Warning: Cannot modify header information - headers already sent by (output started at D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\extensions\SiteminderAuthPlugin.php:60) in D:\apps\Apache24\bin\apache\apache2.4.9\htdocs\mediawiki-1.24.1\includes\WebResponse.php on line 37

Testers[edit]

No testers yet