From e9aa37ba673ba9200d30f8fe832183125a7a03eb Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 12 May 2021 18:49:15 +0200 Subject: [PATCH] outposts/ldap: fix user info caching, fix mixed case DN Signed-off-by: Jens Langhammer #864 --- outpost/pkg/ldap/bind.go | 8 +++++--- outpost/pkg/ldap/instance_bind.go | 19 ++++++++++--------- outpost/pkg/ldap/instance_search.go | 3 +++ outpost/pkg/ldap/ldap.go | 2 +- outpost/pkg/ldap/search.go | 6 +++--- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/outpost/pkg/ldap/bind.go b/outpost/pkg/ldap/bind.go index 1fcc6e781..c050b7184 100644 --- a/outpost/pkg/ldap/bind.go +++ b/outpost/pkg/ldap/bind.go @@ -2,20 +2,22 @@ package ldap import ( "net" + "strings" "github.com/nmcclain/ldap" ) func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) { - ls.log.WithField("boundDN", bindDN).Info("bind") + ls.log.WithField("bindDN", bindDN).Info("bind") + bindDN = strings.ToLower(bindDN) for _, instance := range ls.providers { username, err := instance.getUsername(bindDN) if err == nil { - return instance.Bind(username, bindPW, conn) + return instance.Bind(username, bindDN, bindPW, conn) } else { ls.log.WithError(err).Debug("Username not for instance") } } - ls.log.WithField("boundDN", bindDN).WithField("request", "bind").Warning("No provider found for request") + ls.log.WithField("bindDN", bindDN).WithField("request", "bind").Warning("No provider found for request") return ldap.LDAPResultOperationsError, nil } diff --git a/outpost/pkg/ldap/instance_bind.go b/outpost/pkg/ldap/instance_bind.go index 6ba7d1d4b..b9a860854 100644 --- a/outpost/pkg/ldap/instance_bind.go +++ b/outpost/pkg/ldap/instance_bind.go @@ -47,7 +47,7 @@ func (pi *ProviderInstance) getUsername(dn string) (string, error) { return "", errors.New("failed to find cn") } -func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) { +func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) { jar, err := cookiejar.New(nil) if err != nil { pi.log.WithError(err).Warning("Failed to create cookiejar") @@ -69,7 +69,7 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) params.Add("goauthentik.io/outpost/ldap", "true") passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode(), 1) if err != nil { - pi.log.WithField("boundDN", username).WithError(err).Warning("failed to solve challenge") + pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge") return ldap.LDAPResultOperationsError, nil } if !passed { @@ -82,25 +82,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) }, httptransport.PassThroughAuth) if err != nil { if _, denied := err.(*core.CoreApplicationsCheckAccessForbidden); denied { - pi.log.WithField("boundDN", username).Info("Access denied for user") + pi.log.WithField("bindDN", bindDN).Info("Access denied for user") return ldap.LDAPResultInsufficientAccessRights, nil } - pi.log.WithField("boundDN", username).WithError(err).Warning("failed to check access") + pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to check access") return ldap.LDAPResultOperationsError, nil } - pi.log.WithField("boundDN", username).Info("User has access") + pi.log.WithField("bindDN", bindDN).Info("User has access") // Get user info to store in context userInfo, err := pi.s.ac.Client.Core.CoreUsersMe(&core.CoreUsersMeParams{ Context: context.Background(), HTTPClient: client, }, httptransport.PassThroughAuth) if err != nil { - pi.log.WithField("boundDN", username).WithError(err).Warning("failed to get user info") + pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to get user info") return ldap.LDAPResultOperationsError, nil } pi.boundUsersMutex.Lock() - pi.boundUsers[username] = UserFlags{ - UserInfo: userInfo.Payload.User, + pi.boundUsers[bindDN] = UserFlags{ + UserInfo: *userInfo.Payload.User, CanSearch: pi.SearchAccessCheck(userInfo.Payload.User), } defer pi.boundUsersMutex.Unlock() @@ -112,7 +112,8 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) func (pi *ProviderInstance) SearchAccessCheck(user *models.User) bool { for _, group := range user.Groups { for _, allowedGroup := range pi.searchAllowedGroups { - if &group.Pk == allowedGroup { + pi.log.WithField("userGroup", group.Pk).WithField("allowedGroup", allowedGroup).Trace("Checking search access") + if group.Pk.String() == allowedGroup.String() { pi.log.WithField("group", group.Name).Info("Allowed access to search") return true } diff --git a/outpost/pkg/ldap/instance_search.go b/outpost/pkg/ldap/instance_search.go index b7102c347..913adcbb2 100644 --- a/outpost/pkg/ldap/instance_search.go +++ b/outpost/pkg/ldap/instance_search.go @@ -29,10 +29,13 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest, pi.boundUsersMutex.RLock() defer pi.boundUsersMutex.RUnlock() flags, ok := pi.boundUsers[bindDN] + pi.log.WithField("bindDN", bindDN).WithField("ok", ok).Debugf("%+v\n", flags) if !ok { + pi.log.Debug("User info not cached") return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") } if !flags.CanSearch { + pi.log.Debug("User can't search") return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") } diff --git a/outpost/pkg/ldap/ldap.go b/outpost/pkg/ldap/ldap.go index a22348639..f7253108e 100644 --- a/outpost/pkg/ldap/ldap.go +++ b/outpost/pkg/ldap/ldap.go @@ -31,7 +31,7 @@ type ProviderInstance struct { } type UserFlags struct { - UserInfo *models.User + UserInfo models.User CanSearch bool } diff --git a/outpost/pkg/ldap/search.go b/outpost/pkg/ldap/search.go index ecc5f35e6..ab00a71bc 100644 --- a/outpost/pkg/ldap/search.go +++ b/outpost/pkg/ldap/search.go @@ -8,8 +8,8 @@ import ( "github.com/nmcclain/ldap" ) -func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) { - ls.log.WithField("boundDN", boundDN).WithField("baseDN", searchReq.BaseDN).Info("search") +func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) { + ls.log.WithField("bindDN", bindDN).WithField("baseDN", searchReq.BaseDN).Info("search") if searchReq.BaseDN == "" { return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil } @@ -21,7 +21,7 @@ func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn for _, provider := range ls.providers { providerBase, _ := goldap.ParseDN(provider.BaseDN) if providerBase.AncestorOf(bd) { - return provider.Search(boundDN, searchReq, conn) + return provider.Search(bindDN, searchReq, conn) } } return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("no provider could handle request")