There should be an option to automatically update Drupal user information with data from LDAP. Ideally, the Queue API used and frequency optional.

Would simply resaving every LDAP user account work? Is something else required or is there a better method?

Yes, I know, patches are accepted. I'm willing (and may find time) to write the patch, but want some direction first.

Comments

ShaunDychko’s picture

Doesn't cron take care of this? The number of users to check is configured at /admin/config/people/ldap/user

13rac1’s picture

13rac1’s picture

The existing code checks for users deleted from LDAP, but it doesn't update existing user data.

johnbarclay’s picture

Title: Update Drupal Users with LDAP data automatically » LDAP User: Update Drupal Users with LDAP data automatically
Status: Active » Postponed

Simply resaving would work when service accounts are used for binding to ldap. Ldap feeds would be a more appropriate way to do this. I believe this should wait for 8.x version if its implemented in ldap_user.

13rac1’s picture

How would it work in LDAP Feeds? I'm willing to write this. Putting it in LDAP user is the only place it made sense though. Add everyone once a week (adjustable) to the cron queue to be resaved.

vgalindus’s picture

Issue summary: View changes

Hi john, what is the status for that issue? why do you think it should be implemented in ldap feeds instead of ldap_user, to me it looks more logic to place it in ldap_user since it reduces the number of dependencies (ldap query, ldap feeds).

We are considering to implement this feature to reduce log in times.

\BR

mausolos’s picture

I wrote a simple function that we will trigger either with a button click in an admin panel or call with a cron job.

It adds or syncs all Drupal accounts against AD via LDAP, and it saves the usernames it extracts from LDAP into an array as it goes.

Then, if you initiate the function with $drupal_sync = TRUE, it does a query of all drupal users with the LDAP role (all users created in Drupal via LDAP are assigned the LDAP role) and puts those into a second, simple array.

Finally, a third array is created via array_diff(), and the resulting array is iterated through, cancelling the Drupal accounts in the process.

The function also allows you to tell it whether it's called via cron or not, and generates results output accordingly.

I'm putting some finishing details on it, but I'll consider sharing it when I'm done, if you're interested.

zanix’s picture

I would like to see your solution mausolos

mausolos’s picture

NOTES:
- This is pretty slow and takes a lot of memory. It could probably be optimized. I would LOVE it if people pointed out things I could/should do different or better. Thank you in advance!
- Some of our fields need additional processing. For these, you can write another function and pass $user_final to it, extract, modify and save them using entity_metadata_wrapper technique:

$user_wrapper = entity_metadata_wrapper('user', $drupal_user);
$user_wrapper->field_fieldname->set("my value");
$user_wrapper->save();

- Some of our fields are specifically formatted in DN format. To break the useful data out, we employ a function similar to the one described here, by Renoir Boulanger: http://www.php.net/manual/en/function.ldap-explode-dn.php
- We plan to optimize this by adding the "whenchanged" attribute (might be different in your system). This would allow us to patrol/synch a much more limited set of users, and thus increase performance. It's not perfect.
- I have not finished full unit testing, use at your own risk and only on a dev system!!!


function MYMODULE_provision_users_main($drupal_sync = TRUE, $called_by_cron = FALSE) {
 $users_added = 0;
 $users_synched = 0;
 $users_deleted = 0;
 $ldap_usernames = array();

 // Returns a list of available query objects
 $ldap_queries = ldap_query_get_queries(NULL, 'enabled');
 $ldap_queries = array_keys($regions_list_query);

 foreach($ldap_queries as $ldap_query) {
  // Now we actually run the queries that we're interested in
  $lquery = ldap_query_get_queries($region, 'enabled', TRUE);
  $ldap_users = $lquery->query();
  foreach($ldap_users as $ldap_user) {
   $added_a_user = 0;
   $synched_a_user = 0;
   // plain string username
   if( !empty($ldap_user['cn'][0]) ) {
    $ldap_username = $ldap_user['cn'][0];
    array_push($ldap_usernames, $ldap_username);
    // attempt to load the user, thus verifying if the user account exists in Drupal yet or not.
    // if it doesn't, create an empty user account defined by just the username.
    if(!$drup_user = user_load_by_name($ldap_username)) {
     $drup_user = user_save('', array('name' => $ldap_username));
     $added_a_user++;
    } else {
     $synched_a_user++;
    }
    $ldap_user_conf = ldap_user_conf('default', TRUE);
    $user_edit = array('name' => $ldap_username, 'enabled' => 1);
    if( !$user_final = $ldap_user_conf->synchToDrupalAccount($drup_user, $user_edit, LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, NULL, TRUE) ) {
    } else {
     // save the user
     $users_added += $added_a_user;
     $users_synched += $synched_a_user;
     user_save($user_final);
     
     // we created an "ldap" role, with id 4, to keep track of which users are ldap to begin with. this allows us to support other non-ldap users if we like, administrator for example
     ldap_authorizations_user_authorizations($user_final, 'set', 'drupal_role', 'logon');
    }
   }
  }
 }

 if($drupal_sync == TRUE) {
  $query = "SELECT users.name AS users_name FROM {users} users INNER JOIN {users_roles} users_roles ON users.uid = users_roles.uid WHERE (( (users.status <> '0') AND (users_roles.rid = '4') ))";
  $result = db_query($query);
  $drupal_ldap_users = $result->fetchCol();

  // give me just the users that are in Drupal, have the LDAP role, AND cannot be found in AD
  $cull_users = array_diff($drupal_ldap_users, $ldap_usernames);
  if( sizeof($cull_users) > 0) {
   $cull_uids = array();
   foreach($cull_users as $cull_user) {
    $doomed = user_load_by_name($cull_user);
    $cull_uids[] = $doomed->uid;
   }
   user_delete_multiple($cull_uids);
  }
 }
 $basic_message = "\nUsers added: " . $users_added . "\nUsers synched: " . $users_synched . "\nUsers deleted:" . $users_deleted . "\n\n";
 if($called_by_cron == TRUE) {
  echo $basic_message;
 } else {
  if($users_added + $users_synched + $users_deleted > 0) {
   $users_added_suffix = $users_added == 1 ? "" : "s";
   $users_synched_suffix = $users_synched == 1 ? "" : "s";
   $users_deleted_suffix = $users_deleted == 1 ? "" : "s";
   $outmsg = 'LDAP to Drupal users updated: Added ' . $users_added . $users_added_suffix;
   $outmsg .=', synched ' . $users_synched . $users_synched_suffix;
   $outmsg .=' and deleted ' . $users_deleted . $users_deleted_suffix . '.';
   drupal_set_message(t($outmsg, 'status'));
  }
 }
 watchdog('LDAP-synch', t($basic_message), NULL, WATCHDOG_NOTICE);
}

johnbarclay’s picture

@#6. The rationale for using feeds is basically this:

Its a generic integration tool for synching A to B with a good architecture of hooks and the ability to deal with a variety of field types. By using a more generic, heavily used tool we don't have to deal with all the use cases that are not specific to ldap (eg. binary fields, cron and batch processes, etc.) If every drupal integration module wrote their own synching interface for their particular data source it would be a big mess. And thats only if we consider integration between drupal and other data sources. When you get into permutations of other data sources it gets even messier.

Take a look at feeds and its architecture and give it a try. Once you get used to it, you'll find its a much better model that recreating the wheel. With features/exportables you can make feeds a dependency and create the feature (specific configuration) for the end user rather than creating whole new integration modules.

13rac1’s picture

@johnbarclay Thanks John.

I've made a staff directory (View with images and exposed filters) and Feeds seems to be handling the user import correctly. Users are connecting to their account when logged in and all of the data is there. The only frustrating part is that I think I'll need to map all of the user fields twice. Once in LDAP Users and once in Feeds, so both can check for user data changes. It's not really a problem though. Once you set it up it... it's done. If don't have many users, you could also "Skip hash check" in Feeds to update every user on cron.

Can someone else try this method? Maybe we can just write some docs and close this issue...

larowlan’s picture

Status: Postponed » Closed (won't fix)

No update in > 12 months, no patches - closing