This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
montpellier/drupal/simplesamlphp_auth/simplesamlphp_auth.module

1067 lines
40 KiB
Plaintext

<?php
/**
* @file
* simpleSAMLphp authentication module for Drupal.
*
* This authentication module is based on the shibboleth authentication module,
* with changes to adopt to use simpleSAMLphp.
*
* ISSUES and TODOs:
* ISSUE: User is always dropped on user page after login, instead of where
* they were when they clicked "Federated Log In". Because of this, deep
* linking to access controlled content does not work. Usability would
* be considerably increased if this were resolved.
* FYI: Drupal now requires knowledge of the local user password in order to
* change e-mail address, etc. This could be an issue for users of
* accounts that are autoprovisioned by this module, though Drupal does
* give users the ability to reset their password to something they know
* via the Request new password feature.
* KLUDGE: Drupal does not kill the session on logout, even with
* drupal_session_destroy_uid(), so I had to use session_destroy().
* @todo Rework the default login limitation logic to use a drupal permission
* rather than a list of UIDs.
* @todo When denying access because the administrator has chosen not to allow
* the module to register/create accounts, the user is told to contact
* the administrator; the message should provide the contact information.
* ISSUE: Until Drupal issue #754560 is resolved users will not see logout
* notices.
*/
/**
* Implements hook_menu().
*/
function simplesamlphp_auth_menu() {
$items = array();
$items['admin/config/people/simplesamlphp_auth'] = array(
'title' => 'SimpleSAMLphp Auth Settings',
'description' => 'Control the various settings of the simpleSAMLphp authentication module',
'page callback' => 'drupal_get_form',
'page arguments' => array('simplesamlphp_auth_settings'),
'access arguments' => array('administer simpleSAMLphp authentication'),
'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM,
);
$items['saml_login'] = array(
'title' => 'Logon to the site',
'description' => 'Provides a site login page',
'page callback' => 'simplesamlphp_auth_loginpage',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_admin_paths().
*/
function simplesamlphp_auth_admin_paths() {
return array('admin/config/people/simplesamlphp_auth' => TRUE);
}
/**
* Implements hook_help().
*/
function simplesamlphp_auth_help($path, $arg) {
switch ($path) {
case 'admin/config/people/simplesamlphp_auth':
$output = t('<p>This module integrates Drupal with a SimpleSAMLphp Service Point (SP), effectively federating Drupal.</p>');
$output .= t('<p></p>');
return $output;
}
}
/**
* Implements hook_permission().
*/
function simplesamlphp_auth_permission() {
return array(
'administer simpleSAMLphp authentication' => array(
'title' => t('Administer simpleSAMLphp authentication'),
'description' => t('Warning: Give to trusted roles only; this permission has security implications.'),
),
);
}
/**
* Represents the Drupal page (saml_login), which triggers user authentication against the SimpleSAMLphp service provider.
*/
function simplesamlphp_auth_loginpage() {
global $user;
global $base_url;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$fail = NULL;
$output = NULL;
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without initializing.
drupal_set_message(t('We\'re sorry this feature is not yet enabled.'));
return '';
}
// Do some sanity checking before attempting anything.
$config = SimpleSAML_Configuration::getInstance();
$configStoreType = $config->getValue('store.type');
// Make sure phpsession is NOT being used.
if ($configStoreType == 'phpsession') {
watchdog('simplesamlphp_auth', 'A user attempted to login using simplesamlphp but the store.type is phpsession, use memcache or sql for simplesamlphp session storage. See: simplesamlphp/config/config.php.', NULL, WATCHDOG_WARNING);
$fail = TRUE;
}
// Make sure there is an instance of SimpleSAML_Auth_Simple.
if (!$_simplesamlphp_auth_as) {
watchdog('simplesamlphp_auth', 'A user attempted to login using this module but there was a problem.', NULL, WATCHDOG_WARNING);
$fail = TRUE;
}
// There was a problem, we can't go on, but we don't want to tell the user any specifics either.
if ($fail) {
drupal_set_message(t('We\'re sorry. There was a problem. The issue has been logged for the administrator.'));
drupal_goto(base_path());
}
$returnto = NULL;
// Support for deep linking.
// See if a URL has been explicitly provided in ReturnTo. If so, use it (as long as it points to this site).
if ((isset($_REQUEST['ReturnTo']) && $_REQUEST['ReturnTo']) &&
(valid_url($_REQUEST['ReturnTo']) && stristr($_REQUEST['ReturnTo'], $base_url))) {
$returnto = $_REQUEST['ReturnTo'];
// If not, see if a REFERER URL is available. If so, use it (as long as it points to this site).
}
elseif ((isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']) &&
(valid_url($_SERVER['HTTP_REFERER']) && stristr($_SERVER['HTTP_REFERER'], $base_url))) {
$returnto = $_SERVER['HTTP_REFERER'];
}
// If the user is anonymous, set the cookie (if we can) and require authentication.
if ($user->uid == 0) {
if ($returnto) {
// Set the cookie so we can deliver the user to the place they started
setrawcookie('simplesamlphp_auth_returnto', $returnto, time()+60*60);
}
// Require the user to be authenticated.
//$_simplesamlphp_auth_as->requireAuth();
return drupal_goto("https://${_SERVER['SERVER_NAME']}/simplesamlphp/module.php/core/as_login.php?ReturnTo=http://${_SERVER['SERVER_NAME']}/saml_login&AuthId=default-sp");
// If the user is authenticated, send them along.
}
else {
$gotourl = NULL;
// Check to see if we've set a cookie. If there is one, give it priority.
if (isset($_COOKIE['simplesamlphp_auth_returnto']) && $_COOKIE['simplesamlphp_auth_returnto']) {
// use the cookie for the ReturnTo
$gotourl = $_COOKIE['simplesamlphp_auth_returnto'];
// unset the cookie
setrawcookie('simplesamlphp_auth_returnto', '');
}
elseif ($returnto) {
$gotourl = $returnto;
}
}
return drupal_goto("http://${_SERVER['SERVER_NAME']}/");
}
/**
* Implements hook_init().
*/
function simplesamlphp_auth_init() {
global $user;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
global $_simplesamlphp_auth_saml_config;
global $_simplesamlphp_auth_saml_version;
if (!_simplesamlphp_auth_isEnabled(TRUE)) {
// Exit without initializing.
return;
}
// Get the simplesamlphp session.
$basedir = variable_get('simplesamlphp_auth_installdir', 'sites/all/modules/simplesamlphp_auth/simplesamlphp');
require_once($basedir . '/lib/_autoload.php');
$_simplesamlphp_auth_saml_config = SimpleSAML_Configuration::getInstance();
$_simplesamlphp_auth_saml_version = $_simplesamlphp_auth_saml_config->getVersion();
// Load simpleSAMLphp, configuration and metadata.
$_simplesamlphp_auth_as = new SimpleSAML_Auth_Simple(variable_get('simplesamlphp_auth_authsource', 'default-sp'));
$_simplesamlphp_auth_saml_attributes = $_simplesamlphp_auth_as->getAttributes();
if ($user->uid == 0) {
// User is not logged in to Drupal.
if ($_simplesamlphp_auth_as->isAuthenticated()) {
// User is logged in - SimpleSAMLphp (but not Drupal).
$authname = _simplesamlphp_auth_get_authname();
try {
$name = _simplesamlphp_auth_get_default_name();
_simplesaml_auth_debug(t('Registering user [%acctname]', array('%acctname' => $name)));
}
catch (Exception $e) {
drupal_set_message(t("Impossible de fédérer votre compte : nom d'utilisateur non envoyé."), "error");
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
return;
}
_simplesaml_auth_debug(t('Authname is [%authname] userid is [%uid]', array('%authname' => $authname, '%uid' => $user->uid)));
if (!empty($authname)) {
// User is logged in with SAML authentication and we got the unique identifier.
// Try to log into Drupal.
_simplesaml_auth_debug(t('Load user [%authname]', array('%authname' => $authname)));
// Retrieve user mapping and attempt to log the user in.
// @todo : add authmapper here
$ext_user = user_external_load($authname);
if (!$ext_user) {
// First we check the admin settings for simpleSAMLphp and find out if we are allowed to register users.
if (variable_get('simplesamlphp_auth_registerusers', TRUE)) {
// We are allowed to register new users.
_simplesaml_auth_debug(t('Register [%authname]', array('%authname' => $authname)));
user_external_login_register($authname, 'simplesamlphp_auth');
if ($user) {
// Populate roles based on configuration setting.
$roles = _simplesamlphp_auth_rolepopulation(variable_get('simplesamlphp_auth_rolepopulation', ''));
$userinfo = array('roles' => $roles);
$user = user_save($user, $userinfo); // @todo - Fjernet rolle-delen her da den gav en bra feilmelding når roller ikke finnes ;)
}
}
else {
// We are not allowed to register new users on the site through simpleSAML.
// We let the user know about this and redirect to the user/login page.
$msg = t("We are sorry. While you have successfully authenticated, you are not yet entitled to access this site. Please ask the site administrator to provision access for you.");
drupal_set_message(check_plain($msg));
$_simplesamlphp_auth_as->logout(base_path());
}
}
else {
// If successfully logged into Drupal.
// See if we're supposed to re-evaluate role assignments.
if (variable_get('simplesamlphp_auth_roleevaleverytime', 0)) {
// If the user is already registered...
// Update the roles.
// Populate roles based on configuration setting.
_simplesaml_auth_debug(t('User already registered [%authname] updating roles.', array('%authname' => $authname)));
$roles = _simplesamlphp_auth_rolepopulation(variable_get('simplesamlphp_auth_rolepopulation', ''));
$userinfo = array('roles' => $roles);
// Save the updated roles and populate the user object.
$user = user_save($ext_user, $userinfo);
}
else {
// No need to evaluate roles, populate the user object.
$user = $ext_user;
}
if (module_exists('rules')) {
rules_invoke_event('simplesamlphp_auth_rules_event_login', $user);
}
}
// Finalizing the login, calls hook_user op login.
$edit = array();
user_login_finalize($edit);
} // End if !empty authname.
} // End if isset saml_session.
}
else {
// The user is already logged into Drupal.
// If we forbid users from logging in using local accounts.
if (FALSE == variable_get('simplesamlphp_auth_allowdefaultlogin', FALSE)) {
// If the user has NOT been authenticated via simpleSAML...
if (!$_simplesamlphp_auth_as->isAuthenticated()) {
// :FYI: Until Drupal issue #754560 is corrected this message will never be seen by the user.
drupal_set_message(t("We are sorry, users are not permitted to log in using local accounts."));
// Destroy the user's session (log them out).
_simplesamlphp_auth_destroy_drupal_session();
}
}
else {
// If we are allowing users to log in with local accounts.
// If the user has NOT been authenticated via simpleSAML.
if (!$_simplesamlphp_auth_as->isAuthenticated()) {
// See if we limit this privilege to specified users
$strAllwDefLogUsers = variable_get('simplesamlphp_auth_allowdefaultloginusers', '');
$arrAllwDefLogUsers = array();
// See if we limit this privilege to specified roles.
$arrAllwDefLogRoles = variable_get('simplesamlphp_auth_allowdefaultloginroles', FALSE);
// If user IDs or roles are specified, we let them in, but everyone else gets logged out.
if (drupal_strlen($strAllwDefLogUsers) || $arrAllwDefLogRoles) {
// Convert the string into an array.
// @todo Perform a test to make sure that only numbers, spaces, or commas are in the string.
$arrAllwDefLogUsers = explode(',', $strAllwDefLogUsers);
// If we still have something to work with.
if (0 < count($arrAllwDefLogUsers) || 0 < count($arrAllwDefLogRoles)) {
/* Log the user out of Drupal if:
1) the current user's uid is NOT in the list of allowed uids...
2) or their role does not match and allowed mixed mode role. */
$matchRoles = array_intersect(array_keys($user->roles), $arrAllwDefLogRoles);
if (!in_array($user->uid, $arrAllwDefLogUsers) && count($matchRoles) == 0) {
// User is logged into Drupal, but may not be logged into simpleSAML.
// If this is the case we're supposed to log the user out of Drupal.
// :FYI: Until Drupal issue #754560 is corrected this message will never be seen by the user.
drupal_set_message(t("We are sorry, you are not permitted to log in using a local account."));
// The least we can do is write something to the watchdog so someone will know what's happening.
watchdog('simplesamlphp_auth', 'User %name not authorized to log in using local account.', array('%name' => $user->name));
_simplesamlphp_auth_destroy_drupal_session();
}
}
} // Test for specified users.
} // End if $_simplesamlphp_auth_as->isAuthenticated().
} // End test to see if we allow default logins.
} // End if user->uid.
simplesamlphp_sync_attributes();
}
/* Implement hook_auth_user_insert() */
function simplesamlphp_auth_user_insert(&$edit, $account, $category = NUL) {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without initializing.
return;
}
if ($category = 'account') {
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$name = '';
try {
$name = _simplesamlphp_auth_get_default_name();
_simplesaml_auth_debug(t('Registering user [%acctname]', array('%acctname' => $name)));
}
catch (Exception $e) {
drupal_set_message(t("Nom d'utilisateur non disponible."), "error");
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
}
if (!$name)
$name='inconnu';
$origin_name = $name;
$i = 0;
while (db_select('users', 'u')->fields('u')->condition('name', $name, '=')->execute()->fetchAssoc()) {
$name = $origin_name . "$i";
$i++;
}
db_update('users')
->fields(array('name' => $name))
->condition('uid', $account->uid)
->execute();
}
if (module_exists('rules')) {
rules_invoke_event('simplesamlphp_auth_rules_event_register', $account);
}
}
}
/**
* Sync attributes
*/
function simplesamlphp_sync_attributes() {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
global $user;
if (!_simplesamlphp_auth_isEnabled() or !$user) {
// Exit without initializing.
return;
}
// If user registration has a valid session...
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$user = user_load($user->uid);
// Get name from default attributes.
_simplesaml_auth_debug(t('Updating username [%acctname]', array('%acctname' => $user->name)));
// Get mail from default attribute.
try {
$mail_address = _simplesamlphp_auth_get_attr('simplesamlphp_auth_mailattr', 'email');
$first_name = _simplesamlphp_auth_get_attr('simplesamlphp_auth_first_name', 'gn');
}
catch (Exception $e) {
drupal_set_message(t('Prénom ou email non disponnible.'), "error");
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
}
$data = array('nameid' => _simplesamlphp_auth_get_authname());
$fields = array('data' => $data);
if (!empty($mail_address))
$fields["mail"] = $mail_address;
if (!empty($first_name))
{
$fields['field_prenom'] = array(
'und' => array(
0 => array('value' => $first_name),
),
);
}
$prenom = field_get_items('user', $user, 'field_prenom');
user_save($user, $fields);
//_simplesaml_auth_debug(t('Updating mail [%mailaddr]', array('%mailaddr' => $mail_address)));
}
}
/**
* Implements hook_user_logout().
*/
function simplesamlphp_auth_user_logout($account) {
global $user;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
global $base_url;
if (!empty($_simplesamlphp_auth_saml_attributes)) {
$config = SimpleSAML_Configuration::getInstance();
// :KLUDGE: for some reason Drupal is not killing the session, even if I were to call drupal_session_destroy_uid() here.
session_destroy();
$gotourl = base_path();
if (variable_get('simplesamlphp_auth_logoutgotourl', '')) {
$gotourl = variable_get('simplesamlphp_auth_logoutgotourl', '');
}
$_simplesamlphp_auth_as->logout("http://${_SERVER['SERVER_NAME']}/");
}
}
/**
* Implements hook_user_delete().
*/
/*function simplesamlphp_auth_user_delete($account) {
db_delete('authmap')
->condition('uid', $account->uid)
->condition('name', $account->name)
->execute();
}*/
/**
* Implements settings for the module.
*/
function simplesamlphp_auth_settings() {
global $_simplesamlphp_auth_saml_version, $base_url;
if (!empty($_simplesamlphp_auth_saml_version )) {
$ver = explode('.', $_simplesamlphp_auth_saml_version);
if ( !($ver[0] >= 1 && $ver[1] >= 5) ) {
drupal_set_message(t("Please upgrade SimpleSAMLphp. You are using %ssp_version", array('%ssp_version' => $_simplesamlphp_auth_saml_version)), 'warning');
}
}
$roles = user_roles(TRUE);
$form['simplesamlphp_auth_grp_setup'] = array(
'#type' => 'fieldset',
'#title' => t('Basic Setup'),
'#collapsible' => FALSE,
);
$form['simplesamlphp_auth_grp_setup']['simplesamlphp_auth_activate'] = array(
'#type' => 'checkbox',
'#title' => t('Activate authentication via SimpleSAMLphp'),
'#default_value' => variable_get('simplesamlphp_auth_activate', FALSE),
'#description' => t('Checking this box before configuring the module could lock you out of Drupal.'),
);
$form['simplesamlphp_auth_grp_setup']['simplesamlphp_auth_installdir'] = array(
'#type' => 'textfield',
'#title' => t('Installation directory (default: sites/all/modules/simplesamlphp_auth/simplesamlphp)'),
'#default_value' => variable_get('simplesamlphp_auth_installdir', 'sites/all/modules/simplesamlphp_auth/simplesamlphp'),
'#description' => t('The base directory of simpleSAMLphp. Absolute path with no trailing slash.'),
);
$form['simplesamlphp_auth_grp_setup']['simplesamlphp_auth_authsource'] = array(
'#type' => 'textfield',
'#title' => t('Autenticaton source for this SP (default: default-sp)'),
'#default_value' => variable_get('simplesamlphp_auth_authsource', 'default-sp'),
'#description' => t('The name of the source to use from authsources.php'),
);
$form['simplesamlphp_auth_grp_setup']['simplesamlphp_auth_forcehttps'] = array(
'#type' => 'checkbox',
'#title' => t('Force https for login links'),
'#default_value' => variable_get('simplesamlphp_auth_forcehttps', TRUE),
'#description' => t('Should be enabled on production sites.'),
);
$form['simplesamlphp_auth_grp_user'] = array(
'#type' => 'fieldset',
'#title' => t('User Info and Syncing'),
'#collapsible' => FALSE,
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_user_name'] = array(
'#type' => 'textfield',
'#title' => t('Which attribute from simpleSAMLphp should be used as user\'s name'),
'#default_value' => variable_get('simplesamlphp_auth_user_name', 'uid'),
'#description' => t('Example: <i>eduPersonPrincipalName</i> or <i>displayName</i><br />If the attribute is multivalued, the first value will be used.'),
'#required' => TRUE,
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_unique_id'] = array(
'#type' => 'textfield',
'#title' => t('Which attribute from simpleSAMLphp should be used as unique identifier for the user'),
'#default_value' => variable_get('simplesamlphp_auth_unique_id', 'NameID'),
'#description' => t('Example: <i>eduPersonPrincipalName</i> or <i>eduPersonTargetedID</i><br />If the attribute is multivalued, the first value will be used.'),
'#required' => TRUE,
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_mailattr'] = array(
'#type' => 'textfield',
'#title' => t('Which attribute from simpleSAMLphp should be used as user mail address'),
'#default_value' => variable_get('simplesamlphp_auth_mailattr', 'email'),
'#description' => t('Example: <i>email</i><br />If the user attribute is multivalued, the first value will be used.'),
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_first_name'] = array(
'#type' => 'textfield',
'#title' => t('Which attribute from simpleSAMLphp should be used as first name'),
'#default_value' => variable_get('simplesamlphp_auth_first_name', 'gn'),
'#description' => t('Example: <i>gn</i><br />If the user attribute is multivalued, the first value will be used.'),
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_rolepopulation'] = array(
'#type' => 'textarea',
'#title' => t('Automatic role population from simpleSAMLphp attributes'),
'#default_value' => variable_get('simplesamlphp_auth_rolepopulation', ''),
'#description' => t('A pipe separated list of rules.<br />Example: <i>roleid1:condition1|roleid2:contition2...</i> <br />For instance: <i>1:eduPersonPrincipalName,@=,uninett.no;affiliation,=,employee|2:mail,=,andreas@uninett.no</i>'),
);
$form['simplesamlphp_auth_grp_user']['simplesamlphp_auth_roleevaleverytime'] = array(
'#type' => 'checkbox',
'#title' => t('Reevaluate roles every time the user logs in.'),
'#default_value' => variable_get('simplesamlphp_auth_roleevaleverytime', 0),
'#description' => t('NOTE: This means users could loose any roles that have been assigned manually in Drupal.'),
);
$form['simplesamlphp_auth_grp_reg'] = array(
'#type' => 'fieldset',
'#title' => t('User Provisioning'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['simplesamlphp_auth_grp_reg']['simplesamlphp_auth_registerusers'] = array(
'#type' => 'checkbox',
'#title' => t('Register users (i.e., auto-provisioning)'),
'#default_value' => variable_get('simplesamlphp_auth_registerusers', TRUE),
'#description' => t('Determines wether or not the module should automatically create/register new Drupal accounts for users that authenticate using SimpleSAMLphp. Unless you\'ve done some custom work to provision Drupal accounts with the necessary authmap entries you will want this checked.<br /><br />NOTE: If unchecked each user must already have been provisioned a Drupal account with an appropriate entry in the authmap table before logging in. Otherwise they will receive a notice and be denied access. Be aware that simply creating a Drupal account will not create the necessary entry in the authmap table.'),
);
$form['simplesamlphp_auth_grp_auth'] = array(
'#type' => 'fieldset',
'#title' => t('Drupal Authentication'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['simplesamlphp_auth_grp_auth']['simplesamlphp_auth_allowsetdrupalpwd'] = array(
'#type' => 'checkbox',
'#title' => t('Allow SAML users to set Drupal passwords.'),
'#default_value' => variable_get('simplesamlphp_auth_allowsetdrupalpwd', FALSE),
'#description' => t('Check this box if you want to let people set passwords for their local Drupal accounts. This will allow users to log in using either SAML or a local Drupal account. Disabling this removes the password change fields from the user profile form.<br/>NOTE: In order for them to login using their local Drupal password you must allow local logins with the settings below.'),
);
$form['simplesamlphp_auth_grp_auth']['simplesamlphp_auth_allowdefaultlogin'] = array(
'#type' => 'checkbox',
'#title' => t('Allow authentication with local Drupal accounts'),
'#default_value' => variable_get('simplesamlphp_auth_allowdefaultlogin', FALSE),
'#description' => t('Check this box if you want to let people log in with local Drupal accounts (without using simpleSAMLphp). If you want to restrict this privilege to certain users you can enter the Drupal user IDs in the field below.'),
);
$form['simplesamlphp_auth_grp_auth']['simplesamlphp_auth_allowdefaultloginroles'] = array(
'#type' => 'select',
'#size' => 3,
'#options' => $roles,
'#multiple' => TRUE,
'#title' => t('Which ROLES should be allowed to login with local accounts?'),
'#default_value' => variable_get('simplesamlphp_auth_allowdefaultloginroles', ''),
'#description' => t('Roles that should be allowed to login without simpleSAMLphp. Examples are dev/admin roles or guest roles.'),
);
$form['simplesamlphp_auth_grp_auth']['simplesamlphp_auth_allowdefaultloginusers'] = array(
'#type' => 'textfield',
'#title' => t('Which users should be allowed to login with local accounts?'),
'#default_value' => variable_get('simplesamlphp_auth_allowdefaultloginusers', ''),
'#description' => t('Example: <i>1,2,3</i><br />A comma-separated list of user IDs that should be allowed to login without simpleSAMLphp. If left blank, all local accounts can login.'),
);
$form['simplesamlphp_auth_grp_auth']['simplesamlphp_auth_logoutgotourl'] = array(
'#type' => 'textfield',
'#title' => t('Optionally, specify a URL for users to go to after logging out'),
'#default_value' => variable_get('simplesamlphp_auth_logoutgotourl', ''),
'#description' => t('Example: ' . $base_url),
);
return system_settings_form($form);
}
/**
* Implements hook_form_alter().
*/
function simplesamlphp_auth_form_alter(&$form, $form_state, $form_id) {
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
if ($form_id == 'user_login_block') {
$link = l('Connexion via mon compte citoyen', 'saml_login');
//$links = $form['links']['#markup'];
//$links = str_replace('</ul>', '<li class="saml">' . $link . '</li></ul>', $links);
$button = '<div class="item-list"><ul><li class="first last" style="padding-top: 10px;"><input type="button" class="form-submit" value="Se connecter / S\'inscrire" onClick="document.location.href=\'https://www.montpellier3m.fr/saml_login\'"></li></ul></div>';
$form['links']['#markup'] = $button;
unset($form['pass']);
unset($form['name']);
unset($form['submit']);
unset($form['actions']);
}
if ($form_id == 'user_account_form') {
$link = l('Federated Log In', 'saml_login');
$links = $form['links']['#markup'];
$links = str_replace('</ul>', '<li class="saml">' . $link . '</li></ul>', $links);
$form['links']['#markup'] = $links;
}
// If the user has a simplesamlphp_auth authmap record, then don't require them to know their Drupal password.
// This will allow them to change their e-mail address, and set a Drupal password if they want to (and are allowed).
if ((isset($form['#user']->init) && $form['#user']->init) && (_simplesaml_auth_user_has_authmap($form['#user']->init) && $form_id == 'user_profile_form')) {
unset($form['account']['current_pass']);
unset($form['account']['current_pass_required_values']);
$form['#validate'] = array_diff($form['#validate'], array('user_validate_current_pass'));
// If the user is a simplesamlphp_auth user and is NOT allowed to set their Drupal password, remove the fields from the form.
if (!variable_get('simplesamlphp_auth_allowsetdrupalpwd')) {
unset($form['account']['pass']);
}
}
}
/**
* Implements hook_block_view().
*/
function simplesamlphp_auth_block_view($delta = '') {
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
switch ($delta) {
case 0:
$block = array('subject' => t('simpleSAMLphp login'),
'content' => _simplesamlphp_auth_generate_block_text());
break;
}
return $block;
}
/**
* Implements hook_block_info().
*/
function simplesamlphp_auth_block_info() {
$block = array(
array(
'info' => t('simpleSAMLphp authentication'),
'cache' => DRUPAL_NO_CACHE,
)
);
return $block;
}
/****************************************************************************
* Private functions ********************************************************
****************************************************************************/
/**
* Checks to see if authentication via SimpleSAMLphp should be activated
*
* @param bShowInactiveMsg
* Whether to display the "module not activated" message
*
* @return
* TRUE/FALSE
*/
function _simplesamlphp_auth_isEnabled($bShowInactiveMsg=FALSE) {
GLOBAL $user;
$failure = NULL;
$isActivated = variable_get('simplesamlphp_auth_activate');
$basedir = variable_get('simplesamlphp_auth_installdir', 'sites/all/modules/simplesamlphp_auth/simplesamlphp');
if (!$isActivated) {
$adminPath = array_keys(simplesamlphp_auth_admin_paths());
$failure = t('SimpleSAMLphp authentication is NOT yet activated. It can be activated on the ' . l('configuration page', $adminPath[0]) . '.');
watchdog('simplesamlphp_auth', $failure, NULL, WATCHDOG_WARNING);
}
else {
// Make sure we know where SimpleSAMLphp is.
if (!file_exists($basedir)) {
$failure = t('SimpleSAMLphp could not be found at %basedir . The simplesamlphp_auth module cannot function until the path to the local SimpleSAMLphp instance is configured.', array('%basedir' => $basedir));
watchdog('simplesamlphp_auth', $failure, NULL, WATCHDOG_WARNING);
}
}
// If there were no failures, then it should be activated
if (!$failure) {
return TRUE;
}
else {
// communicate but don't be too annoying
if ( $bShowInactiveMsg && (1 == $user->uid || user_access('access administration pages')) && ( preg_match('/admin\/people/', request_uri()) || preg_match('/admin\/modules/', request_uri()) || preg_match('/admin\/config/', request_uri()) ) ) {
drupal_set_message($failure);
}
}
return FALSE;
}
/**
* Gets the authname attribute from the SAML assertion.
*
* @return
* The authname attribute.
*/
function _simplesamlphp_auth_get_authname() {
global $_simplesamlphp_auth_saml_attributes;
$authname = '';
error_log(print_r($_simplesamlphp_auth_saml_attributes, TRUE));
// Check if valid local session exists..
if (isset($_simplesamlphp_auth_saml_attributes)) {
_simplesaml_auth_debug(t('_simplesamlphp_auth_get_authname: Valid local session exist'));
if (isset($_simplesamlphp_auth_saml_attributes[variable_get('simplesamlphp_auth_unique_id', 'NameID')])) {
$authname = $_simplesamlphp_auth_saml_attributes[variable_get('simplesamlphp_auth_unique_id', 'NameID')][0];
}
else {
throw new Exception(t('error in simplesamlphp_auth.module: no valid unique id attribute set'));
}
}
return $authname;
}
/**
* Gets the default name attribute from the SAML assertion.
*
* @return
* The name attribute.
*/
function _simplesamlphp_auth_get_default_name() {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$default_name = '';
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated() ) {
$auth_user_name_attr = variable_get('simplesamlphp_auth_user_name', 'eduPersonPrincipalName');
if ((!isset($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr])) ||
(!isset($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0])) ||
($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0] == '')) {
throw new Exception(t('There was no set attribute named "%auth_user_name_attr"',
array(
'%auth_user_name_attr' => $auth_user_name_attr,
)));
}
$default_name = $_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0];
}
return $default_name;
}
/**
* Gets an attribute value.
*
* @return
* The mail attribute.
*/
function _simplesamlphp_auth_get_attr($attr_name, $default) {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$value = '';
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
if (isset($_simplesamlphp_auth_saml_attributes[variable_get($attr_name, $default)])) {
$value = $_simplesamlphp_auth_saml_attributes[variable_get($attr_name, $default)][0];
}
else {
throw new Exception(t("Error in simplesamlphp_auth.module: No valid $attr_name attribute set."));
}
}
return $value;
}
/**
* Forces HTTPS connections.
*/
function _simplesamlphp_auth_forcehttps_rewrite($url) {
if (variable_get('simplesamlphp_auth_forcehttps', TRUE)) {
$url = str_replace('http://', 'https://', $url);
_simplesaml_auth_debug('forcehttps rewrite: ' . $url);
}
return $url;
}
/**
* Generates the text for the log in block.
*/
function _simplesamlphp_auth_generate_block_text() {
global $_simplesamlphp_auth_as;
$block_content = '';
global $user;
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$block_content .= '<p>Logged in as: ' . $user->name . '<br />' . l('Log Out', 'user/logout') . '</a></p>';
}
else {
$block_content .= '<p>' . l('Federated Log In', 'saml_login') . '</p>';
}
return $block_content;
}
/**
* Evaluates a role rule.
*
* @param $roleruleevaluation
* An array containing the role rule to evaluate.
* @param $attributes
* An array containing the identity attributes.
*
* @return
* An array containing role value and the attribute, or FALSE.
*/
function _simplesamlphp_auth_evaulaterolerule($roleruleevaluation, $attributes) {
_simplesaml_auth_debug(t('Evaluate rule (key=%key,operator=%op,value=%val)', array('%key' => $roleruleevaluation[0], '%op' => $roleruleevaluation[1], '%val' => $roleruleevaluation[2])));
if (!array_key_exists($roleruleevaluation[0], $attributes)) {
return FALSE;
}
$attribute = $attributes[$roleruleevaluation[0]];
switch ($roleruleevaluation[1]) {
case '=' :
return in_array($roleruleevaluation[2], $attribute);
case '@=' :
$dc = explode('@', $attribute[0]);
if (count($dc) != 2) {
return FALSE;
}
return ($dc[1] == $roleruleevaluation[2]);
}
return FALSE;
}
/**
* Performs role population.
*
* @param $rolemap
* A string containing the role map.
*
* @return
* An array containing user's roles.
*/
function _simplesamlphp_auth_rolepopulation($rolemap) {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$roles = array();
_simplesaml_auth_debug(t('Rolemap: %rolemap', array('%rolemap' => $rolemap)));
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$attributes = $_simplesamlphp_auth_saml_attributes;
if (empty($rolemap)) return $roles;
_simplesaml_auth_debug(t('Evaluate rolemap: %rolemap', array('%rolemap' => $rolemap)));
$rolerules = explode('|', $rolemap);
foreach ($rolerules AS $rolerule) {
_simplesaml_auth_debug(t('Evaluate role rule: %rolerule', array('%rolerule' => $rolerule)));
$roleruledecompose = explode(':', $rolerule);
$roleid = $roleruledecompose[0];
$roleruleevaluations = explode(';', $roleruledecompose[1]);
$addnew = TRUE;
foreach ($roleruleevaluations AS $roleruleevaluation) {
_simplesaml_auth_debug(t('Evaluate role evaulation: %roleruleeval', array('%roleruleeval' => $roleruleevaluation)));
$roleruleevaluationdc = explode(',', $roleruleevaluation);
if (!_simplesamlphp_auth_evaulaterolerule($roleruleevaluationdc, $attributes)) {
$addnew = FALSE;
}
}
if ($addnew) {
$roles[$roleid] = $roleid;
_simplesaml_auth_debug(t('Add new role: %roleid', array('%roleid' => $roleid)));
}
}
}
return $roles;
}
/**
* See if the user has an authmap record for simplesamlphp_auth
*/
function _simplesaml_auth_user_has_authmap($authname) {
$authmaps = user_get_authmaps($authname);
$return = 0;
if (is_array($authmaps)) {
$return = in_array('simplesamlphp_auth', array_keys($authmaps));
}
return $return;
}
/**
* This helper function is used by developers to debug the form API workflow in this module.
*/
function _simplesaml_auth_debug($message) {
watchdog('simplesamlphp', $message, NULL, WATCHDOG_DEBUG);
}
/**
* Helper function for logging out a user that is has a active session in Drupal but not with simpleSAML.
*/
function _simplesamlphp_auth_destroy_drupal_session() {
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();
drupal_goto();
}
/****************************************************************************
* Public functions *********************************************************
****************************************************************************/
/**
* Determine if the current user is authenticated through SAML.
*
* @return
* TRUE if the current user is authenticated through SAML. FALSE otherwise.
*/
function simplesamlphp_auth_is_authenticated() {
global $_simplesamlphp_auth_as;
// Assume that the user isn't authenticated until proven otherwise.
$authenticated = FALSE;
// If the associated global variable exists, and the auth flag is set, note it.
if (isset($_simplesamlphp_auth_as) && $_simplesamlphp_auth_as->isAuthenticated()) {
$authenticated = TRUE;
}
// Return the result.
return $authenticated;
}
/**
* Return any attributes provided by the SAML IDP.
*
* @param $attribute
* The attribute whose value to return. Can be skipped if all attribute
* values are requested.
*
* @return
* If an attribute was provided, the value of the attribute is returned.
* Otherwise, an array of all attribute values is returned, keyed by
* attribute.
*/
function simplesamlphp_auth_get_attributes($attribute = NULL) {
global $_simplesamlphp_auth_saml_attributes;
if (isset($attribute)) {
// Initially, assume that there's nothing to return.
$result = NULL;
// If the specified attribute is set, grab it.
if (isset($_simplesamlphp_auth_saml_attributes)) {
if (isset($_simplesamlphp_auth_saml_attributes[$attribute])) {
$result = $_simplesamlphp_auth_saml_attributes[$attribute];
}
}
}
// No specific attribute was requested; return all of them.
else {
// Initially, assume that there's nothing to return.
$result = array();
// If the global array exists, return it.
if (isset($_simplesamlphp_auth_saml_attributes)) {
$result = $_simplesamlphp_auth_saml_attributes;
}
}
// Return whatever we've got.`
return $result;
}