Extension:Phpbb Single Sign-On

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manualManual:Extensions
Crystal Clear action run.png
phpBB Single Sign-On

Release status:Extension status beta

ImplementationTemplate:Extension#type User identity
DescriptionTemplate:Extension#description Uses the phpBB session to automatically log users in or out based on their phpBB3 login status
Author(s)Template:Extension#username Adam Meyer, Rusty Burchfield, User:Otheus Shelling, James Kinsman, Ian Ward Comfort, AJ Quick, PH Baudin
Latest versionTemplate:Extension#version 0.12.1
MediaWikiTemplate:Extension#mediawiki 1.19+
LicenseTemplate:Extension#license GPLv2
Download No link

Translate the Phpbb Single Sign-On extension if it is available at translatewiki.net

Check usage and version matrix.

This is a single sign-on extension for MediaWiki and phpBB3. It does not use any funny tricks like curl or anything like that. It actually uses the phpBB sessions to do it all. When a user logs into the forum, they will be logged into the wiki. If a user logs out of the forum, they are logged out of the wiki.

The extension also replaces the wiki login and logout links with the ones from the forum.

This plugin is a hacked version of Extension:Auth_remoteuser. It was used as the base for this, I just replaced all needed code to be specific to phpBB.

This is very beta - Please make any edits to this code to make it better. I probably will not keep the code current, so please take control.

How it works[edit]

So... This little guy, what he does is: When a user comes to the wiki, it checks to see if the user is currently logged into the forum. If not, nothing happens. If so, he then looks to see if the user is logged into the wiki. If the user is, then everything is good. If the user is not, he takes the session data from phpbb and logs the user in automatically to the wiki. If there is no such user on the wiki, he creates the user.

Installation[edit]

In <wiki directory>/LocalSettings.php[edit]

Place the following lines in your LocalSettings.php file:

$wgPhpBBForumLocation = './../forum/';
require_once "$IP/extensions/phpBBSSO/phpBBSSO.php";

The default for $wgPhpBBForumLocation (./../forum/) works if your site is setup like so:

  • example.com/forum
  • example.com/wiki

Be sure to change it if needed.

New Files[edit]

Create a folder in your MediaWiki extensions folder called phpBBSSO and save the following three files in there as phpBBSSO.php, phpBBUser.php and AuthRemoteUser.php.


<wiki directory>/extensions/phpBBSSO/phpBBSSO.php[edit]

<?php

$wgExtensionCredits['other'][] = array(
	'name' => 'phpBB SSO',
	'version' => '0.12.1',
	'author' => array('Adam Meyer', 'Otheus Shelling', 'Rusty Burchfield', 'James Kinsman', 'Daniel Thomas', 'Ian Ward Comfort', 'AJ Quick', 'P-H Baudin'),
	'url' => 'http://www.mediawiki.org/wiki/Extension:Phpbb_Single_Sign-On',
	'description' => 'Automatically logs users in/out using their phpBB session. Based on the Auth_remote_user extension',
);

// This requires a user be logged into the wiki to make changes and no anonymous edits
$GLOBALS['wgGroupPermissions']['*']['edit'] = false;
$GLOBALS['wgGroupPermissions']['*']['createaccount'] = false;

if (!isset($wgPhpBBForumLocation)) {
	$wgPhpBBForumLocation = './../forum/';
}

require('phpBBUser.php');
$wgPhpBBUser = new phpBBUser($wgPhpBBForumLocation);

require('AuthRemoteUser.php');
$wgAuth = new AuthRemoteUser();

$wgExtensionFunctions[] = 'phpbbSSOExtensionFunction';

$wgHooks['SpecialPage_initList'][] = 'onSpecialPage_initList';

$wgHooks['PersonalUrls'][] = 'onPersonalUrls';

function phpbbSSOExtensionFunction() {
	global $wgUser, $wgRequest, $wgPhpBBUser, $wgAuth;

	$user = User::newFromSession();

	// For a few special pages, don't do anything.
	$title = $wgRequest->getVal('title');
	if (in_array($title, array(Title::makeName(NS_SPECIAL, 'UserLogout'), Title::makeName(NS_SPECIAL, 'UserLogin')))) {
		return;
	}

	if ($wgPhpBBUser->isAnonymous()) {
		$user->doLogout();
		return;
	}

	if (!$user->isAnon()) {
		if ($user->getName() == $wgAuth->getCanonicalName($wgPhpBBUser->username_clean)) {
			return; // Correct user is already logged in.
		}
		else {
			$user->doLogout(); // Logout mismatched user.
		}
	}

	// Copied from includes/SpecialUserlogin.php
	if (!isset( $wgCommandLineMode ) && !isset($_COOKIE[session_name()])) {
		wfSetupSession();
	}

	// If the login form returns NEED_TOKEN try once more with the right token
	$trycount = 0;
	$token = '';
	$errormessage = '';
	do {
		$tryagain = false;
		// Submit a fake login form to authenticate the user.
		$params = new FauxRequest(array(
			'wpName' => $wgAuth->getCanonicalName($wgPhpBBUser->username_clean),
			'wpPassword' => ' ',
			'wpDomain' => '',
			'wpLoginToken' => $token,
			'wpRemember' => ''
		));

		// Authenticate user data will automatically create new users.
		$loginForm = new LoginForm($params);
		$result = $loginForm->authenticateUserData();

		switch ($result) {
			case LoginForm::SUCCESS :
				$wgUser->setOption('rememberpassword', 1);
				$wgUser->setCookies();
				break;
			case LoginForm::NEED_TOKEN:
				$token = $loginForm->getLoginToken();
				$tryagain = ($trycount == 0);
				break;
			case LoginForm::WRONG_TOKEN:
				$errormessage = 'WrongToken';
				break;
			case LoginForm::NO_NAME :
				$errormessage = 'NoName';
				break;
			case LoginForm::ILLEGAL :
				$errormessage = 'Illegal';
				break;
			case LoginForm::WRONG_PLUGIN_PASS :
				$errormessage = 'WrongPluginPass';
				break;
			case LoginForm::NOT_EXISTS :
				$errormessage = 'NotExists';
				break;
			case LoginForm::WRONG_PASS :
				$errormessage = 'WrongPass';
				break;
			case LoginForm::EMPTY_PASS :
				$errormessage = 'EmptyPass';
				break;
			default:
				$errormessage = 'Unknown';
				break;
		}

		if ($result != LoginForm::SUCCESS && $result != LoginForm::NEED_TOKEN) {
			echo $errormessage;
			error_log('Unexpected REMOTE_USER authentication failure. Login Error was:' . $errormessage);
		}
		$trycount++;
	} while ($tryagain);

	return;
}

function onSpecialPage_initList(&$aSpecialPages) {
	unset($aSpecialPages['Userlogout']);
	unset($aSpecialPages['Userlogin']);

	return true;
}

function onPersonalUrls(array &$personal_urls, Title $title, SkinTemplate $skin) {
	global $wgPhpBBForumLocation, $wgPhpBBUser;

	$forumLocation = dirname(dirname(substr(__DIR__, strlen(str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['SCRIPT_FILENAME']))))) . '/' . $wgPhpBBForumLocation;

	if (array_key_exists('login', $personal_urls)) {
		$personal_urls['login']['href'] = $forumLocation . 'ucp.php?mode=login';
	}

	if (array_key_exists('anonlogin', $personal_urls)) {
		$personal_urls['anonlogin']['href'] = $forumLocation . 'ucp.php?mode=login';
	}

	if (array_key_exists('logout', $personal_urls)) {
		$personal_urls['logout']['href'] = $forumLocation . 'ucp.php?mode=logout&sid=' . $wgPhpBBUser->session_id;
	}

	return true;
}

<wiki directory>/extensions/phpBBSSO/phpBBUser.php[edit]

<?php

class phpBBUser {
	/*
	 * Configuration
	 */
	const REQUIRE_GROUP = false; // Must the user be part of a specific group?
	const WIKI_GROUP = 'Wiki'; // If yes, what group?

	public $userId;
	public $userType;
	public $userName;
	public $email;
	public $username_clean;
	public $session_id;
	public $messages;

	/*
	 * Connect to phpBB and setup/get session
	 */
	function __construct($forumDirectory) {
		global $db, $cache, $config, $user, $auth, $template, $phpbb_root_path, $phpEx, $request, $symfony_request, $phpbb_filesystem, $phpbb_container, $phpbb_dispatcher, $phpbb_extension_manager, $phpbb_path_helper;

 		define('PHPBB_ROOT_PATH', $forumDirectory);
		define('IN_PHPBB', true);

		$phpbb_root_path = PHPBB_ROOT_PATH;
		$phpEx = substr(strrchr(__FILE__, '.'), 1);
		include($phpbb_root_path . 'common.' . $phpEx);

		// Start session management
		$user->session_begin();
		$auth->acl($user->data);
		$user->setup();

		$request->enable_super_globals();

		$this->userId = $user->data['user_id'];
		$this->userType = $user->data['user_type'];
		$this->userName = $user->data['username'];
		$this->email = $user->data['user_email'];
		$this->username_clean = $user->data['username_clean'];
		$this->session_id = $user->data['session_id'];
		$this->messages = $user->data['user_new_privmsg'];

		unset($db, $cache, $config, $user, $auth, $template, $phpbb_root_path, $phpEx, $request, $symfony_request, $phpbb_filesystem, $phpbb_container, $phpbb_dispatcher, $phpbb_extension_manager);
	}

	/*
	 * Check if the user is part of a specific group
	 */
	private function checkIfUserInGroup() {
		global $db; // phpBB database object

		$sql = 'SELECT
					COUNT(*) as count
				FROM
					' . USER_GROUP_TABLE . ', ' . GROUPS_TABLE . '
				WHERE
					' . USER_GROUP_TABLE . '.group_id = ' . GROUPS_TABLE . '.group_id AND
					' . USER_GROUP_TABLE . '.user_pending = 0  AND
					' . GROUPS_TABLE . '.group_name = "' . $db->sql_escape(self::WIKI_GROUP) . '"  AND
					' . USER_GROUP_TABLE . '.user_id = "' . $db->sql_escape($this->userId) . '"';

		$result = $db->sql_query($sql);
		$row = $db->sql_fetchrow($result);

		return (bool) $row['count'];
	}

	/*
	 * Find if user is anonymous or a bot, or nor part of the group
	 */
	public function isAnonymous() {
		if (self::REQUIRE_GROUP && !$this->checkIfUserInGroup()) { // If a group is required, and that user is not in that group, say anonymous
			return true;
		}

		if ($this->userId == 1 || $this->userType == 1 || $this->userType == 2) {
			return true;
		}

		return false;
	}
}

<wiki directory>/extensions/phpBBSSO/AuthRemoteUser.php[edit]

<?php

class AuthRemoteUser extends AuthPlugin {
	/**
	 * Disallow password change.
	 *
	 * @return bool
	 */
	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
	 */
	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
	 */
	public function addUser($user, $password, $email = '', $realname = '')
	{
		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) {
		return true;
	}

	/**
	 * 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, $autocreate = true) {
		global $c, $wgPhpBBUser, $wgAuth;

		$username = $wgAuth->getCanonicalName($wgPhpBBUser->username_clean);
		$user->setEmail($wgPhpBBUser->email);

		// The update user function does everything else we need done.
		$this->updateUser($user);

		$user->mEmailAuthenticated = wfTimestampNow();
		$user->setToken();

		$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, &$type) {
		// 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 then uppercase first letter
		return ucfirst(strtolower($username));
	}
}

Troubleshooting[edit]

Cookies[edit]

If the user has cookies turned off, they will probably find themselves in an endless redirection loop.

Backups[edit]

If your backups don't work, try setting the REMOTE_USER environment variable manually:

REMOTE_USER="wikiusername" php dumpBackup.php  --full