a usernames cn is

cn=jane porter,.....
drupal username is jane porter

user gets married
drupal username is jane smith
cn becomes
cn=jane porter,.....

logs in again.
-- how is user preserved
-- how is username changed

current thinking:
- admin configures true ldap id and is stored in authmap
- when cn changes, username may change and username are tied together by whatever true id is.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

micahw156’s picture

+1

I've encountered this problem in our environment. Usernames can change, but we also store a guid (in eDirectory workforceID, but it could be anything) that never changes for a user. If the Drupal LDAP auth was mapped to tie Drupal uid to the guid attribute instead of using usernames, this wouldn't be a problem. Existing behavior should probably be the default, but this would be a sweet option.

thatoneguy’s picture

I am confused by #1. You say "cn becomes" and then put the same value. It looks like you mean dn and not cn. But did you mean dn becomes cn=jane smith,... or did you really mean the dn stays the same?

johnbarclay’s picture

yeah. the cn and dn would change. these are some notes from a skype call among ldap developers. If you are up for it, can you rewrite this issue as a new issue and I can close this one. More clarity seems needed.

thatoneguy’s picture

I'd be happy to rewrite this, but I'm still not sure I entirely understand this. So the problem is that changing the user name or DN may break synchronization but we still want to be able to change the user name or DN, right? So we can set up users to map based on a separate attribute that does not change and does not need to be the same as the user name. Is that it?

As a side-node, there has been quite a bit of discussion regarding the potential removal of authmap at #817118: Remove {authmap} and migrate OpenID entries to their own table; particularly for Drupal 8 but even so, probably best to move away from it. Several other modules already have implemented their own tables for mapping.

johnbarclay’s picture

Version: 7.x-1.x-dev » 7.x-2.x-dev
johnbarclay’s picture

Title: Use Case: ldap cn is not unique, unalterable user attribute » Use Case: ldap attribute for deriving email or username is not unique, unalterable user attribute
Version: 7.x-2.x-dev » 7.x-1.x-dev
Assigned: Unassigned » johnbarclay

Made some progress on this. Here is the featureset I believe is related to this

1. ability to specify unique, persistent attribute such as uid in ldap server user configuration
2. ability to prevent conflicts between drupal and ldap usernames
3. ability to logon with non uid attribute (mail, cn, etc) and have accounts reconciled. that is, if their mail, cn, etc has changed and is mapped to their drupal account, change the name of their drupal account but don't create a duplicate drupal account.

To deal with this correctly:

- in ldap server interface, allow specification of unique attribute
- add tokens for mapping of ldap attributes to username, email, and unique id
- allow admins to specify drupal accounts in mixed mode should not conflict with existing users.

I've taken care of the token system and some of the UI stuff, but that is all so far.

mrryanjohnston’s picture

Subscribing

johnbarclay’s picture

This code also needs to deal with #1317704: same uid on two different ldap servers able to log into drupal and claim same drupal user. case where derived username is not unique.

Not sure what the username would be or if both users should simply be disallowed. Here are some approaches.

  1. Both users be disallowed
  2. The second user be disallowed
  3. The second user be forced to have an automated drupal username such as [uid]1,[uid]2, etc
  4. Have users logon with domain\username as they would in Active Directory and have domain mapped to a give server

The domain\username seems most workable and desireable. If a field was in the server setup for "domain tag", the instructions for logging in could be altered to say use "domainx\username or domainy\username" to login.

zeezhao’s picture

Hi John. Re #8, I would vote for both solutions 3 & 4 but with option of deciding in drupal ldap setup screen.

Probably need a means of storing a unique token for the user based on username + domain.

Solution 3 enables users to login automatically without worrying about domains...

johnbarclay’s picture

User interface and crud is done for this. The following needs to be done. "uid" is changeable user id such as uid or samaccountname in ldap. "puid" is the permanent user id in ldap. I think #8 should remain a separate issue #1317704: same uid on two different ldap servers able to log into drupal and claim same drupal user..

Need to

  1. store puid when user is provisioned. should be part of user provision function so useful via continuous provisioning models.
  2. check when user authenticates or batch function is called (for continuous provisioning) for uid changes,
  3. change drupal username and uid when change is found.
  4. add checkbox for whether puid is unique across all ldaps or per ldap server. In the case of per ldap server, will need to be prefixed with server id.

Simpletest 1

  1. Creating a user based on a record in the mock/test ldap.
  2. Changing the alterable uid in the mock ldap, relogging in.
  3. Test would be for user retaining same drupal uid and roles.
  4. Test for basic storage.

Simpletest 2 (optional).

  1. Create a user based on a record in the mock/test ldap where puid is specific to one ldap.
  2. Attempt to create a puid with a second mock ldap and make sure server id prefixing works.
  3. logon on second server and make sure prefixed puids allow for separate drupal users.
zeezhao’s picture

Thanks for the update. By the way, is this back-ported to D6, or is it for D7 only? Thanks.

johnbarclay’s picture

only d7.

johnbarclay’s picture

Version: 7.x-1.x-dev » 7.x-2.x-dev
cgmonroe’s picture

I'm looking at solving this for the 6.x ldap_integration and an SSO solution I'm working on. So I thought I'd chime in here with some of my plans/thought on how I'm considering implementing this.

Re: PUID

One thing not mentioned here is the fact that most LDAP servers are moving to implement RFC 4530's entryUUID operational attribute to solve this problem. Even the server who haven't done this yet, generally have an attribute that duplicates this functionality. Here's a 2010 list I found for the "guid" attributes for the most common LDAP servers:

entryUUID - OpenLDAP, OpenDS/OpenDJ, Apache DS
objectGUID - Microsoft Active Directory (Note: this is a "binary string" that needs to be converted to hex).
GUID - Novell eDirectory
ibm-entryUuid - IBM Tivoli DS

So, it seems like LDAP is supplying the "PUID" value, it just needs to be settable in the admin screens. Of course site specific attributes (like employeeNumber / serialNumber) could be used as well.

One "Con" of using "entryUUID" is that if your backup/migrate to new server processes do not include the entryUUID values, new ones will be generated and the mapping broken.

Re: Storing GUIDs

This could be added to the user data, but then you can't easily do a query to find the matching user. IMHO, this really needs to be in a query-able column so uid can quickly be found from the LDAP GUID.

One spot might be the init column in the user table which the bakery module folks have pointed out is not used by the core anymore (and they redefined it). Easy, but probably not the best spot and since bakery has already redefined it once.

IMHO, it will require an ldapauth_users table with columns like: luid, uid, sid, machine_name, dn, guid

Note, I include machine_name because of some 6.x work on supporting features and the need to do cross site / DB independent server look ups which sid is not.

Re: Name conflicts

A "Golden Rule" that I've been working on in the 6.x version is:

All new Drupal users will all have unique name and email values.

This assumptions is embedded in a lot of Drupal core (like password recovery, registration, and the like).

Currently, the code in 6.x will just deny creation of users who don't meet this criteria. Some thoughts on "fixing" this constraint were:

Adding a drupal_alter hook like hook_ldapauth_account( &$account, $op, $sid, $dn), to allow site specific user renaming via custom modules. The ldapauth logic would be:

- If a valid LDAP user with no Drupal id, but the username is taken (and the LDAP user e-mail is unique) is found THEN.
- Create the user object but before allowing it to be saved, call drupal_alter('ldapauth_name', $account, 'duplicate_name', $sid, $dn).
- Then test the result that comes back for valid name/email.
- Create user or error.

This will let custom site specific modules define what to do if a duplicate ldap user name is found rather than trying to have a one size fits all solution.

In addition, there are other possible $op functions that could be defined/add as needed, like 'duplicate_email' or 'create' and the like. There are a couple of places in the code that would benefit from letting site specific alteration happen.

That said, I like the "domain" idea as a default implementation of this and would suggest that the server "machine_name" (implemented for features support in 6.x) would be a good candidate for defining "domain" names.

johnbarclay’s picture

thanks for chiming in on this. The fields ended up being puid, puid attribute and current dn and for entity field queries, i put them all in their own fields. This worked out well. Its all in the 7.x-2.0 branch. the puid attribute is what the admin configures to be the guid. the puid field is the guid. I store both in case the admin decides to do something dumb.

johnbarclay’s picture

Here's a screenshot of the server config from the 7.x-2.0 branch with the fields on it. I highlighted the fields that have changed since 6.x- version.

johnbarclay’s picture

Title: Use Case: ldap attribute for deriving email or username is not unique, unalterable user attribute » LDAP User: Use Case: ldap attribute for deriving email or username is not unique, unalterable user attribute
Issue tags: -D7 stable release blocker +7.x-2.0 release blocker
johnbarclay’s picture

cgmonroe’s picture

FYI: Edit was to correct common function code. Cache was using new name as key instead of original name.

FYI the latest release of the D6-only ldap_integration includes a hook to handle this "need different names" situation. Here's the hook definition from the ldapauth_api.php doc:

/**
 * Called after LDAP user has been authenticated but before the drupal
 * user mapping/creation done.
 *
 * Allows modules to alter the drupal account name that maps to an ldap account.
 * For example, adding a prefix or suffix based on server.
 *
 * @param String $name The name to alter
 * @param LDAPInterface $ldap LDAP server interface object bound to server as ldap user.
 * @param String $dn The DN for the authenticated user
 */
function hook_ldap_drupal_user_name_alter( &$name, $ldap, $dn ) {
  // Some example code to add the server machine name to the drupal name
  // E.g. LDAP user on server AD1 with sAMAccount=jsmith will map to drupal
  // user AD1-jsmith.  While LDAP user on server OL1 with uid=jsmith will
  // map to drupal user OL1-jsmith.
  $server = ldapauth_server_load($ldap->getOption('sid'));
  $name = $server->machine_name . "-" . $name;
}

The all ldap_integration modules always calls the common function below anytime a Drupal user name is needed:

/**
 * Allow modules to do site dependent login form name to drupal account name
 * mapping.
 *
 * Calls the hook_ldap_drupal_user_name_alter function(s) once per name.
 *
 * @param String $name Name entered on login form (case insensitive match to user attr).
 * @param LDAPInterface $ldap Fully initialized LDAP server interface object
 * @param boolean $reset If true the cache will be cleared.
 * @param String $dn The DN that has been authenticated with LDAP.
 */
function ldapauth_drupal_user_name( $name, $ldap, $dn, $reset=FALSE ) {
  static $drupal_names = array();

  if ( $reset ) {
    $drupal_name = array();
  }
  if ( ! $name ) {  // Allow reset with no lookup.
    return FALSE;
  }

  if ( ! isset($drupal_names[$name])) {
    $new_name = $name;
    drupal_alter('ldap_drupal_user_name', $new_name, $ldap, $dn);
    $drupal_names[$name] = $new_name;
  }
  return $drupal_names[$name];
}
johnbarclay’s picture

I'll try to shape the hook the same. Thanks.

johnbarclay’s picture

Status: Active » Needs review
FileSize
11.06 KB

Finally got to this one. Definately looking for testing and feedback on this.

I implemented this with using a couple methods on ldap_server class:

  • derivePuidFromLdapEntry($user_ldap_entry)
  • drupalUserFromPuid($puid)

both of which were already there, but had bugs.

The LdapUserConf->entryToUserEdit has a hook to allow alteration of all the Drupal user attributes (hook_ldap_user_edit_user_alter), so I didn't end up implementing the hook_ldap_drupal_user_name_alter

This is committed to 7.x-2.x-dev and has simpletest coverage.

johnbarclay’s picture

Status: Needs review » Fixed

The hook in #19 hook_ldap_user_edit_user_alter() and is described in ldap_user.api.php

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

michielnugter’s picture

Status: Closed (fixed) » Needs work

I think this issue or a related issue caused a problem with creating a new user.

In the file ldap_authentication.inc (line 449-456):

      $account = $ldap_server->userUserEntityFromPuid($puid);
      if ($account) {
        $account_exists = TRUE;
        $user_edit['name'] = $name;
        $account = user_save($account, $user_edit, 'ldap_user');
        user_set_authmaps($account, array("authname_ldap_user" => $name));
      }
    }

Uses the $name variable, this variable is not defined anymore in the function. This has been changed to $authname with a comment referring to issue #1599632: LDAP User: Support $account->name being different to the users login id thru use of Persistent and Unique User Attribute.

There is an attempt to not use the entered username anymore when creating a new user. I think it at least should be changed to $authname or properly implement the getting the username from the LDAP user settings.

michielnugter’s picture

It seems that $name should be replaced to $accountname, this is filled with the accountname based on the settings in the user configuration

johnbarclay’s picture

Status: Needs work » Needs review

Yes. There should be no $name variable at all in http://drupalcode.org/project/ldap.git/blob/refs/heads/7.x-2.x:/ldap_aut...

Here is what the 2 variables mean:
135 $authname = $form_state['values']['name']; // $authname is the name the user is authenticated with from the logon form // patch 1599632
136 $accountname = $authname; // $accountname is used as the drupal account name $account->name property.

The authmaps on line 446 should use $authname as on line 456.

        $account_exists = TRUE;
        $user_edit['name'] = $accountname;
        $account = user_save($account, $user_edit, 'ldap_user');
-       user_set_authmaps($account, array("authname_ldap_user" => $authname));
+       user_set_authmaps($account, array("authname_ldap_user" => $authname));

I'll push this out to -dev shortly. Thanks.

johnbarclay’s picture

Category: feature » bug
johnbarclay’s picture

Category: bug » feature
Status: Needs review » Fixed

Status: Fixed » Closed (fixed)
Issue tags: -7.x-2.0 release blocker

Automatically closed -- issue fixed for 2 weeks with no activity.