Currently, LDAP module requries a root bind user.
Also, LDAP module wants to allow users to change their passwords through Drupal.

I'd like a much less featured LDAP module - one that only authenticated against the LDAP server and, if no user existed, would create a stub user if the main new account settings allowed it.

Changing LDAP password through drupal is nice but not necessary. There should be a way to turn this off.

That way, a bind user would be unneccessary (it's a security problem anyway) as we could bind with the user given.

Currently, I am using web-auth, which makes having drupal and ldap users coexist difficult.
It would be great to have a simple working LDAP auth only module.

Comments

rla’s picture

+1 votes for this feature. Just a config switch or something. Definitely don't want to have a write access binding account here. I only want to auth against AD LDAP. Users can change PW other ways.

deanypop’s picture

Category: feature » bug
Priority: Normal » Critical

+50, this is absolutely essential!

There is* this script:

http://cmeerw.org/notes/drupal_ldap.html

which is ldap auth-only... But it would be nice to have something full-featured, that could run the gamut and have tunable settings for 'how deep' the integration goes in terms of user profiles, mappings, etc.

billybp’s picture

Category: bug » feature

I too would really like to see a simple way to authenticate users via ldap - I am happy for drupal to store thier profie settings - but I really can not justify asking people to remember yet another password. I too am not worried about resetting passwords etc.

It does not look that complicated to implement - I would be very happy to work with someone on this fix. I am new to Drupal - but impressed with what I see so far.

I had a look at the xml-rpc script - it looks like it would meet my needs - but I do not think it has been updated to support 4.6

billybp’s picture

Currently, LDAP module requries a root bind user.
Also, LDAP module wants to allow users to change their passwords through Drupal.

I'd like a much less featured LDAP module - one that only authenticated against the LDAP server and, if no user existed, would create a stub user if the main new account settings allowed it.

Changing LDAP password through drupal is nice but not necessary. There should be a way to turn this off.

That way, a bind user would be unneccessary (it's a security problem anyway) as we could bind with the user given.

Currently, I am using web-auth, which makes having drupal and ldap users coexist difficult.
It would be great to have a simple working LDAP auth only module.

pablobm’s picture

Assigned: Unassigned » pablobm
Priority: Critical » Normal

Well, sort-of back from holidays, I think it's time to start replying some requests, like this one.

The original module, back to when I took over its maintenance, used to do what you ask for. But I was working on in on request of somebody else who had different needs, so I changed it to its original form.

That module only authentified each user once during their lifetime: the first time it logged on the system. Then it created a "stub user" within Drupal, that would work detached from the original LDAP user. Therefore, once every possible LDAP user logged in, the module was not needed anymore, provided that no new users joined the LDAP directory.

I understand that could be desiderable to the current implementation under some circumstances, so please let me have a look. I'm still sort-of on holidays, but don't worry ;) .

firebus’s picture

making stub users doesn't work when the user changes their password on the LDAP side (for example, if you're organization requires you to change paswords every X day for security reasons)

ideally, the LDAP auth module would auth everytime the user logged in.

i think that creating a user is still a desireable option if a user that doesn't exist yet in drupal is logging in for the first time.
i think it even makes sense to try to populate the new user with as much info as possible from the LDAP server.

but once the user exists, the admin should be able to change their info without worrying about the LDAP server overwriting it - the LDAP auth should only be password auth at this point.

and i agree with the above poster that it should not require a BIND user.

thanks!

rla’s picture

This would probably much to difficult to do, but what would be cool would be for the LDAP module to enumerate the Drupal roles and test the user for membership in the LDAP groups of the same names. Add the user to that role if they are a member of the LDAP group, remove them from roles from which they are not members. If the role exists in Drupal, but not in LDAP - ignore the add/drop for that role. Everyone who authenticates would be in the "authenticated user" role.

pablobm’s picture

I'll clarify here why and when WRITER and READER exist.

  1. First and foremost: requesting lost passwords. This action makes Drupal perform a "_load()" and a "_update()" which make the module both read and write, respectively, with, obviously, no means of getting a proper authentication from the user.

  2. Second: perform read and write operations on users' attributes.

The question that arises is: why using the same method in both cases?. Well, the other option would have been keeping users' passwords as session variables while they were logged on. Reasons against this:

  • Sounds to me like having two ways to attack. I prefer having only one possible entrance for intruders.
  • Complicates module, making necessary write code to tell apart when users are doing one thing or another, what is nothing straightforward due to the way Drupal manages this things.

And those are the ones I can recall now.

I think it would be easier and safer to create a different module, something like ldap_integration_simple, with less functionality, that responds to your requests. Please keep giving suggestions on the topic.

sfrancis’s picture

Here are some simple changes to allow you to decide whether or not you want users that are created by authenticating against LDAP to always use the LDAP directory for authentication - i.e. allowing you to change your Active Directory password (or whatever LDAP system you use) and have it also change the drupal login password. Or you can clear the check box and have it keep the current behaviour of creating an account with the same as the current LDAP password, but then having drupal/ldap maintain password info separately.

Note that the line numbers in the diff are related to the version of code that I sent in re making the LDAP module work reliably with AD (see http://drupal.org/node/18899), but they are not major changes, so should be easy to reassemble:

diff -u ldap_integration.module /srv/www/htdocs/modules/ldap_integration/ldap_integration.module
--- ldap_integration.module 2005-08-26 12:36:21.000000000 +0000
+++ /srv/www/htdocs/modules/ldap_integration/ldap_integration.module 2005-09-07 21:59:19.964294503 +0000
@@ -63,6 +63,7 @@

// Login format settings
$group = form_checkbox(t('Logins must be on the form user@server'), 'ldap_login_string_with_server', 1, variable_get('ldap_login_string_with_server', 1), t('Drupal requires login strings from external sources to be in the form user@server. Disabling this setting will circumvent such limitation.'));
+ $group .= form_checkbox(t('Accounts that are created by LDAP will only use LDAP as the authentication source.'), 'ldap_login_string_ext_auth_only', 1, variable_get('ldap_login_string_ext_auth_only', 1), t('If this box is checked, then accounts created from LDAP authentication will only use LDAP. Otherwise, their passwords in Drupal can be changed independently of the LDAP source.'));

@@ -317,37 +322,40 @@

function _ldap_integration_fake_user_login($edit = array(), $msg = '') {
global $user, $base_url;

// If we are already logged on, go to the user page instead.
if ($user->uid) {
drupal_goto('user');
}

if (user_deny('user', $edit['name'])) {
$error = t('The name %s has been denied access.', array('%s' => theme('placeholder', $edit['name'])));
}
else if ($edit['name'] && $edit['pass']) {
-
- if (!$user->uid) {
- $user = user_authenticate($edit['name'], $edit['pass']);
-
+ $name=$edit['name'];
+ $result = db_query("select name from {users} where locate('ldap_authentified',data) and name='%s'",$name);
+ if ((db_num_rows($result) != 1) || (variable_get('ldap_login_string_ext_auth_only', 1) == 0)) {
+//authenticate locally only if not initially authenticated by LDAP, or if configured to do so
+ if (!$user->uid) {
+ $user = user_authenticate($edit['name'], $edit['pass']);
+ }
+ }
// === HACK STARTS ===
- // If not succesful, try LDAP authentication directly
+ // If not succesful, or user initial authenticated by LDAP, try LDAP authentication directly
if (!$user->uid) {
- $user = _ldap_integration_code_added_user_login($edit['name'], $edit['pass']);
- }
+ $user = _ldap_integration_code_added_user_login($edit['name'], $edit['pass']);
+ }
// ==== HACK ENDS ====
- }

@@ -395,8 +403,9 @@
$at = '';
$server = '';
}

+
if (ldap_integration_auth($name, $pass, $server)) {
$account = user_load(array('name' => "$name$at$server"));
$tmp_user->name = "$name$at$server";
if (!$account->uid) { // Register this new user.
@@ -410,9 +419,9 @@
$dn = _ldap_integration_login2dn("$name$at$server");
$mail = $ldap->retrieveAttribute($dn, LDAP_EMAIL_ATTRIBUTE);
$user = user_save('', array('name' => "$name$at$server", 'pass' => $pass, 'mail' => $mail, 'init' => $mail, 'status' => 1, "authname_$module" => "$name$at$server", 'roles' => array(_user_authenticated_id()), 'ldap_authentified' => TRUE));
watchdog('user', t('New external user: %user using module %module.', array('%user' => theme('placeholder', $name .'@'. $server), '%module' => theme('placeholder', $module))), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $user->uid .'/edit'));
- }
+ } else $user = $account;
}

return $user;
}

pablobm’s picture

OK, finally I've been having a look at sfrancis's patch, but I'm not sure it this is really what was being requested.

sfrancis's patch, provided that the new functionality it implements is activated, denies login to users whose login//pass is fine to Drupal but not to LDAP. It's not even a question of precedence, but an absolute. Briefly:

  • LDAP login//pass match: login.
  • LDAP login//pass don't match: access denied.

I think that the original poster of this thread, firebus, wanted a different thing, working like this:

  • Drupal login//pass match: login.
  • Drupal login//pass don't match and LDAP does: create new user in Drupal's DB. It will be, from this moment on, absolutely independent from its LDAP counterpart (passwords can be made different).
  • Neither match: access denied.

Therefore, this means that, if user changes its Drupal password, its "LDAP sibling" must take no notice. This is not happening with sfrancis's patch.

So please, post your opinions on this :) .

sfrancis’s picture

You say "I think that the original poster of this thread, firebus, wanted a different thing, working like this:

* Drupal login//pass match: login.
* Drupal login//pass don't match and LDAP does: create new user in Drupal's DB. It will be, from this moment on, absolutely independent from its LDAP counterpart (passwords can be made different).
* Neither match: access denied."

However, this was the behaviour before - or at least it was for me after I got LDAP working with AD, so no request would have been necessary.
And I think the main point was that no one wants to have to manage 2 passwords, and change them in two places, or more importantly, have 2 places to have to remember to disable users' accounts on terminations.

"ideally, the LDAP auth module would auth everytime the user logged in." That is what this patch does.

I should let others speak for themselves - but this patch does what I wanted, and I think its the norm for corporate type places trying to get single authentication systems.

pablobm’s picture

Status: Active » Needs review

sfrancis's changes habe been commited to the module with some changes to improve code readability.

deanypop’s picture

I am the boss of firebus, and here's what I* want:

1) Drupal checks to see if user exists in drupalDB;

2) If user exists:

2a) check user/pass against drupalDB ->login on success

2b) on failure, check user/pass against LDAP -> login on success

3) If user does not exist:

3a) check user/pass against LDAP -> create drupalDB user and login on success

4) If none of the above, access is denied

Really, I want Drupal to abstract auth the way everyone else does, so that you can check Drupal users against DrupalDB/MySQL/LDAP/passwd file/etc. In such a situation, you could set the priority of each auth method in the admin.

But, for now, the above would be a great stopgap!

The problem with the existing plugin is that we could EASILY create duplicate users, or too-similar users that would confuse drupal (some folks have multiple 'cn' attributes, or even uids, and new users were getting created each login).

deanypop’s picture

One more addition:

Drupal/ldap-auth-module should check LDAP users to see if they have LDAP groups that match Drupal roles, and grant access to those roles within drupal.

In fact, I will place a bounty on my last two posts' combined featureset for $500.

Thanks!

-deano

billybp’s picture

My 2cents:

- If user authenticates using LDAP credentials - then Drupal does not need to offer a forgotton password option - it could offer a text area allowing the user to be advised regarding how their password can be reset e.g. contact HR etc.

- We are happy for a stub user to be created etc. but we would want the password to be checked from the LDAP directory every time the user authenticates.

My needs are based on - reducing the number of passwords our users need - and also having a single location for controlling user access to our systems.

pablobm’s picture

StatusFileSize
new22.48 KB

So, according to deanypop (#13), I had the right idea on what was wanted, and according to billybp (#15), it was sfrancis the right one. So both of us were right :) .

OK, I have already implemented that, but will release later this week, along with the groups/roles feature requested by deanypop (#14).

So, in next release login mode will be selected as a setting, as on the attached screenshot.

pablobm’s picture

Well, after some work on realising that my understanding of hook_user() was wrong and some more on figuring it out right, I come up with a new version adding the features requested above.

Please, test and enjoy. Look out for new settings on ldap_integration/conf.php.

(Er... did anyone say "bounty"?).

deanypop’s picture

Hrm. It looks* beautiful, but while my LDAP server (Apple Open Directory OS X 10.4) claims the user is successfully auth'd, the ldap module is returning "Invalid Credentials", and not letting me in. I have set neither of the bind DNs (read or write)... Are they required to make just regular auth work?

Thanks!

-deano

deanypop’s picture

Ack. I am a complete moron! I had a typo in the baseDN. It works!

Have to hammer on this a bit more, but definitely looks bountiable.

deanypop’s picture

Okay, I'm not sure if this is even possible, but I got the question, so...

How hard would it be to auth off of multiple LDAP trees? Is this module only capable of saying "check drupal, then check THIS LDAP ONLY", or can it (or multiple duplicate modules) chain off to serve LDAP 1, LDAP 2, LDAP 3, etc?

Just curious as to how hard that would be...

-deano

pablobm’s picture

Do you mean something like... this: http://drupal.org/node/33174?. Well, that would be trivial :) .

deanypop’s picture

It's definitely working, but I have a question on group->role mapping...

I can't get this to work, and I know I'm just underinformed about how to set it up.

I need to specify a role in Drupal to associate with nodes, right?

Therefore, there's going to be a role... Should that map to the shortname of the group? The gid? Something else?

Also, I don't* want to add people to the roles, because then I won't be using the group at all - it'll just see the drupal role is set, and use that (right?).

So, how do I set up the groups functionality? Are any of the additional conf settings needed (admin read/write) for this to work?

Thanks!

deanypop’s picture

Ack, sorry for all the questions, but does ldap_integration interfere with the other auth modules (auth_mysql, livejournal, etc)?

Thanks,

-deano

tracstarr’s picture

just tried this out and it appears there are some blank page issues running it on PHP5. I am able to enable the module, but when i click on settings i just get a blank page.

tracstarr’s picture

seems the above is an issue in the CVS branch and not in 4.6.3

pablobm’s picture

About deanypop on #22, I don't understand what you mean, so I'll try to explain everything here.

When a user logs in, the module checks the settings concerning the groups (those in the "From LDAP groups to Drupal roles" section), and signs the user in any existing roles with the same name.

So, for example, say that a LDAP user has a multivalued attribute named "group", its values are "admins", "users" and "bosses". Drupal (actually, this LDAP module) is instructed, through the settings, to read that multivalued attribute and understand its values as roles.

Say now that the Drupal installation has several roles configured, named "admins", "users" and "gods". At login, the user will be included in those roles matching values for that multivalued attribute. Therefore, the user will have the "admins" and "users" roles, and not the "bosses" or "gods" roles.

Well, I hope this one is understandable or, at least, that we are converging into a common vocabulary.

pablobm’s picture

About deanypop's #23. Short answer: not that I know.

Long answer: no if module writers don't perform any stunt.

Due to Drupal 4.6's big lack of means to build a 100% proper authentication module (or to my moronic inability to find them), this module implements a hack which may lead to problems with other modules.

The hack is just a substitution of a callback originally defined in user.module (part of Drupal's core) by another callback I define in zcallbacks.module. The 'Z' at the beginning makes Drupal to execute it last, after user.module, so the substitution can be done in the right order.

What does this mean?. Well, if any module tries a similar hack, or relies in any other undocumented behaviour, things may happen. But as far as I can tell, nobody has yet had a problem. And the module's current codebase was first released back in January 2005.

rla’s picture

Pablo, this module is coming along very nicely! Seems to working well - I do have a couple thoughts though.

Regarding the groups from LDAP; Active Directory returns the groups a person belongs to as a full CN, often a long string. For example, one of our groups looks like this:

CN=DeptTST,OU=Departmental,OU=Groups,DC=myorg,DC=mytld

this is too long a string for Drupal's role names (I think the db limits it to 32 characters). I wonder if it would be possible to provide a regular expression for extracting the group name? For example, I'd like the Drupal role to be "DeptTST".

Also, what happens if the person is removed from the LDAP group? Do they get removed from the Drupal role next time they log in or do they keep it? Seems like this would be tricky. How would you know if the role is derived from a LDAP group or one assigned outside LDAP?

Maybe having a LDAP group/Drupal role mapping table (like the attribute mappings) would solve both problems? You could map the long group name to short Drupal name and loop over the list of mappings to decide which ones to remove the user from if they no longer belong to that LDAP group.

pablobm’s picture

Active Directory returns the groups a person belongs to as a full CN, often a long string. For example, one of our groups looks like this:

CN=DeptTST,OU=Departmental,OU=Groups,DC=myorg,DC=mytld

this is too long a string for Drupal's role names (I think the db limits it to 32 characters). I wonder if it would be possible to provide a regular expression for extracting the group name? For example, I'd like the Drupal role to be "DeptTST".

Mmmm, is the leftmost value always the desired one or may it change?. Just wondering in case we can avoid of one more regexp.

Also, what happens if the person is removed from the LDAP group? Do they get removed from the Drupal role next time they log in or do they keep it? Seems like this would be tricky. How would you know if the role is derived from a LDAP group or one assigned outside LDAP?

You got it right. That's why it is not implemented.

Maybe having a LDAP group/Drupal role mapping table (like the attribute mappings) would solve both problems? You could map the long group name to short Drupal name and loop over the list of mappings to decide which ones to remove the user from if they no longer belong to that LDAP group.

Looks right to me. Please hang on a bit while I polish some other stuff. (Or go to function ldap_integration_user_login() and start contributing ;) ).

rla’s picture

At least in the case of AD, I think the leftmost value is the one you want. Since that value is AD group name and AD groups names must be unique (regardless of tree location), it should always be unique.

I'll have a look at ldap_integration_user_login() - I know php, but not some of the Drupal specific stuff. I will take try at it.

pablobm’s picture

Just to avoid mixing things, I've created a new thread on the groups/roles issue at "Map LDAP groups to Drupal roles". So let's move that sub-thread there from now on.

pablobm’s picture

Status: Needs review » Fixed

Well, this has been quiet for a while, so I think I can consider it fixed, can't I?

Anonymous’s picture

Status: Fixed » Closed (fixed)