Skip to content

Commit

Permalink
feat(ldap): allow to customize user filter
Browse files Browse the repository at this point in the history
Fix #2924

Signed-off-by: Vladimir Ermakov <[email protected]>
  • Loading branch information
vooon committed Jan 31, 2025
1 parent 05823cd commit bdbd282
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 18 deletions.
3 changes: 2 additions & 1 deletion examples/config-ldap.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"startTLS": false,
"baseDN":"ou=Users,dc=example,dc=org",
"userAttribute": "uid",
"userFilter": "(!(nsaccountlock=TRUE))",
"userGroupAttribute": "memberOf",
"skipVerify": true,
"subtreeSearch": true
Expand Down Expand Up @@ -69,4 +70,4 @@
"output": "/tmp/zot.log",
"audit": "/tmp/zot-audit.log"
}
}
}
3 changes: 2 additions & 1 deletion pkg/api/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
BindDN: ldapConfig.BindDN(),
BindPassword: ldapConfig.BindPassword(),
UserGroupAttribute: ldapConfig.UserGroupAttribute, // from config
UserFilter: fmt.Sprintf("(%s=%%s)", ldapConfig.UserAttribute),
UserAttribute: ldapConfig.UserAttribute,
UserFilter: ldapConfig.UserFilter,
InsecureSkipVerify: ldapConfig.SkipVerify,
ServerName: ldapConfig.Address,
Log: ctlr.Log,
Expand Down
1 change: 1 addition & 0 deletions pkg/api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ type LDAPConfig struct {
UserGroupAttribute string
BaseDN string
UserAttribute string
UserFilter string
CACert string
}

Expand Down
53 changes: 39 additions & 14 deletions pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ var (
LDAPBaseDN = "ou=" + username //nolint: gochecknoglobals
LDAPBindDN = "cn=reader," + LDAPBaseDN //nolint: gochecknoglobals
LDAPBindPassword = "ldappass" //nolint: gochecknoglobals
LDAPUserAttr = "uid" //nolint: gochecknoglobals
)

func TestNew(t *testing.T) {
Expand Down Expand Up @@ -2974,6 +2975,13 @@ func (l *testLDAPServer) Search(boundDN string, req vldap.SearchRequest,
}, nil
}

if req.Filter == "(&(uid=locked-user)((!(nsaccountlock=TRUE))))" {
return vldap.ServerSearchResult{
Entries: []*vldap.Entry{},
ResultCode: vldap.LDAPResultSuccess,
}, nil
}

check := fmt.Sprintf("(uid=%s)", username)

if check == req.Filter {
Expand Down Expand Up @@ -3769,31 +3777,48 @@ func TestLDAPClient(t *testing.T) {

// bad user credentials with anonymous authentication
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserFilter: "(uid=%s)",
SkipTLS: true,
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserAttribute: LDAPUserAttr,
UserFilter: "",
SkipTLS: true,
}

_, _, _, err = lClient.Authenticate("fail-user-bind", "")
So(err, ShouldNotBeNil)

// bad user credentials with anonymous authentication
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserFilter: "(uid=%s)",
SkipTLS: true,
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserAttribute: LDAPUserAttr,
UserFilter: "",
SkipTLS: true,
}

_, _, _, err = lClient.Authenticate("fail-user-bind", "pass")
So(err, ShouldNotBeNil)

// user filtered by additional filter (disabled account in FreeIPA)
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserAttribute: LDAPUserAttr,
UserFilter: "(!(nsaccountlock=TRUE))",
SkipTLS: true,
}

_, _, _, err = lClient.Authenticate("locked-user", "pass")
So(err, ShouldNotBeNil)
})
}

Expand Down
15 changes: 13 additions & 2 deletions pkg/api/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type LDAPClient struct {
UserGroupAttribute string // e.g. "memberOf"
Host string
ServerName string
UserFilter string // e.g. "(uid=%s)"
UserFilter string // e.g. "(!(nsaccountlock=TRUE))"
UserAttribute string // e.g. "uid"
Conn *ldap.Conn
ClientCertificates []tls.Certificate // Adding client certificates
ClientCAs *x509.CertPool
Expand Down Expand Up @@ -187,7 +188,7 @@ func (lc *LDAPClient) Authenticate(username, password string) (bool, map[string]
searchRequest := ldap.NewSearchRequest(
lc.Base,
searchScope, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(lc.UserFilter, username),
lc.userFilter(username),
attributes,
nil,
)
Expand Down Expand Up @@ -242,3 +243,13 @@ func (lc *LDAPClient) Authenticate(username, password string) (bool, map[string]

return true, user, userGroups, nil
}

func (lc *LDAPClient) userFilter(username string) string {
filter := fmt.Sprintf("(%s=%s)", lc.UserAttribute, ldap.EscapeFilter(username))

if lc.UserFilter != "" {
filter = fmt.Sprintf("(&(%s)(%s))", filter, lc.UserFilter)
}

return filter
}

0 comments on commit bdbd282

Please sign in to comment.