outpost/ldap: check access based on Group Membership

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-05-05 00:03:19 +02:00
parent d84d7c26ca
commit 32934fcd38
4 changed files with 32 additions and 27 deletions

View File

@ -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:

View File

@ -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

View File

@ -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{})

View File

@ -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 {