simple_ldap_user.module in Simple LDAP 7
Same filename and directory in other branches
Main simple_ldap_user module file.
File
simple_ldap_user/simple_ldap_user.moduleView source
<?php
/**
* @file
* Main simple_ldap_user module file.
*/
/**
* Implements hook_menu().
*/
function simple_ldap_user_menu() {
$items = array();
$items['admin/config/people/simple_ldap/user'] = array(
'title' => 'Users',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'simple_ldap_user_admin',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'simple_ldap_user.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/people/simple_ldap_user_import'] = array(
'title' => 'Import from LDAP',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'simple_ldap_user_import',
),
'access arguments' => array(
'administer users',
),
'file' => 'simple_ldap_user.admin.inc',
'type' => MENU_LOCAL_ACTION,
);
return $items;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Overrides the built-in user module's list of users, setting accounts to
* "blocked" if there is no matching LDAP account.
*/
function simple_ldap_user_form_user_admin_account_alter(&$form, &$form_state, $form_id) {
// Update the user array.
foreach ($form['accounts']['#options'] as $uid => $user) {
// Don't mess with user/1.
if ($uid == 1) {
continue;
}
// Verify active users. Blocked users may be provisioned to LDAP when they
// are set to active, so they are left alone here.
if ($user['status'] == 'active') {
// Load the user objects.
$drupal_user = user_load($uid);
$ldap_user = SimpleLdapUser::singleton($drupal_user->name);
// Check whether the user exists in LDAP.
if (!$ldap_user->exists) {
$form['accounts']['#options'][$uid]['status'] = 'blocked';
}
// Check whether the user is disabled (Active Directory only).
// http://support.microsoft.com/kb/305144
if ($ldap_user->server->type == 'Active Directory') {
if (isset($ldap_user->useraccountcontrol[0]) && (int) $ldap_user->useraccountcontrol[0] & 2) {
$form['accounts']['#options'][$uid]['status'] = 'blocked';
}
}
}
}
}
/**
* Implements hook_entity_info_alter().
*
* Specifies that SimpleLdapuserController should be used to load users instead
* of the default controller.
*/
function simple_ldap_user_entity_info_alter(&$entity_info) {
if (isset($entity_info['user'])) {
// Use the SimpleLdapUserController class to manage users.
$entity_info['user']['controller class'] = 'SimpleLdapUserController';
}
}
/**
* Implements hook_form_alter().
*/
function simple_ldap_user_form_alter(&$form, &$form_state, $form_id) {
switch ($form_id) {
case 'user_login_block':
// Remove the register and password reminder links.
$server = SimpleLdapServer::singleton();
if ($server->readonly) {
unset($form['links']);
}
case 'user_login':
case 'user_pass':
// Insert simple_ldap_user's username validation.
array_unshift($form['#validate'], 'simple_ldap_user_login_name_validate');
break;
case 'user_profile_form':
$server = SimpleLdapServer::singleton();
$map_object = SimpleLdapUserMap::singleton();
// Active Directory has some additional restrictions.
if ($server->type == 'Active Directory') {
$form['account']['name']['#disabled'] = TRUE;
$form['account']['pass']['#disabled'] = stripos($server->host, 'ldaps://') === FALSE;
}
// If the server is read/write, then break early to bypass disabling
// of any properties or fields.
if ($server->readonly) {
$map_object
->disableMappedFormFields($form);
}
break;
case 'user_register_form':
array_unshift($form['#validate'], 'simple_ldap_user_register_form_validate');
break;
default:
}
}
/**
* Implements hook_menu_alter().
*
* Disables the user register and password reminder pages if the LDAP server is
* read-only.
*/
function simple_ldap_user_menu_alter(&$items) {
$server = SimpleLdapServer::singleton();
if ($server->readonly) {
$items['user/register']['access callback'] = FALSE;
$items['user/password']['access callback'] = FALSE;
}
}
/**
* Implements hook_user_login().
*
* Fires when a user logs in.
*
* @param array $edit
* The form values submitted by the user to log in,
* including raw username and password.
*/
function simple_ldap_user_user_login(&$edit, $account) {
if ($account->uid == 1) {
return;
}
$sync = simple_ldap_user_variable_get('simple_ldap_user_sync');
if ($sync == 'hook_user_login') {
simple_ldap_user_sync_user($account);
}
}
/**
* Implements hook_user_presave().
*
* Fires before an account is created or changed.
*
* @param array $edit
* The form values submitted by the user.
*/
function simple_ldap_user_user_presave(&$edit, $account, $category) {
// Do not overwrite the user status in the database.
if (isset($account->simple_ldap_user_drupal_status)) {
// If status is in the edit array, we need to be sure we're not
// unintentionally saving the LDAP value to the database. To check this, we
// see if $edit['status'] matches $account->status. If it does, set
// $edit['status'] to the simple_ldap_user_drupal_status value.
if (isset($edit['status']) && $edit['status'] == $account->status) {
$edit['status'] = $account->simple_ldap_user_drupal_status;
}
// Now set the $account->status back to the value in the database, just to
// be safe. This ensures that if $edit['status'] is empty, we don't mess up
// what's in the database with what is on the user object.
$account->status = $account->simple_ldap_user_drupal_status;
}
// To make sure we've covered all our bases, we also set $account->original's
// status back to what is in the database as well.
if (isset($account->original) && isset($account->original->simple_ldap_user_drupal_status)) {
$account->original->status = $account->simple_ldap_user_drupal_status;
}
if ($account->is_new && isset($edit['name'])) {
$ldap_user = SimpleLdapUser::singleton($edit['name']);
if ($ldap_user->exists) {
// Force an initial sync from LDAP to drupal.
$ldap_user->mapObject
->mapFromLdapToDrupal($ldap_user, $edit, $account);
}
}
//If account updated and username changed, RDN default to drupal name and ldap
//entry already exists from elsewhere, reset new changed username to its previous value.
if (isset($account->original)) {
//New name edited or programmatically set ?
$new_username = '';
if (!empty($edit['name']) && $account->original->name !== $edit['name']) {
$new_username = $edit['name'];
}
elseif ($account->original->name !== $account->name) {
$new_username = $account->name;
}
if ($new_username) {
$ldap_user = SimpleLdapUser::singleton($new_username);
$attribute_rdn = simple_ldap_user_variable_get('simple_ldap_user_attribute_rdn');
if (empty($attribute_rdn) && $ldap_user->exists) {
$account->name = $account->original->name;
$edit['name'] = $account->original->name;
drupal_set_message(t('The new username %name could not be changed because an LDAP entry already exists. Previous name have been keep and no LDAP modification have been done.', array(
'%name' => $new_username,
)), 'error');
}
}
}
}
/**
* Implements hook_user_insert().
*
* Fires after a new account is created.
*
* @param array $edit
* The form values submitted by the user.
*/
function simple_ldap_user_user_insert(&$edit, $account, $category) {
$ldap_user = SimpleLdapUser::singleton($account->name);
if (!$ldap_user->exists) {
module_invoke_all('sync_user_to_ldap', $account);
}
}
/**
* Implements hook_user_update().
*
* Fires when a user account is edited.
*
* @param array $edit
* The form values submitted by the user.
*/
function simple_ldap_user_user_update(&$edit, $account, $category) {
// Don't do anything for uid 1.
if ($account->uid == 1) {
return;
}
// Don't do anything if the hook was called via hook_sync_user_to_drupal().
if (empty($account->hook_sync_user_to_drupal)) {
$ldap_user = SimpleLdapUser::singleton($account->name);
$enabled = isset($edit['status']) ? $edit['status'] : NULL;
// In hook_user_presave, we may have messed with the $edit['status'] and
// $account->status values, setting them to the database values, not what
// LDAP had set status to. Set those back to the LDAP values now.
if (isset($account->simple_ldap_user_ldap_status)) {
$enabled = $account->status = $account->simple_ldap_user_ldap_status;
}
if ($enabled || $ldap_user->exists) {
module_invoke_all('sync_user_to_ldap', $account);
}
}
else {
unset($account->hook_sync_user_to_drupal);
}
}
/**
* Implements hook_user_delete().
*
* Fires when a user account is deleted, before account is
* deleted.
*
* @throw SimpleLdapException
*/
function simple_ldap_user_user_delete($account) {
if (!simple_ldap_user_variable_get('simple_ldap_user_delete_from_ldap')) {
return;
}
$ldap_user = SimpleLdapUser::singleton($account->name);
try {
$ldap_user
->delete();
} catch (SimpleLdapException $e) {
drupal_set_message(t('Failed to delete @name from LDAP.', array(
'@name' => $account->name,
)) . ' ' . t('Error @code: @message.', array(
'@code' => $e
->getCode(),
'@message' => $e
->getMessage(),
)), 'error', FALSE);
}
}
/**
* Implements hook_user_load().
*
* Fires when user information is being loaded from the database.
* User information is cached, so this does not fire every time
* a user object is handled.
*/
function simple_ldap_user_user_load($users) {
$sync = simple_ldap_user_variable_get('simple_ldap_user_sync');
if ($sync == 'hook_user_load') {
foreach ($users as $account) {
if ($account->uid == 1) {
continue;
}
simple_ldap_user_sync_user($account);
}
}
}
/**
* Check the name and email both belong to the same LDAP account, or no
* account at all.
*/
function simple_ldap_user_register_form_validate($form, &$form_state) {
$ldap_user_by_name = SimpleLdapUser::singleton($form_state['values']['name']);
$ldap_user_by_mail = SimpleLdapUser::singleton($form_state['values']['mail']);
$name_dn = $ldap_user_by_name->dn;
$mail_dn = $ldap_user_by_mail->dn;
if ($name_dn !== $mail_dn) {
if (empty($mail_dn)) {
form_set_error('name', t('A user with that username is already registered, but not with that email address.'));
}
elseif (empty($name_dn)) {
form_set_error('mail', t('A user with that email address is already registerd, but not with that username.'));
}
else {
form_set_error('name', t('Both the username and email address are in use, but not with the same account.'));
}
}
}
/**
* Validate the username on a login or password reset form.
*/
function simple_ldap_user_login_name_validate($form, &$form_state) {
// Get the username from the form data.
$name = trim($form_state['values']['name']);
simple_ldap_user_load_or_create_by_name($name);
}
/**
* Create a valid LDAP user on this site if they don't already exist.
*
* @param string $name
* The username or email address to load.
*
* @return mixed
* The Drupal user object, or FALSE if the process failed.
*/
function simple_ldap_user_load_or_create_by_name($name) {
// Load the LDAP user with the given username.
$ldap_user = SimpleLdapUser::singleton($name);
// If the user doesn't exist in LDAP, there is nothing for us to do.
if (!$ldap_user->exists) {
return FALSE;
}
// Attempt to load the drupal user.
$drupal_user = user_load_by_name($name);
if (!$drupal_user) {
$drupal_user = user_load_by_mail($name);
}
// If the user doesn't already exist in Drupal, create them.
if (!$drupal_user) {
$attribute_name = simple_ldap_user_variable_get('simple_ldap_user_attribute_name');
$attribute_mail = simple_ldap_user_variable_get('simple_ldap_user_attribute_mail');
$edit = array(
'name' => $ldap_user->{strtolower($attribute_name)}[0],
'mail' => $ldap_user->{strtolower($attribute_mail)}[0],
'status' => 1,
);
$drupal_user = user_save(NULL, $edit);
}
return $drupal_user;
}
/**
* Synchronize a user from or to LDAP, depending on the settings.
*/
function simple_ldap_user_sync_user($drupal_user) {
switch (simple_ldap_user_variable_get('simple_ldap_user_source')) {
case 'ldap':
simple_ldap_user_sync_user_to_drupal($drupal_user);
break;
case 'drupal':
simple_ldap_user_sync_user_to_ldap($drupal_user);
break;
}
}
/**
* Synchronizes Drupal user properties to LDAP.
*/
function simple_ldap_user_sync_user_to_ldap($drupal_user) {
// Don't try to sync if the server is read-only.
$server = SimpleLdapServer::singleton();
if ($server->readonly) {
return;
}
// Don't try to sync anonymous or user 1.
if ($drupal_user->uid == 0 || $drupal_user->uid == 1) {
return;
}
// simple_ldap_user configuration.
$attribute_name = simple_ldap_user_variable_get('simple_ldap_user_attribute_name');
$attribute_mail = simple_ldap_user_variable_get('simple_ldap_user_attribute_mail');
$attribute_pass = simple_ldap_user_variable_get('simple_ldap_user_attribute_pass');
// Load the LDAP user.
$ldap_user = SimpleLdapUser::singleton($drupal_user->name);
// Mail is a special attribute.
$ldap_user->{$attribute_mail} = $drupal_user->mail;
// Password is a special attribute.
$ldap_user->{$attribute_pass} = $drupal_user->pass;
// Perform additional property and field mappings based on the user map.
$ldap_user->mapObject
->mapFromDrupalToLdap($drupal_user, $ldap_user);
// Set the DN.
$attribute_rdn = simple_ldap_user_variable_get('simple_ldap_user_attribute_rdn');
if (empty($attribute_rdn)) {
$attribute_rdn = $attribute_name;
}
if ($ldap_user->{$attribute_rdn}['count'] > 0) {
//If drupal username have changed and is used for RDN, retrieve old ldap
//user already loaded by controller and set previous DN temporary to move it.
if (isset($drupal_user->original) && $drupal_user->original->name !== $drupal_user->name && $attribute_rdn === $attribute_name) {
$original_ldap_user = SimpleLdapUser::singleton($drupal_user->original->name);
$ldap_user->dn = $original_ldap_user->dn;
}
if ($ldap_user->dn) {
// Reconstruct an existing DN.
$parts = SimpleLdap::ldap_explode_dn($ldap_user->dn);
$basedn = '';
for ($i = 1; $i < $parts['count']; $i++) {
$basedn .= ',' . $parts[$i];
}
}
else {
// Default to using the configured basedn.
$basedn = ',' . simple_ldap_user_variable_get('simple_ldap_user_basedn');
}
$ldap_user->dn = $attribute_rdn . '=' . $ldap_user->{$attribute_rdn}[0] . $basedn;
}
// Allow altering the LDAP user object before saving.
drupal_alter('simple_ldap_user_to_ldap', $ldap_user, $drupal_user);
// Save any changes.
try {
$ldap_user
->save();
} catch (SimpleLdapException $e) {
drupal_set_message(t('Failed to save the user to LDAP.') . ' ' . format_string('%error', array(
'%error' => $e
->getMessage(),
)), 'error');
}
}
/**
* Synchronizes LDAP attributes to Drupal user properties.
*/
function simple_ldap_user_sync_user_to_drupal($drupal_user) {
// Load the LDAP user, force a cache reset.
$ldap_user = SimpleLdapUser::singleton($drupal_user->name, TRUE);
// Nothing to sync.
if (!$ldap_user->exists || isset($drupal_user->uid) && $drupal_user->uid == 1) {
return;
}
// Initialize array of attribute changes.
$edit = array();
$ldap_user->mapObject
->mapFromLdapToDrupal($ldap_user, $edit, $drupal_user);
// Allow altering the Drupal user object before saving.
drupal_alter('simple_ldap_user_to_drupal', $edit, $drupal_user, $ldap_user);
// Save any changes.
if (!empty($edit)) {
if (!isset($drupal_user->original)) {
// This avoids an infinite load/save loop.
$drupal_user->original = clone $drupal_user;
}
$drupal_user->hook_sync_user_to_drupal = TRUE;
$drupal_user = user_save($drupal_user, $edit);
}
// Synchronized user.
return $drupal_user;
}
/**
* Implements hook_user_operations().
*/
function simple_ldap_user_user_operations() {
$operations = array();
$server = SimpleLdapServer::singleton();
if (!$server->readonly) {
$operations['simple_ldap_user_export'] = array(
'label' => t('Export selected users to LDAP'),
'callback' => 'simple_ldap_user_export',
);
}
return $operations;
}
/**
* Handles bulk user export from admin/people.
*/
function simple_ldap_user_export($users) {
// Generate the batch operation array.
$operations = array();
foreach ($users as $uid) {
// Don't sync user1.
if ($uid == 1) {
continue;
}
$operations[] = array(
'simple_ldap_user_export_user',
array(
$uid,
),
);
}
$batch = array(
'operations' => $operations,
);
batch_set($batch);
}
/**
* Batch process function for mass user export.
*/
function simple_ldap_user_export_user($uid, $context) {
// Sync user to LDAP.
$user = user_load($uid);
simple_ldap_user_sync_user_to_ldap($user);
$context['message'] = 'Exporting ' . $user->name;
}
/**
* Returns the value for the specified variable.
*
* This function takes into account the configured LDAP server type, and
* attempts to determine a reasonable default value to try to use in the event
* that the module has not yet been configured.
*/
function simple_ldap_user_variable_get($name, $default = NULL, $force_default = FALSE) {
// Allow variable name shorthand by prepending 'simple_ldap_user_' to $name if
// it is not already there.
if (strpos($name, 'simple_ldap_user_') !== 0) {
$name = 'simple_ldap_user_' . $name;
}
// Get an LDAP server object.
$server = SimpleLdapServer::singleton();
// Handle special variables.
switch ($name) {
case 'simple_ldap_user_source':
// If the LDAP server is set to read-only, force LDAP->Drupal sync.
if ($server->readonly) {
return 'ldap';
}
break;
case 'simple_ldap_user_attribute_map':
// Load the attribute map from settings.php.
$attribute_map = variable_get($name, array());
return $attribute_map;
}
// Define defaults that differ based on LDAP server type.
switch ($server->type) {
case 'Active Directory':
$defaults = array(
'simple_ldap_user_objectclass' => array(
'user',
),
'simple_ldap_user_attribute_name' => 'samaccountname',
'simple_ldap_user_attribute_mail' => 'mail',
'simple_ldap_user_attribute_pass' => 'unicodepwd',
'simple_ldap_user_password_hash' => 'unicode',
'simple_ldap_user_attribute_rdn' => 'cn',
);
break;
default:
$defaults = array(
'simple_ldap_user_objectclass' => array(
'inetorgperson',
),
'simple_ldap_user_attribute_name' => 'cn',
'simple_ldap_user_attribute_mail' => 'mail',
'simple_ldap_user_attribute_pass' => 'userpassword',
'simple_ldap_user_password_hash' => 'salted sha',
);
}
// Define defaults that do not depend on LDAP server type.
$defaults['simple_ldap_user_basedn'] = $server->basedn;
$defaults['simple_ldap_user_scope'] = 'sub';
$defaults['simple_ldap_user_source'] = 'ldap';
$defaults['simple_ldap_user_sync'] = 'hook_user_load';
$defaults['simple_ldap_user_delete_from_ldap'] = '1';
// Determine the default value for the given variable.
$default = isset($defaults[$name]) ? $defaults[$name] : $default;
if ($force_default) {
return $default;
}
return variable_get($name, $default);
}