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
parent
23e28ee659
commit
264a94d4e7
|
@ -61,6 +61,7 @@ authentication_backend:
|
||||||
# The groups filter used for retrieving groups of a given user.
|
# The groups filter used for retrieving groups of a given user.
|
||||||
# {0} is a matcher replaced by username.
|
# {0} is a matcher replaced by username.
|
||||||
# {dn} is a matcher replaced by user DN.
|
# {dn} is a matcher replaced by user DN.
|
||||||
|
# {uid} is a matcher replaced by user uid.
|
||||||
# 'member={dn}' by default.
|
# 'member={dn}' by default.
|
||||||
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ authentication_backend:
|
||||||
# The groups filter used for retrieving groups of a given user.
|
# The groups filter used for retrieving groups of a given user.
|
||||||
# {0} is a matcher replaced by username.
|
# {0} is a matcher replaced by username.
|
||||||
# {dn} is a matcher replaced by user DN.
|
# {dn} is a matcher replaced by user DN.
|
||||||
|
# {uid} is a matcher replaced by user uid.
|
||||||
# 'member={dn}' by default.
|
# 'member={dn}' by default.
|
||||||
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
||||||
|
|
||||||
|
|
|
@ -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 () {
|
it("should retrieve mail from custom attribute", function () {
|
||||||
const USER_DN = "cn=user1,ou=users,dc=example,dc=com";
|
const USER_DN = "cn=user1,ou=users,dc=example,dc=com";
|
||||||
const options: LdapConfiguration = {
|
const options: LdapConfiguration = {
|
||||||
|
|
|
@ -61,6 +61,12 @@ export class Session implements ISession {
|
||||||
return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN));
|
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);
|
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 that = this;
|
||||||
const filter = this.options.users_filter.replace("{0}", username);
|
const filter = this.options.users_filter.replace("{0}", username);
|
||||||
this.logger.debug("Computed users filter is %s", filter);
|
this.logger.debug("Computed users filter is %s", filter);
|
||||||
const query = {
|
const query = {
|
||||||
scope: "sub",
|
scope: "sub",
|
||||||
sizeLimit: 1,
|
sizeLimit: 1,
|
||||||
attributes: ["dn"],
|
attributes: [attribute],
|
||||||
filter: filter
|
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)
|
return that.connector.searchAsync(this.usersSearchBase, query)
|
||||||
.then(function (users: { dn: string }[]) {
|
.then(function (users: { [attribute: string]: string }[]) {
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn);
|
that.logger.debug("LDAP: retrieved user %s is %s", attribute, users[0][attribute]);
|
||||||
return BluebirdPromise.resolve(users[0].dn);
|
return BluebirdPromise.resolve(users[0][attribute]);
|
||||||
}
|
}
|
||||||
return BluebirdPromise.reject(new Error(
|
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[]> {
|
searchEmails(username: string): BluebirdPromise<string[]> {
|
||||||
const that = this;
|
const that = this;
|
||||||
const query = {
|
const query = {
|
||||||
|
|
Loading…
Reference in New Issue