PhpBB3 Api Bridge

By Wilson Sheldon (wilsonsheldon)
Hi everyone,

I recently needed to bridge between my Cake app and a PhpBB3 forum. When I realized the old PhpBB2 Bridge didn't work anymore, I decided to write a new one.

Public functions included are: login, logout, changePassword

INTRODUCTION


Hi everyone,

I recently needed to bridge between my Cake app and a PhpBB3 forum. When I realized the old PhpBB2 Bridge didn't work anymore, I decided to write a new one.

Public functions included are: login, logout, changePassword

In addition, the login function will check if a user current exists, and if not, will add them.

As much as possible, I used the native PhpBB3 'API'. However, for changePassword(), a custom query was required as well as a few password generation functions which were adapted from Jonathan Gibb's (http://www.polr.co.uk) PHPBB3Integration class.

Small caveat, to workaround the 'cache' classname collision, you must change four lines of code in PhpBB3. If someone knows of another workaround, please let me know.

In phpBB3/includes/cache.php, rename the 'cache' class.

Download code class phpbb3_cache extends acm
In these three files, 'phpBB3/common.php', 'phpBB3/style.php', and 'phpBB3/download/file.php' change:

Download code $cache = new cache()
to

Download code $cache = new phpbb3_cache()
Perhaps, you could store the four changes in an SVN branch and merge when you update your forum.

USAGE


To use the component, simply include it in your controller and call as you normally would.

Download code
var $components = array('PhpBB3');

Download code
//login to the forum as well
$this->PhpBB3->login($this->data['Member']['username'], $textPassword, $this->data['Member']['email']);

CODE


Component file name will be: app/controllers/components/php_b_b3.php

Component Class:

Download code <?php 
class PhpBB3Component extends Object {

    var 
$controller;
    var 
$model;

    function 
startup(&$controller) {

        
$this->controller = &$controller;

        
define('IN_PHPBB'true);

        global 
$phpbb_root_path$phpEx$db$config$user$auth$cache$template;

        
$phpbb_root_path WWW_ROOT 'phpBB3/';
        
$phpEx substr(strrchr(__FILE__'.'), 1);
        require_once(
$phpbb_root_path 'common.' $phpEx);
        
        
$this->table_prefix $table_prefix;
        
$this->auth $auth;
        
$this->user $user;
        
        
// Start session management
        
$this->user->session_begin();
        
$this->auth->acl($user->data);
        
$this->user->setup();
    
        require_once(
$phpbb_root_path .'includes/functions_user.php');

    }
    
    private function 
checkUserExists($username$isFalse false) {
        
        if (
user_get_id_name($isFalse$username) == 'NO_USERS') {
            return 
false;
        } else {
            return 
true;
        }    

    }

    
// Helper Methods
    
    /**
     * Generate salt for hash generation
     */
    
private function _hash_gensalt_private($input,&$itoa64,$iteration_count_log2 6) {
        if (
$iteration_count_log2 || $iteration_count_log2 31) {
            
$iteration_count_log2 8;
        }
        
        
$output '$H$';
        
$output .= $itoa64 [min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 3), 30)];
        
$output .= $this->_hash_encode64($input6$itoa64);
        
        return 
$output;
    }
    
    
/**
     * Encode hash
     */
    
private function _hash_encode64($input,$count,&$itoa64) {
        
        
$output '';
        
$i 0;
        
        do {
            
$value ord $input [$i ++] );
            
$output .= $itoa64 [$value 0x3f];
            
            if (
$i $count) {
                
$value |= ord $input [$i] ) << 8;
            }
            
            
$output .= $itoa64 [($value >> 6) & 0x3f];
            
            if (
$i ++ >= $count) {
                break;
            }
            
            if (
$i $count) {
                
$value |= ord $input [$i] ) << 16;
            }
            
            
$output .= $itoa64 [($value >> 12) & 0x3f];
            
            if (
$i ++ >= $count) {
                break;
            }
            
            
$output .= $itoa64 [($value >> 18) & 0x3f];
        } while ( 
$i $count );
        
        return 
$output;
    }
    
    
/**
     * The crypt function/replacement
     */
    
private function _hash_crypt_private($password,$setting,&$itoa64) {
        
$output '*';
        
        
// Check for correct hash
        
if (substr $setting0) != '$H$') {
            return 
$output;
        }
        
        
$count_log2 strpos $itoa64$setting [3] );
        
        if (
$count_log2 || $count_log2 30) {
            return 
$output;
        }
        
        
$count << $count_log2;
        
$salt substr $setting4);
        
        if (
strlen $salt ) != 8) {
            return 
$output;
        }
        
        
/**
         * We're kind of forced to use MD5 here since it's the only
         * cryptographic primitive available in all versions of PHP
         * currently in use.  To implement our own low-level crypto
         * in PHP would result in much worse performance and
         * consequently in lower iteration counts and hashes that are
         * quicker to crack (by non-PHP code).
         */
        
if (PHP_VERSION >= 5) {
            
$hash md5 $salt $passwordtrue );
            do {
                
$hash md5 $hash $passwordtrue );
            } while ( -- 
$count );
        } else {
            
$hash pack 'H*'md5 $salt $password ) );
            do {
                
$hash pack 'H*'md5 $hash $password ) );
            } while ( -- 
$count );
        }
        
        
$output substr $setting012 );
        
$output .= $this->_hash_encode64 $hash16$itoa64 );
        
        return 
$output;
    }
    
    private function 
unique_id($extra 'c') {
        static 
$dss_seeded false;
        global 
$config;
        
        
$val $config ['rand_seed'] . microtime ();
        
$val md5 $val );
        
$config ['rand_seed'] = md5 $config ['rand_seed'] . $val $extra );
        
        
$dss_seeded true;
        return 
substr $val416 );
    }
    

    private function 
phpbb_hash($password) {

        
$itoa64 './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
        
$random_state $this->unique_id();
        
$random '';
        
$count 6;
        
        if ((
$fh = @fopen '/dev/urandom''rb' ))) {
            
$random fread ($fh$count);
            
fclose ($fh);
        }
        
        if (
strlen($random) < $count) {
            
$random '';
            for(
$i=0;$i<$count;$i+=16) {
                
$random_state md5($this->unique_id () . $random_state);
                
$random .= pack('H*'md5($random_state));
            }
            
$random substr($random0$count);
        }
        
        
$hash $this->_hash_crypt_private($password$this->_hash_gensalt_private($random$itoa64 ), $itoa64);
        
        if (
strlen($hash) == 34) {
            return 
$hash;
        }
        
        return 
md5($password);
    }

    public function 
login($username$password$email) {
                            
        if (
$this->checkUserExists($username) == false) {
                                    
            
$user_row = array(
                
'username' => $username,
                
'user_password' => md5($password), 
                
'user_email' => $email,
                
'group_id' => 2//Registered users group
                
'user_timezone' => '1.00',
                
'user_dst' => 0,
                
'user_lang' => 'en',
                
'user_type' => '0',
                
'user_actkey' => '',
                
'user_dateformat' => 'd M Y H:i',
                
'user_style' => 1,
                
'user_regdate' => time(),
            );
            
            
user_add($user_row);
            
        }
        
        
$this->auth->login($username$password);
            
    }
    
    public function 
changePassword($username$password) {
        if (
$this->checkUserExists($username) == true) {
            global 
$db;
            
$sql "UPDATE `" $this->table_prefix "users` SET user_password = '" $this->phpbb_hash($password) . "' WHERE username = '" $username "'";
            
$db->sql_query($sql);
            
$this->logout();
        }
    }

    public function 
logout() {
        
$this->user->session_kill();
        
$this->user->session_begin();    
    }
    
}
?>

Hope this can be of some help to others in similar situations. Looking forward to your thoughts and/or improvements.

Wilson

 

Comments 903

CakePHP Team Comments Author Comments
 

Comment

1 Fatal error: Cannot redeclare class user in /www/htdocs/allesova/new/forum/includes/session.php on line 1971

Hello Wilson,

tnx for this component, I was waiting for it :)

Unfortunately after implementing, i came up with this error:

Fatal error: Cannot redeclare class user in /www/htdocs/allesova/new/forum/includes/session.php on line 1971

I have a UserController is that the problem?

Thank you in advantage!

Reijer
Posted Jan 19, 2009 by Reijer van Oorspronk
 

Comment

2 User model

Hello Wilson,

tnx for this component, I was waiting for it :)

Unfortunately after implementing, i came up with this error:

Fatal error: Cannot redeclare class user in /www/htdocs/allesova/new/forum/includes/session.php on line 1971

I have a UserController is that the problem?

Thank you in advantage!

Reijer

Yes, it was a near miss :)

You've got a User model, that name is same that phpBB user class. For example if change your model to Member and controller to MembersController the tutorial works fine.
Posted Jan 20, 2009 by Mihály Kuprivecz
 

Bug

3 Including the component breaks everything

I'm having trouble in getting this thing to work. :-S I'm using the latest phpBB3 (3.0.4) and the latest Cake (1.2.1.8004) under Windows environment. In my members_controller.php I have:

var $components = array('PhpBB3');

this breaks the whole controller. I did some digging, and it happens in the PhpBB3 component's startup() function, on the line where it calls $this->user->setup(). After that the browser renders a broken (without CSS etc.) version of the phpBB3 forum front page. I assume it shouldn't render anything at all? Or did I misunderstand how to use the component? I'm planning to log in to the forum in my members_controller's login() function, using $this->PhpBB3->login(...).

Maybe this is a Windows issue? Something to do with the pathnames \ vs / ? Just wondering... And yes, help would be appreciated! :)

Posted Jan 22, 2009 by Mikko Karvonen
 

Comment

4 Working

After some debugging I got it to work.

My issues lay in my custom login function being called in the beforeFilter() of my AppController (cookies related).

Since your components uses startup(), which is called AFTER the beforeFilter() of the controller it did not require the specified forum files. Thus it could not find the needed functions.

After changing startup() to initialize() it works like a charm.

Thanks a bunch.

EDIT: However, it does not seem to work quite well due to the hashed password in the cookie. I removed it when trying to login with cookie.

Another issue I am having is that users are not remembered for the forum. Am trying to find a solution and was wondering whether you are facing the same?
Posted Jan 24, 2009 by Jef
 

Question

5 Permissions Data

Would I be right in thinking this includes all phpBB3 session data?

including permissions etc
Posted Feb 21, 2009 by Mikey
 

Comment

6 compatible with Auth?

has anyone got this working with Auth?

I am trying to have users log in at /members/login in the normal Auth way. Then if they manage that I want to use this PHPBB api login method to pass along their details and authenticate them there, so if they visit the forum they will be logged in as well. (as I will have all users registered from a exsiting list, I do not require registering on php bb side)

can anyone explain a bit the way this component is set up - are the login functions meant to act as a replacement for Auth on the Cake side of things, as well as handle the login to PHP BB?
thanks!
Posted Mar 12, 2009 by Luke Barker
 

Comment

7 Has anyone got a solid working example of this?

It seems to be the most viable option for PhpBB3 integration but I can't get it working correctly... :(
It only needs a little more explanation and I'm sure we'd be up and running! Anyone?
Posted Apr 14, 2009 by Toby Bradshaw
 

Comment

8 Huge Thanks!

Huge thanks to your help with this, Wilson! I ended up creating a custom ForumUser model that works like a typical cake model (with the save() method overloaded, plus added login() and logout() methods). I have a little more work I'd like to do on it (e.g. I haven't implemented updating a user's data yet), but I'll share it here when I'm done.
Posted Jun 14, 2009 by Matt Huggins