Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
322 lines (300 sloc) 11.9 KB
<?php
/**
* @file
* Hooks, callbacks for OpenID Provider SSO.
*
* @todo
* - Storing Known Relying Parties in Drupal variable does not scale.
*/
/**
* Implementation of hook_help().
*/
function openid_provider_sso_help($path) {
switch ($path) {
case 'admin/settings/openid-provider-sso':
return t('This is a list of Relying Parties that are authorized to use this Drupal site as OpenID Simple Sign-On Provider. Users signing in to these sites will be able to use this OpenID Provider with a simplified workflow.');case 'admin/settings/openid-provider-sso/add':
case 'admin/settings/openid-provider-sso/add':
return t('Add a Relying Party with OpenID Simple Sign-On support - a Drupal site with OpenID and OpenID SSO module installed and configured.');case 'admin/settings/openid-provider-sso/add':
case 'admin/settings/openid-provider-sso/edit':
return t('Modify a Relying Party with OpenID Simple Sign-On support - a Drupal site with OpenID and OpenID SSO module installed and configured.');
}
}
/**
* Implementation of hook_menu().
*/
function openid_provider_sso_menu() {
$items = array();
$items['sso/trusted-sites'] = array(
'page callback' => 'openid_provider_sso_relying_parties_opml',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['sso/logout'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('openid_provider_sso_logout_form'),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['sso/logout/redirect'] = array(
'page callback' => 'openid_provider_sso_logout_page',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['admin/settings/openid-provider-sso'] = array(
'title' => 'OpenID Provider Simple Sign-On',
'description' => 'Configure allowed OpenID Relying Parties for simplified OpenID-based sign-on.',
'page callback' => 'openid_provider_sso_rps_page',
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
'file' => 'openid_provider_sso.admin.inc',
);
$items['admin/settings/openid-provider-sso/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/settings/openid-provider-sso/add'] = array(
'title' => 'Add Relying Party',
'page callback' => 'drupal_get_form',
'page arguments' => array('openid_provider_sso_rp_edit_form'),
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
'file' => 'openid_provider_sso.admin.inc',
);
$items['admin/settings/openid-provider-sso/edit'] = array(
'title' => 'Edit Relying Party',
'page callback' => 'drupal_get_form',
'page arguments' => array('openid_provider_sso_rp_edit_form'),
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
'file' => 'openid_provider_sso.admin.inc',
);
$items['admin/settings/openid-provider-sso/remove'] = array(
'title' => 'Remove Relying Party',
'page callback' => 'drupal_get_form',
'page arguments' => array('openid_provider_sso_rp_remove_form'),
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
'file' => 'openid_provider_sso.admin.inc',
);
return $items;
}
/**
* Implementation of hook_menu_alter() - don't allow common users to see the
* OpenID sites page.
*/
function openid_provider_sso_menu_alter(&$items) {
$items['user/%user/openid_sites']['access callback'] = 'user_access';
$items['user/%user/openid_sites']['access arguments'] = array('administer openid provider');
}
/**
* Implementation of hook_form_alter().
*
* - Keep destinations spanning different sessions.
* - Simplify openid_provider_form.
*
* @todo Support non-clean URLs in redirects.
*/
function openid_provider_sso_form_alter(&$form, $form_state, $form_id) {
// Add redirects to Relying Party if there is a given realm.
// On user_pass_reset stick redirect into session variable and redirect on
// submission of user_profile_form form - this is where user_pass_reset
// redirects to. Otherwise simply add redirect to $form variable.
// When a destination is set, make sure it is kept on certain forms so that
// the user always winds up at the RP where she started out.
// The fundamental difference between the destination 'destination' and the
// destination 'realm' is that 'destination' is an internal destination of
// this site, while 'realm' is a relying party that requested authentication.
if ($form_id == 'user_pass_reset' && $rp = openid_provider_sso_relying_party($_GET['realm'])) {
$_SESSION['openid_provider_sso_redirect'] = $rp['realm'] .'sso/init';
}
elseif ($form_id == 'user_profile_form' && isset($_SESSION['openid_provider_sso_redirect'])) {
$form['#redirect'] = $_SESSION['openid_provider_sso_redirect'];
if (!empty($form_state['post'])) { // Only unset if post is set.
unset($_SESSION['openid_provider_sso_redirect']);
}
}
elseif (in_array($form_id, array('user_register', 'user_login', 'user_pass'))) {
if ($_GET['destination'] == 'openid/provider/continue') {
drupal_add_js(drupal_get_path('module', 'openid_provider_sso') .'/openid_provider_sso.js');
drupal_add_js('Drupal.settings.openid_provider_sso_destination='. drupal_to_js($_GET['destination']) .';', 'inline');
}
if ($rp = openid_provider_sso_relying_party($_GET['realm'])) {
$form['#redirect'] = $rp['realm'] .'sso/init';
}
}
// If RP is known take decisions that simplify the user's choices.
// Otherwise deny access. This only denies new RPs access. If there has been
// an RP in the past that has been set to auto_release=TRUE, the
// openid_provider_form won't be called at all and this access check won't
// be effective.
elseif ($form_id == 'openid_provider_form') {
if ($rp = openid_provider_sso_relying_party($form_state['storage']['realm'])) {
drupal_set_title('');
global $user;
unset($form['submit_always']);
$form['submit_once']['#value'] = t('Log in');
$form['intro']['#value'] = '<div class="sso-message">'. t('Log in to <strong>@relying_party</strong> as @user?', array('@relying_party' => $rp['name'], '@user' => $user->name)) .'</div>';
$form['other_user']['#value'] = l(t('Not @user?', array('@user' => $user->name)), 'sso/logout/redirect', array('query' => 'realm='. urlencode($rp['realm'])));
}
else {
drupal_access_denied();
exit();
}
}
}
/**
* Implementation of hook_mail_alter().
*
* On user / login forms replace login uri and login url with urls that contain
* the requesting RP's realm as destination.
*
* @see openid_provider_sso_form_alter().
*/
function openid_provider_sso_mail_alter(&$message) {
if (in_array($message['id'], array('user_register_no_approval_required', 'user_register_pending_approval', 'user_password_reset'))) {
if ($rp = openid_provider_sso_relying_party($_SESSION['openid_provider']['request']['openid.realm'])) {
module_load_include('inc', 'openid');
$variables = user_mail_tokens($message['params']['account'], $message['language']);
$variables['!login_uri'] = url('user', array('query' => 'realm='. urlencode($rp['realm']), 'absolute' => TRUE, 'language' => $language));
$variables['!login_url'] = user_pass_reset_url($message['params']['account']) .'?realm='. urlencode($rp['realm']);
$message['body'][0] = _user_mail_text(substr($message['id'], 5) .'_body', $message['language'], $variables);
}
}
}
/**
* Logout page callback. Logs out and initiates new authentication request if
* a RP realm is present in $_GET.
*/
function openid_provider_sso_logout_page() {
openid_provider_sso_logout();
if ($rp = openid_provider_sso_relying_party($_GET['realm'])) {
drupal_goto($rp['realm'] .'sso/init');
}
drupal_goto();
}
/**
* Logout form. This form is being visited by an RP after logging out the user.
* The user is presented with an option to log out on the OP (this site) as
* well or stay logged in. In both cases the final destination is the RP where
* the user departed.
*/
function openid_provider_sso_logout_form() {
if ($rp = openid_provider_sso_relying_party($_GET['realm'])) {
if (user_is_logged_in()) {
$form = array();
$form['#rp'] = $rp;
$form['message']['#value'] = '<div class="sso-message">'. t('You logged out of <strong>@relying_party</strong>, would you also like to log out of <strong>@provider</strong>?', array('@relying_party' => $rp['name'], '@provider' => variable_get('site_name', 'Drupal'))) .'</div>';
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Log out'),
'#submit' => array('openid_provider_sso_logout_submit'),
);
$form['cancel'] = array(
'#type' => 'submit',
'#value' => t('Keep me logged in'),
'#submit' => array('openid_provider_sso_logout_cancel'),
);
return $form;
}
drupal_goto($rp['realm']);
}
return array();
}
/**
* Submit handler for openid_provider_sso_logout_form().
*/
function openid_provider_sso_logout_submit($form, &$form_state) {
openid_provider_sso_logout();
// Redirect user to the RP where she came from.
drupal_goto($form['#rp']['realm']);
}
/**
* Submit handler for openid_provider_sso_logout_form().
*/
function openid_provider_sso_logout_cancel($form, &$form_state) {
drupal_goto($form['#rp']['realm']);
}
/**
* Log out - taken from user_logout(). Difference: no redirect at the end of the
* function.
*
* @see user_logout()
*/
function openid_provider_sso_logout() {
global $user;
watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
// Destroy the current session:
session_destroy();
// Only variables can be passed by reference workaround.
$null = NULL;
user_module_invoke('logout', $null, $user);
// Load the anonymous user
$user = drupal_anonymous_user();
}
/**
* Returns a relying party by its realm. Returns FALSE if no relying party of
* given realm is known. $realm may be URL encoded.
*/
function openid_provider_sso_relying_party($realm) {
$realm = trim(urldecode($realm), '/');
foreach (openid_provider_sso_relying_parties() as $rp) {
if (trim($rp['realm'], '/') == $realm) {
return $rp;
}
}
return FALSE;
}
/**
* Returns all known relying parties.
*/
function openid_provider_sso_relying_parties() {
$result = db_query('SELECT realm, name FROM {openid_provider_sso_rps}');
$rps = array();
while ($rp = db_fetch_array($result)) {
$rps[$rp['realm']] = $rp;
}
return $rps;
}
/**
* Add a relying party.
*/
function openid_provider_sso_rp_add($realm, $name) {
$rp = array(
'realm' => trim($realm, '/') .'/',
'name' => $name,
);
if (drupal_write_record('openid_provider_sso_rps', $rp, 'realm')) {
drupal_write_record('openid_provider_sso_rps', $rp);
}
}
/**
* Remove a relying party.
*/
function openid_provider_sso_rp_remove($realm) {
db_query('DELETE FROM {openid_provider_sso_rps} WHERE realm = "%s"', $realm);
}
/**
* Page callback for rendering a list of trusted sites.
*/
function openid_provider_sso_relying_parties_opml() {
// Generate an OPML of trusted sites.
$output = '<?xml version="1.0" encoding="utf-8"?>'."\n";
$output .= '<opml version="2.0">'."\n";
$output .= '<head>'."\n";
$output .= ' <title>'. t('Trusted relying parties for !site', array('!site' => variable_get('site_name', 'Drupal'))) .'</title>'."\n";
$output .= ' <dateCreated>'. format_date(time(), 'custom', 'r', 0) .'</dateCreated>'."\n";
$output .= '</head>'."\n";
$output .= '<body>'."\n";
foreach (openid_provider_sso_relying_parties() as $rp) {
$output .= ' <outline text="'. check_plain($rp['name']) .'" htmlUrl="'. check_url($rp['realm']) .'"/>'."\n";
}
$output .= '</body>'."\n";
$output .= '</opml>';
drupal_set_header('Content-Type=text/x-opml');
print $output;
}