Add ability to search for groups using {uid}

On some LDAP servers, the `uid` attribute is more like a guid, while the
username exists instead in a dedicated field, like `username`. This
means the `uid` is not necessarily equal to `username`.

This is allows referencing using the `uid` to search for groups in the same
way as `dn` so that one can explicitly match the `memberuid` to the `uid` for
the user without the assumptions that come with using `{0}`.
pull/329/head
ViViDboarder 2019-02-27 21:34:18 -08:00
parent 23e28ee659
commit 264a94d4e7
4 changed files with 66 additions and 7 deletions

View File

@ -61,6 +61,7 @@ authentication_backend:
# The groups filter used for retrieving groups of a given user.
# {0} is a matcher replaced by username.
# {dn} is a matcher replaced by user DN.
# {uid} is a matcher replaced by user uid.
# 'member={dn}' by default.
groups_filter: (&(member={dn})(objectclass=groupOfNames))

View File

@ -54,6 +54,7 @@ authentication_backend:
# The groups filter used for retrieving groups of a given user.
# {0} is a matcher replaced by username.
# {dn} is a matcher replaced by user DN.
# {uid} is a matcher replaced by user uid.
# 'member={dn}' by default.
groups_filter: (&(member={dn})(objectclass=groupOfNames))

View File

@ -83,6 +83,49 @@ describe("ldap/Session", function () {
});
});
it("should replace {uid} by user uid when searching for groups in LDAP", function () {
const USER_UID = "user1";
const options: LdapConfiguration = {
url: "ldap://ldap",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "cn={0}",
groups_filter: "member=cn={uid},ou=users,dc=example,dc=com",
group_name_attribute: "cn",
mail_attribute: "mail",
user: "cn=admin,dc=example,dc=com",
password: "password"
};
const ldapClient = new ConnectorStub();
// Retrieve user DN
ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
scope: "sub",
sizeLimit: 1,
attributes: ["uid"],
filter: "cn=user1"
}).returns(BluebirdPromise.resolve([{
uid: USER_UID
}]));
// Retrieve groups
ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", {
scope: "sub",
attributes: ["cn"],
filter: "member=cn=user1,ou=users,dc=example,dc=com"
}).returns(BluebirdPromise.resolve([{
cn: "group1"
}]));
const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston);
return client.searchGroups("user1")
.then(function (groups: string[]) {
Assert.deepEqual(groups, ["group1"]);
});
});
it("should retrieve mail from custom attribute", function () {
const USER_DN = "cn=user1,ou=users,dc=example,dc=com";
const options: LdapConfiguration = {

View File

@ -61,6 +61,12 @@ export class Session implements ISession {
return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN));
});
}
else if (userGroupsFilter.indexOf("{uid}") > 0) {
return this.searchUserUid(username)
.then(function (userUid: string) {
return BluebirdPromise.resolve(userGroupsFilter.replace("{uid}", userUid));
});
}
return BluebirdPromise.resolve(userGroupsFilter);
}
@ -83,29 +89,37 @@ export class Session implements ISession {
});
}
searchUserDn(username: string): BluebirdPromise<string> {
searchUserAttribute(username: string, attribute: string): BluebirdPromise<string> {
const that = this;
const filter = this.options.users_filter.replace("{0}", username);
this.logger.debug("Computed users filter is %s", filter);
const query = {
scope: "sub",
sizeLimit: 1,
attributes: ["dn"],
attributes: [attribute],
filter: filter
};
that.logger.debug("LDAP: searching for user dn of %s", username);
that.logger.debug("LDAP: searching for user %s of %s", attribute, username);
return that.connector.searchAsync(this.usersSearchBase, query)
.then(function (users: { dn: string }[]) {
.then(function (users: { [attribute: string]: string }[]) {
if (users.length > 0) {
that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn);
return BluebirdPromise.resolve(users[0].dn);
that.logger.debug("LDAP: retrieved user %s is %s", attribute, users[0][attribute]);
return BluebirdPromise.resolve(users[0][attribute]);
}
return BluebirdPromise.reject(new Error(
Util.format("No user DN found for user '%s'", username)));
Util.format("No user %s found for user '%s'", attribute, username)));
});
}
searchUserDn(username: string): BluebirdPromise<string> {
return this.searchUserAttribute(username, "dn");
}
searchUserUid(username: string): BluebirdPromise<string> {
return this.searchUserAttribute(username, "uid");
}
searchEmails(username: string): BluebirdPromise<string[]> {
const that = this;
const query = {