Firefox PHP

User registration inside user_session_restore hook

Posted by Phil Connolly 
User registration inside user_session_restore hook
November 16, 2010 07:27PM
I could probably dig deep and eventually figure this one out... but maybe the answer is very obvious and so here is my coding problem:

I am using the user_session_restore hook in a module as part of a SSO solution. One of the things happening in there is registering new users user. My hook code performs its business, no problems there.

But I wanted to be a good citizen and allow other modules to have their chance to hook into the phorum registration hooks (specifically, before_register and after_register). After all, even though I am performing a registration outside of the core, other modules should be able to perform their functions. So I have something like this:

Language: PHP
global $PHORUM;   // ... code to decide if we need to create a new phorum user, etc...   $phorum_user_data = my_function_to_create_new_user_data();   // Since we are about to register a new user, we should let the before_register hooks perform // their tasks and honor their error reports. if (isset($PHORUM[';hooks';][';before_register';])) { $phorum_user_data = phorum_hook(';before_register';, $phorum_user_data); } // ... continue processing, including honor errors reported by the "before_register" hook chain

and:

Language: PHP
// create the new user and get the user_id with which to create a session. $phorum_user_id = phorum_api_user_save($phorum_user_data);   if ($phorum_user_id) { // Now that we have registered a new user, we should let the after_register hooks perform // their tasks. if (isset($PHORUM[';hooks';][';after_register';])) { phorum_hook(';after_register';,$phorum_user_data); } // ... etc }

The code results in other modules hooks firing, but I am getting notices that indicate referencing undefined indexes in the $Phorum data (i.e., Undefined index: mod_username_restrictions, undefined index: LANG). So, is the user_session_restore hook too early in the process for $Phorum to fully setup as expected by the *_register hooks? Or do I have a $Phorum scoping problem? Or is there a more appropriate place to be handling the user creation in the case of what I am trying to do here?

TIA,

-phil.
Re: User registration inside user_session_restore hook
November 16, 2010 07:49PM
The user_session_restore hook is called very early in the process, since Phorum needs to know during the rest of the script execution whether or not a user is logged in. As a reference: common.php:594 has session restore in it. Loading the language file is not done until common:718. Therefore, when before and after register hooks are called from within your code, the language data (and other data) is not setup yet. In the normal registration code, it works because the hooks are called after common.php was loaded.

So, this is not a $PHORUM scoping issue, but a code execution order issue.

For the LANG warning, defining an empty $PHORUM['DATA'] = array() might work.

For the username restrictions, the issue might be solved when going to the module's settings and hitting the save button. I just found that the defaults.php file is not loaded by the module, in which case the mentioned item is undefined when no specific settings have been saved for the module in the database. I will add a load call to the module to take care of this issue (not 100% sure that it's your issue, but nice to have it fixed anyway ;-)


Maurice Makaay
Phorum Development Team
my blog linkedin profile secret sauce
Re: User registration inside user_session_restore hook
November 16, 2010 09:05PM
Based on your description, and looking more closely at common.php, I think user_session_restore will be the most appropriate place for me to handle my own SSO registration tasks, because the very next significant thing that happens in common.php is the common_post_user hook. So I have to be done with my user processing by that point. Like you say I can work around the notices by defining whatever is needed in $PHORUM['DATA'], and this solves the specific issue.

Kind of hacky... because I would prefer to not have to worry about what other modules are doing, they should be responsible for their own behaviors. But I do see why the language data is not fully loaded until line 718. It considers the user's language setting, which obviously, has to wait until a user exists in state. Hmmm... but I have to a create a user before that... and the before_register and after_register hooks assume a certain state is in place. I guess I should probably not be triggering these hooks in my current SSO context. I figured that module writers would be using before_register and after_register hooks to do custom actions resulting from registrations, and why shouldn't an SSO "registration" also take part in that? But sometimes the hook chain can't handle some "out of the loop" use cases. Even if there was a nice high level API "register_user" function that took the responsibility to call interested hooks, I would still be calling it too early in user_session_restore. What I *really* could use is an event model.... yeah right, no native events in PHP....

The username restrictions issue was resolved with a "save" like you said.

-phil.
Re: User registration inside user_session_restore hook
November 16, 2010 10:05PM
Another approach for this (at least that's how I approach it in my own website, where I use user_session_restore exactly how you use it), is to consider that the actual user registration is part of the main website. What you do inside Phorum, is merely synchronization of the user data to make it available within Phorum. As such, the process that is taking place is not registration, yet synchronization, for which you can argue that the registration module code does not apply.

I can see the requirement for running the registration hooks though. The username restriction module is not a module that I see fit for this, since the main site should take care of limiting what usernames are allowed. But modules like automatically setting up some user data or so could be possible.

The clashing point here is handling user synchronization from within user_session_restore. Although this is perfectly possible (and even recommended as the most simple solution for getting things going), it does not adhere to the flow that the Phorum code uses for registration (where the hooks are always called after loading common.php). So pulling those hooks to this part of the code flow makes things trip. But hey, you already knew that bit ;-)

One possible option could be to postpone the hook calls till after the common data has loaded. Store some reminder in the $PHORUM data when doing a registration and act on that data from the common hook to trigger the registration hooks. Of course that is only a partial solution, since you cannot really take care of handling before_registration errors that way.

An ideal solution would be to integrate the Phorum user creation from within the main application's user management code. When doing it from there, you can load common.php, run before registration hooks, register the user and run after registration hooks. Since common.php is fully loaded here, things should work. Whether or not this is possible, depends on the integration that is to be performed of course.

Since the only issue that is left for you seems to be the language data (unless you have more warnings besides this), I think that it should be okay to provide the required language data and be done with it. This action is not one that disturbs the normal flow.


Maurice Makaay
Phorum Development Team
my blog linkedin profile secret sauce
Re: User registration inside user_session_restore hook
November 16, 2010 10:40PM
I definitely see your point about the difference between registration and synchronization. I am working on two customizations right now, one that involves SSO between my main website and Phorum (synchronization), and another that enables SSO between Phorum and a third-party service (registration). The first includes my total control over the process... I am the developer for both systems and administer their databases. The second brings the third-party service experience into Phorum, and thus requires registration of user data which I do not control. This is the scenario where running the registration hooks becomes helpful, not only for me (I can plug in modules I like that do interesting things as part of the registration process), but for others when I share my module to run on systems configured every which way.

Quote
Maurice Makaay
One possible option could be to postpone the hook calls till after the common data has loaded. Store some reminder in the $PHORUM data when doing a registration and act on that data from the common hook to trigger the registration hooks. Of course that is only a partial solution, since you cannot really take care of handling before_registration errors that way.

I've decided to do this, and was actually working on it when your message came through, so it feels good to have an idea validated ;-)

I'm OK with not really properly handling before_registration errors, it is a small trade off for actually allowing registration hooks to run. And if I am really interested in allowing other module developers to influence my SSO-registration behavior, I can always add a new hook in my module code and document it as available.

Quote
Maurice Makaay
An ideal solution would be to integrate the Phorum user creation from within the main application's user management code. When doing it from there, you can load common.php, run before registration hooks, register the user and run after registration hooks. Since common.php is fully loaded here, things should work. Whether or not this is possible, depends on the integration that is to be performed of course.

For my synchronization project, this is how we did it and it works great. It was when I took that code as a template for my "registration" project that a fell in the ditch. But thanks for the discussion... I think I have viable approach for both now.

-phil.
Re: User registration inside user_session_restore hook
November 16, 2010 11:32PM
You're welcome!


Maurice Makaay
Phorum Development Team
my blog linkedin profile secret sauce
Re: User registration inside user_session_restore hook
November 16, 2010 11:51PM
Based on my conversation with Maurice, here is the basic solution which can provide for a user synchronization/registration to Phorum and from another application (which perhaps you have no control over), and still support running phorum registration hooks:

Language: PHP
<?php /* phorum module info hook: common|phorum_mod_sso_sample_common hook: common_post_user|phorum_mod_sso_sample_common_post_user hook: user_session_restore|phorum_mod_sso_sample_user_session_restore title: Sample SSO with Registration hooks desc: Sample code (non-functioning) that shows how to do SSO with registration hooks */ if(!defined(';PHORUM';)) return;   /* function phorum_mod_sso_sample_user_session_restore hook user_session_restore   */ function phorum_mod_sso_sample_user_session_restore($sessions) { // If we are already in a Phorum front-end session, no need to continue if ($sessions[PHORUM_SESSION_LONG_TERM] || $sessions[PHORUM_SESSION_SHORT_TERM]) { return $sessions; }   global $PHORUM;   // Is the user logged in to the external application? user_session_from_external_app() is just an example... $external_user = user_session_from_external_app();   // For example purposes, $external_user has a structure: // $external_user[';id';] // $external_user[';fullname';] // $external_user[';email';]   if ($external_user) {   // Use the external user email (as a username) to locate a Phorum user_id. // TODO Note: This is a weak part of the overall solution. What if the external // user changes his email address in external app? Or user ID in Phorum? // A better solution is a ID mapping table.... but for our simple purposes $phorum_user_id = phorum_api_user_search(';username';,$external_user[';email';]); $phorum_user_data = array(); if ($phorum_user_id) { // then get the Phorum user data from that $phorum_user_id $phorum_user_data = phorum_api_user_get($phorum_user_id); }   // if the Phorum user does not exist then we need to create a phorum user: if (!$phorum_user_id || empty($phorum_user_data)) { // Store the data for the new user... we will create that later, in the common hook $PHORUM[';DATA';][';mod_sso_sample';][';new_phorum_user';] = array( // The user_id must be NULL to create a new user ';user_id'; => NULL, ';real_name'; => $external_user[';fullname';], ';username'; => $external_user[';email';], ';password'; => md5($external_user[';email';]), ';email'; => $external_user[';email';], // By default, create a non-admin user. ';admin'; => 0, ';active'; => PHORUM_USER_ACTIVE, ); } elseif (!empty($phorum_user_data)) { // Case covers when the external session user is found in Phorum // Always make sure the phorum profile is in sync with external: $phorum_user_data[';real_name';] = $external_user[';fullname';]; $phorum_user_data[';email';] = $external_user[';email';];   // Hook to allow other modules to perform tasks on the existing user SSO process // prior to saving the user info, argument is the user data, and return must be // the user data, possibly changed if (isset($PHORUM[';hooks';][';mod_sso_sample_sso_user';])) { $phorum_user_data = phorum_hook(';mod_sso_sample_sso_user';, $phorum_user_data); }   $phorum_user_id = phorum_api_user_save($phorum_user_data); }   if ($phorum_user_id) { // at this point, if we have a $phorum_user_id we can create a phorum session //we have a legit user, so set their session info $sessions[PHORUM_SESSION_LONG_TERM] = $phorum_user_id; $sessions[PHORUM_SESSION_SHORT_TERM] = $phorum_user_id; } }   return $sessions; }   function phorum_mod_sso_sample_common_post_user() { global $PHORUM;   if ( $PHORUM[';DATA';][';LOGGEDIN';] && isset($PHORUM[';user';]) ) { // perform your common post user tasks } }   function phorum_mod_sso_sample_common() { global $PHORUM;   if ( isset($PHORUM[';DATA';][';mod_sso_sample';][';new_phorum_user';]) ) { $phorum_user_data = $PHORUM[';DATA';][';mod_sso_sample';][';new_phorum_user';];   // Since we are about to register a new user, we should let the before_register hooks perform // their tasks and honor their error reports. if (isset($PHORUM[';hooks';][';before_register';])) { $phorum_user_data = phorum_hook(';before_register';, $phorum_user_data);   // Some modules hooking before_register are not expecting to be called this way // So we pretty much have to disregard errors. But others might do some processing on the // registration data, so it is worth running the hook if (isset($phorum_user_data[';error';])) { unset($phorum_user_data[';error';]); } }   // create the new user and get the user_id with which to create a session. $phorum_user_id = phorum_api_user_save($phorum_user_data);   if ($phorum_user_id) { // Now that we have registered a new user, we should let the after_register hooks perform // their tasks. if (isset($PHORUM[';hooks';][';after_register';])) { phorum_hook(';after_register';,$phorum_user_data); }   // Now we need to create the session for this new user: phorum_api_user_session_restore(PHORUM_FORUM_SESSION);   // And this was called already, but there was no user logged in at the time // Now, there is. So call it again phorum_mod_sso_sample_common_post_user();   } else { // There was an error in creating the user... go back to login. Or change to whatever else makes sense phorum_redirect_by_url(phorum_get_url(PHORUM_LOGIN_URL)); } } // We are in a "new_phorum_user" state   }   ?>

Note, this is purely sample code, not expected to run by itself :) I can't even promised I accurately copied it out of my much larger module, grabbing just the essential code. But this works in my system, and will be in the next module I share.

-phil.



Edited 2 time(s). Last edit at 11/17/2010 12:02AM by Phil Connolly.
Re: User registration inside user_session_restore hook
November 17, 2010 01:49AM
One note about the weak part that you indicated: instead of using an id mapping table, you can also opt for keeping the the id for main app and Phorum the same. That's how I handle it too. Phorum does not mind when you supply a user_id other than NULL when creating a user using phorum_api_user_save(). The only requirement is that the user_id does not already exit in the Phorum database (which should always be the case when only creating users based on the main system database).

The big advantage is that you have a real unique shared key. Updating both username and email is possible with this, since they are both not necessary to find the link between the main app and Phorum.

Thanks for sharing your code!


Maurice Makaay
Phorum Development Team
my blog linkedin profile secret sauce
Re: User registration inside user_session_restore hook
November 17, 2010 06:37AM
Very cool about supplying my own ID. I didn't know that.... I didn't dig through the API function code in this case to see what it was doing, so just went with the NULL ID example I found somewhere. I can definitely use that in my integration to my main application. My other integration project doesn't use integer IDs, so I have to use an ID mapping scheme for that.

-phil.
Sorry, only registered users may post in this forum.

Click here to login