Extension:Phpbb Single Sign-On

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

Release status: beta

Implementation User identity
Description Uses the phpbb session to Automatically log users in out based on their phpBB3 login status. - Based off of Extension:AutomaticREMOTE USER
Author(s) Adam Meyer, Rusty Burchfield, User:Otheus Shelling, James Kinsman, Ian Ward Comfort
Latest version 0.1
MediaWiki  ? Tested on 1.16
License GPLv2
Download No link

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

Check usage and version matrix; code metrics

It is finally here!!

This is a TRUE 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. On my site I even removed the wiki login/out and replaced it with the forum login/out.

This plugin is a hacked version of Extension:AutomaticREMOTE USER - 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 curent, so please, take control.

Installation[edit | edit source]

This is an extension for mediawiki, but requires very small edit to phpbb core files to make it work. Nothning that will hurt phpbb at all, and will be completely compatable with any other mods you have.

Because both mediawiki and phpbb have a "user" class they want to fight. So we tell phpbb that when it is being used by mediawiki that we call the class phpbb_user and not user. I am extending the class here so we dont have to make a second copy of the entire user class


in your phpbb installation:[edit | edit source]

in <forum directory>/common.php[edit | edit source]

find

// Instantiate some basic classes
$user = new user();

replace with

// Instantiate some basic classes
$user = (!defined('FROM_MEDIAWIKI'))? new user(): new phpbb_user();

find

// Set PHP error handler to ours
set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');

replace with

// Set PHP error handler to ours
if(!defined('FROM_MEDIAWIKI')){
	set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');
}


In <forum directory>/includes/session.php[edit | edit source]

find

class user extends session
{

replace with

if (!defined('FROM_MEDIAWIKI')){
	class user extends __user{
		function __construct() {
			parent::user();
		}
	}
}else{
	class phpbb_user extends __user{
		function __construct() {
			parent::user();
		}
	}
}
 
class __user extends session
{

in your MediaWiki installation:[edit | edit source]

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

Place the following lines in your LocalSettings.php file: This includes the plugin, allows for user creation when needed and disables login / out of the wiki by normal means

CHANGE THIS FOR YOUR SITE $wgPhpbbSSO_Forum_Location - This needs to be the location of your forum root folder from your wiki.

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

  • example.com/forum
  • example.com/wiki
$wgPhpbbSSO_Forum_Location = './../forum/';  //Using your absolute server path to this would be better; trailing slash required - e.g. /home/user/www/forum/   
require_once("$IP/extensions/phpbSSO/phpbSSO.php");
$wgAuth = new Auth_remoteuser();
 
function noLoginLogOUt(&$list) {
        unset( $list['Userlogout'] );
        unset( $list['Userlogin'] );
        return true;
}
 
$wgHooks['SpecialPage_initList'][]='noLoginLogOUt';

New Files[edit | edit source]

Create a folder in your MediaWiki extensions folder called phpbSSO and save the following two files in there as phpbSSO.php and phpbb.php.


<wiki directory>/extensions/phpbSSO/phpbb.php[edit | edit source]
<?php
 
class phpbbSSO{
 
 
    //////////////////////////////////////////////////////////////////
    //CONFIGURE
    //////////////////////////////////////////////////////////////////
    const REQUIRE_GROUP = false; //must the user be part of a specific group?
    const WIKI_GROUP = 'Wiki'; //What group?
 
 
 
 
    private $phpbb_user;
    public $userId;
    public $userType;
    public $userName;
    public $email;
    public $username_clean;
    public $session_id;
    public $messages;
 
    function __construct($forumDirectory){
    //////////////////////////////////////////////////////////////////
    // Connect to phpbb and setup/get session
    //////////////////////////////////////////////////////////////////
        global $db, $cache, $config, $user, $phpbb_root_path, $phpEx;
 
        $phpbb_root_path = $forumDirectory;
 
 
 
        define('IN_PHPBB', true);
        define('FROM_MEDIAWIKI', true);
 
        $phpEx = substr(strrchr(__FILE__, '.'), 1);
        include_once($phpbb_root_path.'common.'.$phpEx);
 
        //group_membership();
 
        $user->session_begin();                 
        $this->phpbb_user = $user;
 
        $this->userId = $this->phpbb_user->data['user_id'];
        $this->userType = $this->phpbb_user->data['user_type'];
        $this->userName = $this->phpbb_user->data['username'];
        $this->email = $this->phpbb_user->data['user_email'];
        $this->username_clean = $this->phpbb_user->data['username_clean'];
        $this->session_id = $this->phpbb_user->data['session_id'];
        $this->messages = $this->phpbb_user->data['user_new_privmsg'];
 
        unset($user);
        unset($db);
        unset($cache);
        unset($config);
        unset($user);
        unset($phpbb_root_path);
        unset($phpEx);
 
 
 
    }
 
 
 
    private function checkIfUserInGroup($group = 'Wiki'){
    //////////////////////////////////////////////////////////////////
    // Check if the user is part of a specific group
    //////////////////////////////////////////////////////////////////
        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($group) .'"
        AND '.USER_GROUP_TABLE.'.user_id = "'. $db->sql_escape($this->userId) .'"'; 
 
        $result = $db->sql_query($sql);
        $row = $db->sql_fetchrow($result);
 
 
        if($row['count'] > 0){
            return true;
        }else{
            return false;
        }
 
    }
 
 
    public function isAnonymous(){
    //////////////////////////////////////////////////////////////////
    // find if user is anonymous or a bot, or nor part of the group
    //////////////////////////////////////////////////////////////////
 
        if(self::REQUIRE_GROUP){
            //if a group is required, and that user is not in that group, say anonymous
            if(! $this->checkIfUserInGroup(self::WIKI_GROUP)) return true;
        }
 
 
        if($this->userId == 1 || $this->userType == 1 || $this->userType == 2) return true;
 
        return false;
 
 
 
    }
}
?>
<wiki directory>/extensions/phpbSSO/phpbSSO.php[edit | edit source]
<?php
 
 
	$wgExtensionCredits['other'][] = array(
                'path' => __FILE__,
		'name' => 'phpBB SSO',
		'version' => '0.1',
		'author' => array( 'Adam Meyer', 'Otheus Shelling', 'Rusty Burchfield', 'James Kinsman', 'Daniel Thomas', 'Ian Ward Comfort' ),
		'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 anony edits
	$GLOBALS['wgGroupPermissions']['*']['edit'] = false;
	$GLOBALS['wgGroupPermissions']['*']['createaccount'] = false;
	$wgMinimalPasswordLength = 1;
 
 
	require('phpbb.php');
	$wgPhpbbSSO = new phpbbSSO($wgPhpbbSSO_Forum_Location);
 
 
 
	$wgExtensionFunctions[] = 'phpbbSSO_hook';
 
	function phpbbSSO_hook() {
		global $wgUser, $wgRequest, $wgPhpbbSSO, $wgAuth;
 
		$user = User::newFromSession();
 
 
 
		// 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;
		}
 
		if($wgPhpbbSSO->isAnonymous()){
			$user->doLogout();
			return;
		}
 
 
		if ( !$user->isAnon() ) {
 
			if ( $user->getName() == $wgAuth->getCanonicalName($wgPhpbbSSO->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($wgPhpbbSSO->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;
	}
 
	class Auth_remoteuser 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, $wgPhpbbSSO, $wgAuth;
 
			$username = $wgAuth->getCanonicalName($wgPhpbbSSO->username_clean);
			$user->setRealName( '' );
			$user->setEmail( $wgPhpbbSSO->email );
			$user->mPassword = ' ';
 
			//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 ) {
			// 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
			return ucfirst( $username );
		}
	}

Note: Using this extension sets $wgMinimalPasswordLength to 1.

Using it in your MediaWiki skin[edit | edit source]

You probably want to change the login/register / logout links to work with the system.

This is how I did this for my wiki.

Replace the current code with the the following code (or something similar) In the skin php file where the personal links are. - In vector they are located in function renderNavigation under case 'PERSONAL':

This also adds a line so a user can see if they have any forum messages from the wiki. I changed the words "user preferences" to "wiki preferences" so it is not confusing to users.

Make sure to set the $forumURL to your forum address.

$phpbbUser = new phpbbSSO();
$forumURL = 'http://example.org/forum';		
 
if($phpbbUser->isAnonymous()){
	//If a logged in user is not part of the group, he's treated as not logged in. Thus we don't display the Login link if group membership is required.
	if(!phpbbSSO::REQUIRE_GROUP){
		echo '<a href="'.$forumURL.'/ucp.php?mode=login" title="Login" accesskey="l">Login</a>
		 / <a href="'.$forumURL.'/ucp.php?mode=register">Register</a>';
	}
}else{
	echo '
	<a href="'.$prefsUrl.'">wiki preferences</a> | 
	<a href="'.$forumURL.'/ucp.php?i=pm&amp;folder=inbox"><strong>'.$phpbbUser->messages.'</strong> new messages</a> | 
	<a href="'.$forumURL.'/ucp.php?mode=logout&sid='.$phpbbUser->session_id.'" title="Logout [ '.$phpbbUser->userName.' ]" accesskey="l">Logout [ '.$phpbbUser->userName.' ]</a>';
}

In Mediawiki 1.18>

$phpbbUser = new phpbbSSO();

It is necessary to replace on

$phpbbUser = new phpbbSSO('./../forum/');

It may be necessary to define the 'wiki preferences' link. Updating for your site, replace

<a href="'.$prefsUrl.'">wiki preferences</a> |

with

<a href="http://www.example.com/wiki/index.php?title=Special:Preferences">wiki preferences</a> |

How It Works[edit | edit source]

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.


Troubleshooting[edit | edit source]

Cookies[edit | edit source]

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

Backups[edit | edit source]

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

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