Mediawiki

From RavenWiki
Jump to navigationJump to search
The Raven-related software described on this page is NOT supported or maintained by University Information Services. It is provided here in the hope that it may be useful, but it may contain bugs and security vulnerabilities. It may be supported and maintained by others. You should evaluate whether it meets you particular needs before using it.

Mediawiki is the wiki software powering wikipedia. It can fairly easily be made to work with Raven. The Computing Service has been using a Raven-enabled copy of Mediawiki internally for some time. CARET once had a Raven-enabled copy of Mediawiki that was available to the university

Software hacks, 1.5.2

These hacks all assume that the Mediawiki application is first protected by something that sets the REMOTE_USER apache server variable, such as the Raven Apache authentication module.

User.php

Line 60, newFromName, replace:

$t = Title::newFromText( $name ); 

with:

$t = Title::newFromText( $name , NS_USER ); 

To explicitly specify the namespace. This will be used to check in the hack in Title.php, below.

Line 153, idFromName:

$s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $name ), $fname );

... and in the function loadFromSession():

if ( isset( $_SERVER["REMOTE_USER"] ) ) {
  $sName = $_SERVER["REMOTE_USER"];
  $sId = User::idFromName($sName);
  $user = new User();
  if ( $sId == 0 ) {
    /* User doesn't exist here yet.  */
    $user->setName($sName);
    $user->addToDatabase();
  } else {
    $user->mId = $sId;
    $user->loadFromDatabase();
  }
  return $user;
}            

Title.php

Stop the first letter of userpages being capitalised, so we can stick with CRSIDs being all lowercase everywhere. To do this, in the (private) function function secureAndSplit(), replace:

if( $wgCapitalLinks && $this->mInterwiki == '' ) {          
  $t = $wgContLang->ucfirst( $r );
} else {
  $t = $r;                 
}             

With:

if( $wgCapitalLinks && $this->mInterwiki == '' && NS_USER != $this->mNamespace ) {          
  $t = $wgContLang->ucfirst( $r );
} else {
  $t = $r;                 
}             

SkinTemplate.php

To remove the "log out" link at the top of the page, comment out the following section like so:

#                       $personal_urls['logout'] = array( 
#                               'text' => wfMsg('userlogout'),
#                               'href' => $this->makeSpecialUrl('Userlogout','returnto=' . $this->thisurl )
#                       );   

Software hacks, 1.9.3

Here's a set of hacks developed (though not supported) by staff within the Computing Service for supporting Ucam WebAuth authentication in MediaWiki 1.9.3 (and probably others).

These hacks all assume that the Mediawiki application is first protected by something that sets the REMOTE_USER apache server variable, such as the Raven Apache authentication module.

Overview

ucamwebauth.php is a Mediawiki extension that works with Raven (UcamWebAuth only at the moment), allowing Raven logins without having to hack the Mediawiki sourcecode. It also solves the "first edit of the session is a preview" problem with the hacked v1.5.3 version.

Important: links are now case sensitive, and page names aren't automatically capitalised anymore. This may in particular cause problems with files that were uploaded with a lowercase filename, but which were referenced in the wiki markup with a starting capital letter. The solution is to change the wiki text to lowercase.

Installation

Add the following to the bottom of LocalSettings.php:

$wgCapitalLinks = false;
$wgGroupPermissions['*']['createaccount']   = false;
require_once('extensions/ucamwebauth.php');
SetupUcamWebAuth();

Copy ucamwebauth.php into the extensions directory of the wiki.

Version history

v0.2

  • the entire site has to be Raven-protected
  • when not so, login links don't work
  • it's currently impossible to protect the default login link, because Apache's Location directives refuse to deal with query strings.
  • usernames are CRSIDs. These are lowercase. This involves some hacking, bypassing some of the code in includes/User.php. I don't think it breaks anything.

Software hacks, 1.9.4

These hacks all assume that the Mediawiki application is first protected by something that sets the REMOTE_USER apache server variable, such as the Raven Apache authentication module.

The hacks for 1.5.2 have been really useful, but I've had a couple of problems (maybe my fault as I notice this wiki is fine with both of these things):

  • upload of files requires creation of a "real" wiki account, which Raven users can do, but is a slight faff
  • changes are logged against IP rather than Raven ID

This media-wiki extension solves both of these problems, for me, and is easy to implement: Extension:AutomaticREMOTE USER

Documentation below in case the above disappears:

After installing mediawiki put the text below into extensions/Auth_remoteuser.php and add these two lines to the bottom of your LocalSettings.php:

require_once('extensions/Auth_remoteuser.php');
$wgAuth = new Auth_remoteuser();

extensions/Auth_remoteuser.php :

<?php
// vim:sw=2:softtabstop=2:textwidth=80
// See http://meta.wikimedia.org/wiki/User:Otheus/Auto_Login_via_REMOTE_USER
// Adapted by Rusty to be compatible with version 1.9 of mediawiki
//
// Add these two lines to the bottom of your LocalSettings.php
// require_once('extensions/Auth_remoteuser.php');
// $wgAuth = new Auth_remoteuser();
//
// The constructor of Auth_remoteuser registers a hook to do the automatic
// login.  Storing the Auth_remoteuser object in $wgAuth tells mediawiki to use
// that object as the AuthPlugin.  This way the login attempts by the hook will
// be handled by us.
//
// You probably want to edit the initUser function to set the users real name
// and email address properly for your configuration.
 
// Don't let anonymous people do things...
$wgGroupPermissions['*']['createaccount']   = false;
$wgGroupPermissions['*']['read']            = false;
$wgGroupPermissions['*']['edit']            = false;
 
// The Auth_remoteuser class is an AuthPlugin so make sure we have this
// included.
require_once('AuthPlugin.php');
 
/**
 * This hook is registered by the Auth_remoteuser constructor.  It will be
 * called on every page load.  It serves the function of automatically logging
 * in the user.  The Auth_remoteuser class is an AuthPlugin and handles the
 * actual authentication, user creation, etc.
 *
 * Details:
 * 1. Check to see if the user has a session and is not anonymous.  If this is
 *    true we can just return.
 * 2. If the user doesn't have a session, we create a login form with our own
 *    fake request and ask the form to authenticate the user.  If the user does
 *    not exist authenticateUserData will attempt to create one.  The login form
 *    uses our Auth_remoteuser class as an AuthPlugin.
 *
 * Note: If cookies are disabled, an infinite loop /might/ occur?
 */
function Auth_remote_user_hook() {
  global $wgUser;
  global $wgRequest;
  global $_REQUEST;
 
  // For a few special pages, don't do anything.
  $title = $wgRequest->getVal('title');
  if (($title == Title::makeName(NS_SPECIAL, 'Userlogout')) ||
      ($title == Title::makeName(NS_SPECIAL, 'Userlogin'))) {
    return;
  }
 
  // Do nothing if session is valid
  $user = User::newFromSession();
  if (!$user->isAnon()) {
    return;  // User is already logged in and not anonymous.
  }
 
  // Copied from includes/SpecialUserlogin.php
  if(!isset($wgCommandLineMode) && !isset($_COOKIE[session_name()])) {
    wfSetupSession();
  }
 
  // Submit a fake login form to authenticate the user.
  $username = $_SERVER['REMOTE_USER' ];
  $params = new FauxRequest(array(
        'wpName' => $username,
        'wpPassword' => '',
        'wpDomain' => '',
        'wpRemember' => ''
        ));
 
  // Authenticate user data will automatically create new users.
  $loginForm = new LoginForm($params);
  $result = $loginForm->authenticateUserData();
  if ($result != LoginForm::SUCCESS) {
    error_log('Unexpected REMOTE_USER authentication failure.');
    return;
  }
 
  $wgUser->setCookies();
  return;  // User has been logged in.
}
 
class Auth_remoteuser extends AuthPlugin {
 
  function Auth_remoteuser() {
    // Register our hook function.  This hook will be executed on every page
    // load.  Its purpose is to automatically log the user in, if necessary.
    if ( strlen($_SERVER['REMOTE_USER']) ) {
      global $wgExtensionFunctions;
      if (!isset($wgExtensionFunctions)) {
        $wgExtensionFunctions = array();
      }
      else if (!is_array($wgExtensionFunctions)) {
        $wgExtensionFunctions = array( $wgExtensionFunctions );
      }
      array_push($wgExtensionFunctions, 'Auth_remote_user_hook');
    }
    return;
  }
 
  /**
   * Disallow password change.
   *
   * @return bool
   */
# mr349, 25/09/2007
  function allowPasswordChange() {
    return true;
  }
 
  /**
   * This should not be called because we do not allow password change.  Always
   * fail by returning false.
   *
   * @param $user User object.
   * @param $password String: password.
   * @return bool
   * @public
   */
# mr349, 25/09/2007
  function setPassword($user, $password) {
    return true;
  }
 
  /**
   * We don't support this but we have to return true for preferences to save.
   *
   * @param $user User object.
   * @return bool
   * @public
   */
  function updateExternalDB($user) {
    return true;
  }
 
  /**
   * We can't create external accounts so return false.
   *
   * @return bool
   * @public
   */
  function canCreateAccounts() {
    return false;
  }
 
  /**
   * We don't support adding users to whatever service provides REMOTE_USER, so
   * fail by always returning false.
   *
   * @param User $user
   * @param string $password
   * @return bool
   * @public
   */
  function addUser($user, $password) {
    return false;
  }
 
 
  /**
   * Pretend all users exist.  This is checked by authenticateUserData to
   * determine if a user exists in our 'db'.  By returning true we tell it that
   * it can create a local wiki user automatically.
   *
   * @param $username String: username.
   * @return bool
   * @public
   */
  function userExists($username) {
    return true;
  }
 
  /**
   * Check whether the given name matches REMOTE_USER.
   * The name will be normalized to MediaWiki's requirements, so
   * lower it and the REMOTE_USER before checking.
   *
   * @param $username String: username.
   * @param $password String: user password.
   * @return bool
   * @public
   */
  function authenticate($username, $password) {
    global $_SERVER;
    return isset($_SERVER['REMOTE_USER']) &&
           (strtolower($username) == strtolower($_SERVER['REMOTE_USER']));
  }
 
  /**
   * Check to see if the specific domain is a valid domain.
   *
   * @param $domain String: authentication domain.
   * @return bool
   * @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
   * @public
   */
  function updateUser(&$user) {
    // We only set this stuff when accounts are created.
    return true;
  }
 
  /**
   * Return true because 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.
   *
   * @return bool
   * @public
   */
  function autoCreate() {
    return true;
  }
 
  /**
   * Return true to prevent logins that don't authenticate here from being
   * checked against the local database's password fields.
   *
   * @return bool
   * @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.
   *
   * @param $user User object.
   * @public
   */
  function initUser(&$user) {
    global $_SERVER;
    $username = $_SERVER['REMOTE_USER'];
 
    // Using your own methods put the users real name here.
    $user->setRealName('');
    // Using your own methods put the users email here.
    $user->setEmail($username . '@example.com');
 
    $user->mEmailAuthenticated = wfTimestampNow();
    $user->setToken();
 
    //turn on e-mail notifications by default
    $user->setOption('enotifwatchlistpages', 1);
    $user->setOption('enotifusertalkpages', 1);
    $user->setOption('enotifminoredits', 1);
    $user->setOption('enotifrevealaddr', 1);
 
    $user->saveSettings();
  }
 
  /**
   * Modify options in the login template.  This shouldn't be very important
   * because no one should really be bothering with the login page.
   *
   * @param $template UserLoginTemplate object.
   * @public
   */
  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', false);
    $template->set('usedomain', false);
  }
 
  /**
   * Normalize user names to the mediawiki standard to prevent duplicate
   * accounts.
   *
   * @param $username String: username.
   * @return string
   * @public
   */
  function getCanonicalName($username) {
    // lowercase the username
    $username = strtolower($username);
    // uppercase first letter to make mediawiki happy
    $username[0] = strtoupper($username[0]);
    return $username;
  }
}
?>

Yes, I have commented my code changes with my CRSID, feel free to replace the comments.

The code changes:

Line 111 of ./extensions/Auth_remoteuser.php change the false from:
  function allowPasswordChange() {
    return false;
  }
to:
  function allowPasswordChange() {
    return true;
  }

Same for line 124:
  function setPassword($user, $password) {
    return false;
  }
to:
  function setPassword($user, $password) {
    return true;
  }

Instructions that were tested with 1.15.1

Can be found here (The only really SRCF specific bits are in the filesystem paths) It is also more of a HOWTO with less explanation of why things are done and what the reason is for doing them.

REMOTE_USER variable integration with version 1.22.2

Raven integration can now be achieved using the AutomaticREMOTE USER extension which can be found here. This uses the REMOTE_USER session variable, which will need to be set beforehand (for instance, using the Raven Apache authentication module).