From 32934fcd389aac8415c3383a0332ea973a01f449 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 5 May 2021 00:03:19 +0200 Subject: [PATCH] outpost/ldap: check access based on Group Membership Signed-off-by: Jens Langhammer --- authentik/outposts/models.py | 11 +---------- outpost/pkg/ldap/api.go | 19 +++++++++---------- outpost/pkg/ldap/instance_bind.go | 15 ++++++++++++++- outpost/pkg/ldap/ldap.go | 14 ++++++++------ 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index dfd170580..88591ee2b 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -37,7 +37,6 @@ from authentik.outposts.docker_tls import DockerInlineTLS OUR_VERSION = parse(__version__) OUTPOST_HELLO_INTERVAL = 10 LOGGER = get_logger() -USER_ATTRIBUTE_LDAP_CAN_SEARCH = "goauthentik.io/ldap/can-search" class ServiceConnectionInvalid(SentryIgnoredException): @@ -322,21 +321,13 @@ class Outpost(models.Model): """Username for service user""" return f"ak-outpost-{self.uuid.hex}" - @property - def user_attributes(self) -> dict[str, Any]: - """Attributes that will be set on the service account""" - attrs = {USER_ATTRIBUTE_SA: True} - if self.type == OutpostType.LDAP: - attrs[USER_ATTRIBUTE_LDAP_CAN_SEARCH] = True - return attrs - @property def user(self) -> User: """Get/create user with access to all required objects""" users = User.objects.filter(username=self.user_identifier) if not users.exists(): user: User = User.objects.create(username=self.user_identifier) - user.attributes = self.user_attributes + user.attributes[USER_ATTRIBUTE_SA] = True user.set_unusable_password() user.save() else: diff --git a/outpost/pkg/ldap/api.go b/outpost/pkg/ldap/api.go index 37828b266..3b7717651 100644 --- a/outpost/pkg/ldap/api.go +++ b/outpost/pkg/ldap/api.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" "strings" - "sync" + "github.com/go-openapi/strfmt" log "github.com/sirupsen/logrus" "goauthentik.io/outpost/pkg/client/outposts" ) @@ -23,15 +23,14 @@ func (ls *LDAPServer) Refresh() error { userDN := strings.ToLower(fmt.Sprintf("cn=users,%s", provider.BaseDn)) groupDN := strings.ToLower(fmt.Sprintf("cn=groups,%s", provider.BaseDn)) providers[idx] = &ProviderInstance{ - BaseDN: provider.BaseDn, - GroupDN: groupDN, - UserDN: userDN, - appSlug: *provider.ApplicationSlug, - flowSlug: *provider.BindFlowSlug, - s: ls, - log: log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name), - boundUsersMutex: sync.RWMutex{}, - boundUsers: make(map[string]UserFlags), + BaseDN: provider.BaseDn, + GroupDN: groupDN, + UserDN: userDN, + appSlug: *provider.ApplicationSlug, + flowSlug: *provider.BindFlowSlug, + searchAllowedGroups: []*strfmt.UUID{provider.SearchGroup}, + s: ls, + log: log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name), } } ls.providers = providers diff --git a/outpost/pkg/ldap/instance_bind.go b/outpost/pkg/ldap/instance_bind.go index 2a847ee79..ea1f7b7a3 100644 --- a/outpost/pkg/ldap/instance_bind.go +++ b/outpost/pkg/ldap/instance_bind.go @@ -15,6 +15,7 @@ import ( "github.com/nmcclain/ldap" "goauthentik.io/outpost/pkg/client/core" "goauthentik.io/outpost/pkg/client/flows" + "goauthentik.io/outpost/pkg/models" ) const ContextUserKey = "ak_user" @@ -88,13 +89,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) pi.boundUsersMutex.Lock() pi.boundUsers[username] = UserFlags{ UserInfo: userInfo.Payload.User, - CanSearch: userInfo.Payload.User.Attributes.(map[string]bool)["goauthentik.io/ldap/can-search"], + CanSearch: pi.SearchAccessCheck(userInfo.Payload.User), } pi.boundUsersMutex.Unlock() pi.delayDeleteUserInfo(username) return ldap.LDAPResultSuccess, nil } +// SearchAccessCheck Check if the current user is allowed to search +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("group", group.Name).Info("Allowed access to search") + return true + } + } + } + return false +} func (pi *ProviderInstance) delayDeleteUserInfo(dn string) { ticker := time.NewTicker(30 * time.Second) quit := make(chan struct{}) diff --git a/outpost/pkg/ldap/ldap.go b/outpost/pkg/ldap/ldap.go index 4f65f67bf..a22348639 100644 --- a/outpost/pkg/ldap/ldap.go +++ b/outpost/pkg/ldap/ldap.go @@ -3,6 +3,7 @@ package ldap import ( "sync" + "github.com/go-openapi/strfmt" log "github.com/sirupsen/logrus" "goauthentik.io/outpost/pkg/ak" "goauthentik.io/outpost/pkg/models" @@ -19,13 +20,14 @@ type ProviderInstance struct { UserDN string GroupDN string - appSlug string - flowSlug string - s *LDAPServer - log *log.Entry + appSlug string + flowSlug string + s *LDAPServer + log *log.Entry - boundUsersMutex sync.RWMutex - boundUsers map[string]UserFlags + searchAllowedGroups []*strfmt.UUID + boundUsersMutex sync.RWMutex + boundUsers map[string]UserFlags } type UserFlags struct {