ldapsync.module in LDAP integration 6
ldapsync keeps LDAP and Drupal user lists synchronized.
File
ldapsync.moduleView source
<?php
/**
* @file
* ldapsync keeps LDAP and Drupal user lists synchronized.
*/
//////////////////////////////////////////////////////////////////////////////
define('LDAPSYNC_CONFLICT_FOLLOW_LDAPAUTH', 3);
define('LDAPSYNC_TIME_INTERVAL', variable_get('ldapsync_time_interval', -1));
define('LDAPSYNC_FILTER', variable_get('ldapsync_filter', ''));
define('LDAPSYNC_LOGIN_CONFLICT', variable_get('ldapsync_login_conflict', LDAPSYNC_CONFLICT_FOLLOW_LDAPAUTH));
//////////////////////////////////////////////////////////////////////////////
// Core API hooks
/**
* Implements hook_init().
*/
function ldapsync_init() {
module_load_include('inc', 'ldapauth', 'includes/ldap.core');
module_load_include('inc', 'ldapauth', 'includes/LDAPInterface');
}
/**
* Implementation of hook_help().
*/
function ldapsync_help($path, $arg) {
$output = '';
switch ($path) {
case "admin/help#ldapsync":
$output = '<p>' . t("Searches LDAP to update Drupal user membership and information.") . '</p>';
break;
}
return $output;
}
/**
* Implementation of hook_menu().
*/
function ldapsync_menu() {
return array(
'admin/settings/ldap/ldapsync' => array(
'title' => 'Synchronization',
'description' => 'Configure LDAP sync settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'ldapsync_admin_settings',
),
'access arguments' => array(
'administer ldap modules',
),
'file' => 'ldapsync.admin.inc',
),
);
}
/**
* Implements hook_cron().
*
* Checks ldapsync_time_interval and ldapsync_last_sync_time variables to determine whether to run ldapsync.
*/
function ldapsync_cron() {
$time_interval = variable_get('ldapsync_time_interval', -1);
// -1 means "only sync manually"
$last_sync_time = variable_get('ldapsync_last_sync_time', 0);
if (time() - $last_sync_time > $time_interval && $time_interval != -1) {
_ldapsync_sync();
}
}
/**
* Main routine.
*/
function _ldapsync_sync() {
global $_ldapsync_ldap;
// If ldapgroups is enabled, include it for groups-role sync.
if (module_exists('ldapgroups')) {
module_load_include('inc', 'ldapgroups', 'ldapgroups');
}
// Find all users in specified OU (using base DN and bind information from ldapauth).
// and take appropriate action on the Drupal side.
$ldap_users = _ldapsync_search();
$count_orphaned_users = 0;
// Do we have any LDAP-authentified Drupal users who don't exist in LDAP?
if ($ldap_users) {
$result = db_query("SELECT uid, name, data FROM {users} WHERE status = %d", 1);
while ($row = db_fetch_array($result)) {
if (!isset($ldap_users[drupal_strtolower($row['name'])])) {
$data = unserialize($row['data']);
if ($data['ldap_authentified']) {
// Block user if appropriate module setting is set.
if (variable_get('ldapsync_missing_users_action', 'warn') == 'block') {
// Block user.
db_query("UPDATE {users} SET status=0 WHERE uid=%d", $row['uid']);
// Log out blocked user.
$account = user_load(array(
'uid' => $row['uid'],
));
$array = array();
user_module_invoke('logout', $array, $account);
// Log this.
watchdog('ldapsync', 'Disabled LDAP-authentified user %name because the corresponding LDAP account does not exist or is disabled.', array(
'%name' => $row['name'],
));
}
$count_orphaned_users++;
}
}
}
}
// Send watchdog message with process summary.
$params = array(
'@ldap_users' => ldapsync_stats('ldap_users'),
'@existing_users' => ldapsync_stats('existing_users'),
'@new_users' => ldapsync_stats('new_users'),
'@orphaned_users' => $count_orphaned_users,
);
$converted = ldapsync_stats('converted');
$ldap_disabled = ldapsync_stats('ldap_users_disabled');
$notices = ldapsync_stats('notices');
$denied_by_module = ldapsync_stats('denied_by_module');
$summary = t('Completed LDAP sync. LDAP users found: @ldap_users. Existing users updated: @existing_users. New users created: @new_users. LDAP-authentified users that do not have an enabled LDAP account: @orphaned_users.', $params);
if ($converted) {
$summary .= ' ' . t('Existing users converted to LDAP: @converted.', array(
'@converted' => $converted,
));
}
if ($ldap_disabled) {
$summary .= ' ' . t('Disabled LDAP users: @disabled.', array(
'@disabled' => $ldap_disabled,
));
}
if ($notices) {
$summary .= ' ' . t('Watchdog notices/warnings written: @notices.', array(
'@notices' => $notices,
));
}
if ($denied_by_module) {
$summary .= ' ' . t('Access denied by other modules: @denied.', array(
'@denied' => $denied_by_module,
));
}
watchdog('ldapsync', $summary, NULL);
// Update last sync time variable, so that we don't sync again until the specified time period passes again.
variable_set('ldapsync_last_sync_time', time());
// Useful if calling manually from settings page.
return $summary;
}
/**
* Find all LDAP users from servers and OUs specified in ldapauth settings and
* create or update existing users as needed.
*
* @return An array keyed by lower cased Drupal account name of all users found.
*/
function _ldapsync_search() {
global $_ldapsync_ldap;
$users = array();
// Cycle through LDAP configurations.
$result = db_query("SELECT sid FROM {ldapauth} WHERE status = %d ORDER BY sid", 1);
while ($row = db_fetch_object($result)) {
// Initialize LDAP.
if (!_ldapsync_init($row->sid)) {
watchdog('ldapsync', 'ldapsync init failed for ldap server %sid.', array(
'%sid' => $row->sid,
));
continue;
}
// Set up for LDAP search.
$name_attr = $_ldapsync_ldap
->getOption('user_attr') ? $_ldapsync_ldap
->getOption('user_attr') : LDAPAUTH_DEFAULT_USER_ATTR;
$user_attr = drupal_strtolower($name_attr);
// used to find in results.
$filters = array();
if (LDAPSYNC_FILTER) {
$filters = explode("\r\n", LDAPSYNC_FILTER);
}
else {
$filters[] = "({$name_attr}=*)";
}
$attrs = ldapauth_attributes_needed(LDAPAUTH_SYNC_CONTEXT_AUTHENTICATE_DRUPAL_USER, $_ldapsync_ldap
->getOption('sid'));
// If there is no bindn and bindpw - the connect will be an anonymous connect.
$_ldapsync_ldap
->connect($_ldapsync_ldap
->getOption('binddn'), $_ldapsync_ldap
->getOption('bindpw'));
// Search each basedn defined for this server
foreach (explode("\r\n", $_ldapsync_ldap
->getOption('basedn')) as $base_dn) {
if (empty($base_dn)) {
continue;
}
// Re-initialize database object each time.
$ldapresult = array();
$filter_found_users = array();
// Search this server and basedn using all defined filters.
foreach ($filters as $filter) {
$filter = trim($filter);
if (empty($filter)) {
continue;
}
if (variable_get('ldapsync_filter_append_default', 0)) {
$filter = "(&{$filter}({$name_attr}=*))";
}
if (!($ldapresult = $_ldapsync_ldap
->search($base_dn, $filter, $attrs))) {
continue;
}
// Cycle through results to build array of user information.
foreach ($ldapresult as $entry) {
$name = $entry[$user_attr][0];
// Don't include if no name attribute.
if (empty($name)) {
continue;
}
// Don't process the same entry found by different filters twice.
$lcname = drupal_strtolower($name);
if (isset($filter_found_users[$lcname])) {
continue;
// Already found
}
$filter_found_users[$lcname] = $name;
ldapsync_stats('ldap_users', 1);
// Don't include if LDAP account is disabled.
$status = $entry['useraccountcontrol'][0];
if (($status & 2) != 0) {
// This only works for Active Directory -- search includes disabled accounts in other directories.
ldapsync_stats('ldap_users_disabled', 1);
continue;
}
// Process this entry to create/update drupal user (if any).
$account = _ldapsync_process_entry($_ldapsync_ldap, $name, $entry);
if (!$account) {
continue;
}
$users[drupal_strtolower($account->name)] = array(
'uid' => $account->uid,
);
}
}
}
}
return $users;
}
/**
* Take an ldap object entry and determine if there is an existing account or
* a new account needs to be created.
*
* @param LDAPInterface $ldap An initialized LDAP server interface object
* @param String $name The user name attribute value
* @param Array $ldap_entry LDAP attributes for user.
* @return The account object or FALSE if problem
*/
function _ldapsync_process_entry($ldap, $name, $ldap_entry) {
// check whether user is in an OU mapped in module settings (need to create admin/settings/ldapsync page)
$dn = $ldap_entry['dn'];
if ($ldap
->getOption('puid_attr')) {
$puid = ldapauth_extract_puid($server, $name, $ldap_entry);
}
// See if there is a matching Drupal user account
$error = '';
$account = ldapauth_drupal_user_lookup($ldap, $name, $dn, $error, $puid);
if ($account === NULL) {
ldapsync_stats('notices', 1);
$msg = t('drupal_user_lookup() returned: ') . $error;
watchdog('ldapsync', $msg, NULL, WATCHDOG_ERROR);
return FALSE;
}
// Handle map by e-mail option (Issue #1209556)
// If no account or PUID not used and account found does not have matching e-mail
$user_test_method = variable_get('ldapsync_load_user_by', 'name');
if ($user_test_method == 'email' && (!$account || !$ldap
->getOption('puid_attr') && drupal_strtolower($account->mail) != drupal_strtolower($ldap_entry['mail']))) {
$account = user_load(array(
'mail' => $ldap_entry['mail'],
));
}
// Allow other modules to determine if this ldap user can access server.
if (ldapauth_user_denied($ldap, $name, $dn, $account)) {
ldapsync_stats('denied_by_module', 1);
return;
}
// No account found - try to create one
if (!$account) {
if (variable_get('ldapsync_existing_only', 0)) {
return FALSE;
}
$error = '';
$account = ldapauth_drupal_user_create($ldap, $name, $ldap_entry, $error);
if ($account === FALSE) {
ldapsync_stats('notices', 1);
return FALSE;
}
ldapsync_stats('new_users', 1);
// Increment counter
}
else {
// Check authentication method.
if (!$account->ldap_authentified) {
$conflict_resolution = LDAPSYNC_LOGIN_CONFLICT;
if ($conflict_resolution == LDAPSYNC_CONFLICT_FOLLOW_LDAPAUTH) {
$conflict_resolution = LDAPAUTH_LOGIN_CONFLICT;
}
if ($conflict_resolution == LDAPAUTH_CONFLICT_LOG) {
ldapsync_stats('notices', 1);
watchdog('ldapsync', 'Could not create ldap-authentified account for user %name because a local user by that %test_value already exists.', array(
'%name' => $name,
'%test_value' => $user_test_method,
));
return FALSE;
}
else {
$converted = TRUE;
ldapsync_stats('converted', 1);
}
}
// Make sure all the information is up to date.
$drupal_name = ldapauth_drupal_user_name($name, $ldap, $dn);
$data = array(
'ldap_dn' => $dn,
'ldap_config' => $ldap
->getOption('sid'),
'ldap_authentified' => TRUE,
'authname_ldapauth' => $drupal_name,
'ldap_name' => $name,
);
// Follow ldapauth password sync rules.
if (LDAPAUTH_LOGIN_PROCESS == LDAPAUTH_AUTH_MIXED && LDAPAUTH_SYNC_PASSWORDS) {
$data['pass'] = $pass;
}
$puid = $account->ldap_puid;
// save setting from drupal_user_lookupsave.
$account = user_save($account, $data);
// Make sure the ldapauth_users info is current (User object may have been moved).
$user_info = ldapauth_userinfo_load_by_uid($account->uid);
if (empty($user_info)) {
// Don't have entry, so make one.
$user_info = new stdClass();
$user_info->uid = $account->uid;
}
$user_info->sid = $account->ldap_config;
$user_info->machine_name = $ldap
->getOption('machine_name');
$user_info->dn = $dn;
$user_info->puid = $puid ? $puid : $account->{$name};
ldapauth_userinfo_save($user_info);
if (!$converted) {
ldapsync_stats('existing_users', 1);
}
}
// Update user's groups if ldapgroups is enabled.
if (module_exists('ldapgroups')) {
ldapgroups_user_login($account);
}
// Update user's data if ldapdata is enabled.
if (module_exists('ldapdata')) {
_ldapdata_user_load($account, TRUE, $ldap_users);
}
// Enable any blocked user who is enabled in LDAP.
if (!$account->status) {
ldapsync_stats('notices', 1);
db_query("UPDATE {users} SET status = %d where uid = %d", 1, $account->uid);
watchdog('ldapsync', 'Enabled LDAP-authentified user %name because the corresponding LDAP account is enabled.', array(
'%name' => $name,
));
}
// Reset user specific caches to prevent memory problems
ldapauth_user_lookup_by_dn(NULL, NULL, NULL, TRUE);
ldapauth_drupal_user_name(NULL, NULL, NULL, TRUE);
if (module_exists('ldapgroups')) {
ldapgroups_groups_load(NULL, NULL, NULL, TRUE);
}
return $account;
}
//////////////////////////////////////////////////////////////////////////////
// Auxiliary functions
/**
* Initiates the LDAPInterfase class.
*
* @param $sid
* An ID of the LDAP server configuration.
*
* @return
*/
function _ldapsync_init($sid) {
global $_ldapsync_ldap;
$server = ldapauth_server_load($sid);
if (!empty($server)) {
$_ldapsync_ldap = new LDAPInterface();
$_ldapsync_ldap
->setOption('sid', $server->sid);
$_ldapsync_ldap
->setOption('name', $server->name);
$_ldapsync_ldap
->setOption('machine_name', $server->machine_name);
$_ldapsync_ldap
->setOption('server', $server->server);
$_ldapsync_ldap
->setOption('port', $server->port);
$_ldapsync_ldap
->setOption('tls', $server->tls);
$_ldapsync_ldap
->setOption('enc_type', $server->enc_type);
$_ldapsync_ldap
->setOption('basedn', $server->basedn);
$_ldapsync_ldap
->setOption('user_attr', $server->user_attr);
$_ldapsync_ldap
->setOption('mail_attr', $server->mail_attr);
$_ldapsync_ldap
->setOption('puid_attr', $server->puid_attr);
$_ldapsync_ldap
->setOption('binary_puid', $server->binary_puid);
$_ldapsync_ldap
->setOption('binddn', $server->binddn);
$_ldapsync_ldap
->setOption('bindpw', $server->bindpw);
return $_ldapsync_ldap;
}
}
/**
* Statistics counter function to track summary info.
*
* @param string $type The statistics category, e.g. new_users, notices, etc.
* @param int $n number of users to add to category, defaults to 0.
* @return int current count of users in category;
*/
function ldapsync_stats($type, $n = 0) {
static $stats = array();
if (!isset($stats[$type])) {
$stats[$type] = 0;
}
$stats[$type] += $n;
return $stats[$type];
}
Functions
Name | Description |
---|---|
ldapsync_cron | Implements hook_cron(). |
ldapsync_help | Implementation of hook_help(). |
ldapsync_init | Implements hook_init(). |
ldapsync_menu | Implementation of hook_menu(). |
ldapsync_stats | Statistics counter function to track summary info. |
_ldapsync_init | Initiates the LDAPInterfase class. |
_ldapsync_process_entry | Take an ldap object entry and determine if there is an existing account or a new account needs to be created. |
_ldapsync_search | Find all LDAP users from servers and OUs specified in ldapauth settings and create or update existing users as needed. |
_ldapsync_sync | Main routine. |