Krystee Dryer's Single Sign On for your Members presentation from the 2012 Drupal Capital Camp in Washington DC http://www.capitalcamp.org/content/single-sign-your-members
In this session, we will be cracking open our process to develop custom SSO modules with a CRM or membership database and Drupal.
This session will introduce how to:
• Connect with the third-party service
• Insert a custom validation function in the login process
• Use tools to make the development process easier
• Save the information from the application to the Drupal user
• Handle other integration to the SSO (edit my profile, forgot password, etc).
• Manage advanced development like adding members to organic groups programmatically
You can also sneak a peak at our own custom SSO module in development that will allow you to do all of the above easily in the configuration settings.
Breaking the Kubernetes Kill Chain: Host Path Mount
Single Sign On for Your Members
1. Single Sign On for
your Members
July 27, 2012
Krystee Dryer
@krysgeek
Capital Camp
2. Scope
• Will there be one login form to authenticate both
systems (i.e. single sign on)?
• What roles will users be assigned in the CMS and AMS?
• Are there other types of groups or membership types
that have special functionality?
• Will users be able to edit their profile?
• Will users be able to sign up for or renew their
memberships online?
• Will there be online event registration?
• Will there be e-commerce functionality (shopping
cart, payment processors, etc)?
• Will the CMS have role-oriented content?
Balance Interactive Inc. www.BalanceInteractive.com
3. Integration Options
• Does the CMS have an existing module/plugin available
for the AMS integration?
• Is there a web service available?
• Are we authenticating against a remote database?
• What other options are available for single sign on?
• Is there a development installation of the AMS for
testing purposes?
Balance Interactive Inc. www.BalanceInteractive.com
4. Authentication Scenarios
• Will the user login to the CMS (which will authenticate
against the AMS and return with the necessary
information to the CMS) or will the user login to the AMS
(and then add login token to their user session in the
CMS)?
• How do we provide the reverse authentication scenario if
the login is performed in a non-standard way?
• What information is available to the CMS (from the AMS)
to use for personalization?
Balance Interactive Inc. www.BalanceInteractive.com
5. User Stories
• Once all of the decisions have been made, we develop
our user stories to cover all scenarios of user interaction
between the CMS and AMS.
• Those user stories are used in our build phase and
translated into our testing plan with test users to fit each
story.
• Once build is done for each user story, our QA team
tests each scenario and reports findings and any issues
found.
Balance Interactive Inc. www.BalanceInteractive.com
6. SSO Case Study and Example Code
• Clients has a complex membership structure that
includes both roles and committees.
• The web site should have membership based access and
content depending on their roles and committees.
• All of the info should be stored in their AMS (events,
publications, membership activity, etc.)
• They want to use the registration, edit your profile, and
forgot passwords in the AMS (not through Drupal).
• They want so login in one place and go back and forth
between the CMS and AMS.
• They want some limited personalization on the site
(Welcome, Name message).
• Balance Interactive Inc. a extensive API and web services available.
AMS had www.BalanceInteractive.com
7. Custom SSO Module – Install Functions
function netforumod_auth_new_fields() {
return array(
Since the client 'cst_name' => array(
wanted 'description' => t('The customer name'),
'type' => 'varchar',
personalization, we 'length' => 500,
created some new ),
'not null' => FALSE,
fields in the user table 'cst_id' => array(
to store this info with
'description' => t('The customer ID'),
'type' => 'varchar',
the user information. 'length' => 10,
'not null' => FALSE,
),
'cst_web_token' => array(
'description' => t('The SSO Token'),
'type' => 'varchar',
'length' => 400,
'not null' => FALSE,
),
);
}
Balance Interactive Inc. www.BalanceInteractive.com
8. Custom SSO Module – Install Functions
/**
Since our module is * Implements hook_schema_alter().
*/
modifying the function netforumod_auth_schema_alter(&$schema) {
database structure of $schema['users']['fields'] =
another module, array_merge($schema['users']['fields'], netforumod_aut
h_new_fields());
hook_schema_alter() }
is used update the
/**
default $schema and * Implements hook_install().
to take our changes */
into account. function netforumod_auth_install() {
foreach (netforumod_auth_new_fields() as $name =>
$field) {
db_add_field('users', $name, $field);
The install function is }
adding the new fields }
to the user database.
Balance Interactive Inc. www.BalanceInteractive.com
9. Custom SSO Module – Admin Functions
Use the Drupal Form $form['authentication']['netforumod_auth_ssourl'] = array(
'#type' => 'textfield',
API to create a '#title' => t('netFORUM OnDemand SSO Web Service URL'),
'#default_value' => variable_get('netforumod_auth_ssourl', NULL),
settings page used by '#description' => t('WDSL Web Service URL for the netFORUM
the admin to enter in
SSO.'),
);
the web service url(s) $form['authentication']['netforumod_auth_xweburl'] = array(
'#type' => 'textfield',
and authentication '#title' => t('netFORUM OnDemand xWeb Service URL'),
'#default_value' => variable_get('netforumod_auth_xweburl', NULL),
credentials. '#description' => t('WDSL Web Service URL for the netFORUM
xWeb.'),
);
$form['authentication']['netforumod_auth_username'] = array(
'#type' => 'textfield',
'#title' => t('Username for netFORUM'),
'#default_value' =>
variable_get('netforumod_auth_username', NULL),
'#description' => t('Password for the netFORUM web service.'),
);
$form['authentication']['netforumod_auth_pass'] = array(
'#type' => 'textfield',
'#title' => t('Password for netFORUM'),
'#default_value' => variable_get('netforumod_auth_pass', NULL),
'#description' => t('Password for the netFORUM web service.'),
Balance Interactive Inc. ); www.BalanceInteractive.com
10. Custom SSO Module – Admin Functions
Since we needed to foreach (user_roles(TRUE, NULL) as $key => $var) {
$form['roles']['netforumod_auth_role_val_' . $key] =
bring over the roles array(
form the AMS into '#type’ => 'textarea',
Drupal, we wanted a '#title' => t('netFORUM Membership Name to map
to Drupal role - @var', array('@var' => $var)),
way to map the role '#default_value' =>
information returned variable_get('netforumod_auth_role_val_' . $key, ''),
by the web service to '#description' => t('List of Avectra Membership
Types. Enter one netFORUM OnDemand Product Code
the roles we set in per line.')
Drupal. );
}
Balance Interactive Inc. www.BalanceInteractive.com
11. Custom SSO Module – Admin Functions
foreach (og_get_all_group($states =
To handle the array(OG_STATE_ACTIVE), $options =
array(TRUE, FALSE)) as $key => $var) {
committee $form['og']['netforumod_auth_group_val_' . $key] =
functionality, we used array(
'#type’ => 'textfield',
the Organic Groups '#title' => t('netFORUM Product Code to map to
module and created a Organic Group ID - @var', array('@var'=> $var)),
'#default_value' =>
group for each variable_get('netforumod_auth_group_val_' . $key, ''),
);
committee. The }
groups had their own
content and
membership base.
We needed some
variables to handle the
mapping of the AMS
committees to the
Balance Interactive Inc. www.BalanceInteractive.com
organic groups.
12. Custom SSO Module – Admin Functions
We also added a way to $form['exempt']['netforumod_auth_exempt'] = array(
'#type' => 'textarea',
exempt user(s) from the '#title' => t('Exempt users'),
SSO process for testing '#default_value' =>
purposes or content variable_get('netforumod_auth_exempt', ''),
'#description' => t('List of Drupal User IDs on this
entering temps that are site to exempt from netFORUM OnDemand
not in the AMS database. authentication. Enter one user ID per line. These users
will login using their Drupal credentials, and can have
roles in addition to "authenticated user". The user ID of
a user can be determined by visiting !link and clicking
on the "edit" link for each user. The user ID will then be
visible in the URL in the format /user/##/edit where ## is
the user ID.’),
);
Balance Interactive Inc. www.BalanceInteractive.com
13. Custom SSO Module – Admin Functions
The other functions in the file were some validation functions
of the web service urls, and some variables to indicate if we
are overriding the normal Drupal links for registering, forgot
password, and edit profile.
Balance Interactive Inc. www.BalanceInteractive.com
14. Custom SSO Module – Module Functions
/**
We used * Implements hook_menu_alter().
hook_menu_alter() to */
function netforumod_auth_menu_alter(&$items) {
override the normal // Disable password reset page if indicated in module settings.
Drupal links for: $disable_pass_link =
variable_get('netforumod_auth_sso_var_ForgotPass', 0);
• register, if ($disable_pass_link) {
unset($items['user/password']);
• forgot password }
• edit profile // Disable edit my profile page if indicated in module settings.
$disable_profile_link =
variable_get('netforumod_auth_sso_var_Profile', 0);
global $user;
if ($disable_profile_link) {
$items['user/%user_category/edit']['type'] =
(if the settings page MENU_CALLBACK;
$items['user/%user']['type'] = MENU_CALLBACK;
indicated that they }
}
should be removed or
overridden).
Balance Interactive Inc. www.BalanceInteractive.com
15. Custom SSO Module – Module Functions
• We used //**
* Implements hook_form_alter().
hook_form_alter() */
on the Drupal login function
netforumod_auth_form_alter(&$form, &$form_state, $form_id) {
form.
if ($form_id == 'user_login_block' || $form_id == 'user_login' ||
• The links on the form $form_id == 'user_login_form') {
were overridden or
removed if necessary. //Add all the logic around the Drupal links in the login form and
override if necessary
• We add our custom
validation function to // Overwrite Drupal's validation handler and substitute a
the normal Drupal custom one.
validation handler. $form['#validate'] = array('netforumod_auth_login_validate');
}
}
Balance Interactive Inc. www.BalanceInteractive.com
16. Custom SSO Module – Module Functions
We tested if user was function netforumod_auth_login_validate(&$form, &$form_state) {
// Check if user is already in database.
an exempt user. If so,
just use the normal // Grab username from form input.
Drupal login process. If $res = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' =>
$username));
not, go through the if ($res && $res->rowCount() > 0 && $obj = $res->fetchObject()) {
custom authentication }
$uid = $obj->uid;
function. // Find out if the user is in the list of exempt users.
// Make sure that user 1 has been added to this list and that user 0 is not added to the list.
if (!in_array($uid, $list)) {
$auth_rtn = netforumod_auth_authenticate($form_state['values'], $uid, $formstate,
$form);
if (!$auth_rtn) {
// If unsuccessful login, redirect to user login page.
unset($_REQUEST['destination'], $_REQUEST['edit']['destination']);
drupal_set_message(t("Sorry, unrecognized username or password"), 'error');
drupal_goto('user');
}
}
else {
Balance Interactive user_login_authenticate_validate($form, $form_state);
Inc. www.BalanceInteractive.com
}
}
17. Custom SSO Module – Module Functions
Call two different web services:
#1 – send our admin credentials into their system and
return an SSO token (if authenticated).
#2 – SSO web services that we sent our returned SSO
token and sent in the user’s credentials and returned the
authentication variable (true or false) and user object
(roles, committees, company id, etc).
We also had to send in the company id and return the
committees and roles the company owned and apply them
to the user.
Balance Interactive Inc. www.BalanceInteractive.com
19. Custom SSO Module – Module Functions
We have grabbed all of the roles from the user object returned from
the web service and now call our mapping to Drupal roles and
assign them to the $newroles array.
$memberships = array_filter(array_unique($memberships));
$roles = $memberships;
// Check if member type matches.
$drupalroles = user_roles(TRUE, NULL);
// Check if member type matches.
foreach ($drupalroles as $key => $var) {
if (variable_get('netforumod_auth_role_val_' . $key, NULL)) {
foreach ($roles as $rolekey => $rolevar) {
if (!in_array($rolevar, netforumod_auth_textarea_to_array(variable_get('netforumod_auth_role_val_' . $key, NULL)))) {
$newroles[$key] = TRUE;
}
}
}
}
Balance Interactive Inc. www.BalanceInteractive.com
$newroles[DRUPAL_AUTHENTICATED_RID] = TRUE;
20. Custom SSO Module – Module Functions
We have all of the information we need and now put it into an array
that we will send to Drupal to save a new user or update an existing
one.
$mail = $username;
//Update the user table with all relevant information
$update = array(
'mail' => $mail,
'name' => $username,
'pass' => $pass,
'status' => 1,
'cst_key' => $cst_key,
'cst_ssotoken' => $cst_ssotoken,
'cst_id' => $cst_id,
'cst_web_password' => $name,
'init' => $username,
'roles' => $newroles,
'access' => REQUEST_TIME,
Balance Interactive Inc. www.BalanceInteractive.com
);
21. Custom SSO Module – Module Functions
We have all of the information we need and now put it into an array
that we will send to Drupal to save a new user or update an existing
one.
if($uid) {
$account = user_load($uid);
watchdog('user', 'Updated user %user %uid using module %module.', array('%user' => $name, '%uid' => $uid,
'%module' => 'netforumod_auth'), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
$account = user_save($account, $update);
} else {
$account = user_save(NULL, $update);
watchdog('user', 'New external user: %user using module %module.', array('%user' => $name, '%module' =>
'netforumod_auth'), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
Balance Interactive Inc. www.BalanceInteractive.com
}
22. Custom SSO Module – Module Functions
We have all of the information we need and now put it into an array
that we will send to Drupal to save a new user or update an existing
one.
//set group memberships
$account = netforumod_auth_set_groups($memberships, $account);
watchdog('user', 'User id = %uid using module %module.', array('%uid' => $uid, '%module' =>
'netforumod_auth'), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
user_set_authmaps($account, array("authname_netforumod_auth" => $username));
user_external_login_register($username, 'netforumod_auth');
$form_state['uid'] = $account->uid;
$form['#submit'] = array();
// User authenticated and was updated in Drupal - login success.
return TRUE;
Balance Interactive Inc. www.BalanceInteractive.com
23. Final Considerations
We still need to address passing through the logged in
state to the AMS pages.
• The AMS provided a SSO token that we saved into
the database. This token was appended to the url
when linked back to the AMS pages.
• When the user logged out the token on the AMS
side was destroyed.
API’s from the AMS were used to call and return a list
current events and products and display them on the
Drupal site. All links went back to the AMS pages with the
SSO token appended (if logged in).
Balance Interactive Inc. www.BalanceInteractive.com
24. SSO Wrap Up
• Create your admin pages to enter in your connection
variables, user credentials, role mapping.
• Add an exempt user function to allow user story testing
without having to enter the users in the AMS database.
• Add some functionality to override or remove the Drupal
user links if needed.
• Use hook_form_alter() to add a custom authentication
function to the login process and call the custom
authentication function if user is not exempt.
• If user is not exempt, call your web services using the
saved variables and return the user object.
• Compare the roles returned in the user object and map
them to the Drupal roles and save/create the user.
Log the user in to Drupal.
• Balance Interactive Inc. www.BalanceInteractive.com