PhpBB3 Api Bridge

by 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
[H4]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.

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

$cache = new cache()
to

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

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


var $components = array('PhpBB3');


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

[H4]CODE
Component file name will be: app/controllers/components/php_b_b3.php

Component Class:

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

Report

More on Components

Tags

Advertising

Comments

  • Cameri posted on 03/31/10 10:25:25 AM
    Wilson,

    Right after I tried your component, I realized PhpBB3's error handling mechanism was overriding CakePHP's. This results in a ugly looking PHPBB3 error template that overrides whatever was supposed to be shown instead.

    This can be fixed quite easily, just two files need to be modified, and two lines to be added.

    In PhpBB3Component::startup(), add the following line anywhere BEFORE the call to require_once() to include Phpbb3\common.php:
    define('NO_PHPBB_ERROR_HANDLER',true);
    In PhpBB3\common.php, search for 'set_error_handler', and add this line right before the function call:
    if (!defined('NO_PHPBB_ERROR_HANDLER'))
    The file should look like this after you are done (lines 200-201):
    if (!defined('NO_PHPBB_ERROR_HANDLER'))
      set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');

    And that's it. Hope it helped.
  • mhuggins posted on 06/14/09 11:18:30 PM
    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.
  • designvoid posted on 04/14/09 06:50:25 AM
    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?
  • battez posted on 03/12/09 11:51:55 AM
    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!
  • Mr.Mikey posted on 02/21/09 06:40:13 AM
    Would I be right in thinking this includes all phpBB3 session data?

    including permissions etc
  • WalkingSoul posted on 01/24/09 01:11:57 PM
    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?
  • crankshaft posted on 01/22/09 06:34:08 AM
    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! :)

login to post a comment.