Skip to content

Commit 8ae1c73

Browse files
committed
feat(ldap): allow specifying multiple attributes on username input
In some use-cases, one would like to login using either their username or email. Administrators now don't have to choose a single field but may specify multiple fields to count as "username". This change is backwards-compatible. Signed-off-by: Yarden Shoham <[email protected]>
1 parent 6216f7c commit 8ae1c73

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

connector/ldap/ldap.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ import (
3434
// bindDN: uid=serviceaccount,cn=users,dc=example,dc=com
3535
// bindPW: password
3636
// userSearch:
37-
// # Would translate to the query "(&(objectClass=person)(uid=<username>))"
37+
// # Would translate to the query "(&(objectClass=person)(!(uid=<username>)|(mail=<username>)))"
3838
// baseDN: cn=users,dc=example,dc=com
3939
// filter: "(objectClass=person)"
40-
// username: uid
40+
// username: uid,mail
4141
// idAttr: uid
4242
// emailAttr: mail
4343
// nameAttr: name
@@ -108,8 +108,8 @@ type Config struct {
108108
// Optional filter to apply when searching the directory. For example "(objectClass=person)"
109109
Filter string `json:"filter"`
110110

111-
// Attribute to match against the inputted username. This will be translated and combined
112-
// with the other filter as "(<attr>=<username>)".
111+
// Attributes (comma-separated) to match (OR)against the inputted username. This will be translated and combined
112+
// with the other filter as "(!(<attr1>=<username>)|(<attr2>=<username>))".
113113
Username string `json:"username"`
114114

115115
// Can either be:
@@ -414,7 +414,21 @@ func (c *ldapConnector) identityFromEntry(user ldap.Entry) (ident connector.Iden
414414
}
415415

416416
func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.Entry, found bool, err error) {
417-
filter := fmt.Sprintf("(%s=%s)", c.UserSearch.Username, ldap.EscapeFilter(username))
417+
var filter string
418+
escapedUsername := ldap.EscapeFilter(username)
419+
420+
// Split username attribute by comma to support multiple search attributes
421+
usernameAttrs := strings.Split(c.UserSearch.Username, ",")
422+
423+
attrFilters := make([]string, 0, len(usernameAttrs))
424+
for _, attr := range usernameAttrs {
425+
attr = strings.TrimSpace(attr)
426+
if attr != "" {
427+
attrFilters = append(attrFilters, fmt.Sprintf("(%s=%s)", attr, escapedUsername))
428+
}
429+
}
430+
filter = fmt.Sprintf("(|%s)", strings.Join(attrFilters, ""))
431+
418432
if c.UserSearch.Filter != "" {
419433
filter = fmt.Sprintf("(&%s%s)", c.UserSearch.Filter, filter)
420434
}
@@ -432,6 +446,11 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
432446
},
433447
}
434448

449+
for _, attr := range usernameAttrs {
450+
attr = strings.TrimSpace(attr)
451+
req.Attributes = append(req.Attributes, attr)
452+
}
453+
435454
for _, matcher := range c.GroupSearch.UserMatchers {
436455
req.Attributes = append(req.Attributes, matcher.UserAttr)
437456
}

connector/ldap/ldap_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,43 @@ func TestUserFilter(t *testing.T) {
185185
runTests(t, connectLDAP, c, tests)
186186
}
187187

188+
func TestUsernameWithMultipleAttributes(t *testing.T) {
189+
c := &Config{}
190+
c.UserSearch.BaseDN = "ou=TestUsernameWithMultipleAttributes,dc=example,dc=org"
191+
c.UserSearch.NameAttr = "cn"
192+
c.UserSearch.EmailAttr = "mail"
193+
c.UserSearch.IDAttr = "DN"
194+
c.UserSearch.Username = "cn,mail"
195+
c.UserSearch.Filter = "(ou:dn:=Seattle)"
196+
197+
tests := []subtest{
198+
{
199+
name: "cn",
200+
username: "jane",
201+
password: "foo",
202+
want: connector.Identity{
203+
UserID: "cn=jane,ou=People,ou=Seattle,ou=TestUsernameWithMultipleAttributes,dc=example,dc=org",
204+
Username: "jane",
205+
206+
EmailVerified: true,
207+
},
208+
},
209+
{
210+
name: "mail",
211+
username: "[email protected]",
212+
password: "foo",
213+
want: connector.Identity{
214+
UserID: "cn=jane,ou=People,ou=Seattle,ou=TestUsernameWithMultipleAttributes,dc=example,dc=org",
215+
Username: "jane",
216+
217+
EmailVerified: true,
218+
},
219+
},
220+
}
221+
222+
runTests(t, connectLDAP, c, tests)
223+
}
224+
188225
func TestGroupQuery(t *testing.T) {
189226
c := &Config{}
190227
c.UserSearch.BaseDN = "ou=People,ou=TestGroupQuery,dc=example,dc=org"

0 commit comments

Comments
 (0)