Mediawiki: Difference between revisions

From RavenWiki
Jump to navigationJump to search
No edit summary
No edit summary
Line 76: Line 76:
These hacks all assume that the Mediawiki application is first protected by something that sets the REMOTE_USER apache server variable, such as the [http://raven.cam.ac.uk/project/apache/ Raven Apache authentication module].
These hacks all assume that the Mediawiki application is first protected by something that sets the REMOTE_USER apache server variable, such as the [http://raven.cam.ac.uk/project/apache/ 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):
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
* 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:
[http://www.mediawiki.org/wiki/Extension:AutomaticREMOTE_USER#mediawiki_1.9.4 Extension:AutomaticREMOTE USER]
 
Documentation below in case the above disappears:
 
After installing mediawiki put the text below into: extensions/Auth_remoteuser.php
<pre>
<?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 false;
  }
  /**
  * 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 false;
  }
  /**
  * 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;
  }
}
?>
</pre>
 
Yes, I have commented by code changes with my CRSID, feel free to replace the comments.
 
The code changes:
 
<pre>
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;
  }
</pre>

Revision as of 07:36, 27 September 2007

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 have a Raven-enabled copy of Mediawiki that is available to the university - see http://wiki.caret.cam.ac.uk/

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.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.

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

<?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 false;
  }
 
  /**
   * 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 false;
  }
 
  /**
   * 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 by 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;
  }