outpost/ldap: check access based on Group Membership
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
d84d7c26ca
commit
32934fcd38
|
@ -37,7 +37,6 @@ from authentik.outposts.docker_tls import DockerInlineTLS
|
||||||
OUR_VERSION = parse(__version__)
|
OUR_VERSION = parse(__version__)
|
||||||
OUTPOST_HELLO_INTERVAL = 10
|
OUTPOST_HELLO_INTERVAL = 10
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
USER_ATTRIBUTE_LDAP_CAN_SEARCH = "goauthentik.io/ldap/can-search"
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceConnectionInvalid(SentryIgnoredException):
|
class ServiceConnectionInvalid(SentryIgnoredException):
|
||||||
|
@ -322,21 +321,13 @@ class Outpost(models.Model):
|
||||||
"""Username for service user"""
|
"""Username for service user"""
|
||||||
return f"ak-outpost-{self.uuid.hex}"
|
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
|
@property
|
||||||
def user(self) -> User:
|
def user(self) -> User:
|
||||||
"""Get/create user with access to all required objects"""
|
"""Get/create user with access to all required objects"""
|
||||||
users = User.objects.filter(username=self.user_identifier)
|
users = User.objects.filter(username=self.user_identifier)
|
||||||
if not users.exists():
|
if not users.exists():
|
||||||
user: User = User.objects.create(username=self.user_identifier)
|
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.set_unusable_password()
|
||||||
user.save()
|
user.save()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"goauthentik.io/outpost/pkg/client/outposts"
|
"goauthentik.io/outpost/pkg/client/outposts"
|
||||||
)
|
)
|
||||||
|
@ -28,10 +28,9 @@ func (ls *LDAPServer) Refresh() error {
|
||||||
UserDN: userDN,
|
UserDN: userDN,
|
||||||
appSlug: *provider.ApplicationSlug,
|
appSlug: *provider.ApplicationSlug,
|
||||||
flowSlug: *provider.BindFlowSlug,
|
flowSlug: *provider.BindFlowSlug,
|
||||||
|
searchAllowedGroups: []*strfmt.UUID{provider.SearchGroup},
|
||||||
s: ls,
|
s: ls,
|
||||||
log: log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name),
|
log: log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name),
|
||||||
boundUsersMutex: sync.RWMutex{},
|
|
||||||
boundUsers: make(map[string]UserFlags),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ls.providers = providers
|
ls.providers = providers
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/nmcclain/ldap"
|
"github.com/nmcclain/ldap"
|
||||||
"goauthentik.io/outpost/pkg/client/core"
|
"goauthentik.io/outpost/pkg/client/core"
|
||||||
"goauthentik.io/outpost/pkg/client/flows"
|
"goauthentik.io/outpost/pkg/client/flows"
|
||||||
|
"goauthentik.io/outpost/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ContextUserKey = "ak_user"
|
const ContextUserKey = "ak_user"
|
||||||
|
@ -88,13 +89,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
||||||
pi.boundUsersMutex.Lock()
|
pi.boundUsersMutex.Lock()
|
||||||
pi.boundUsers[username] = UserFlags{
|
pi.boundUsers[username] = UserFlags{
|
||||||
UserInfo: userInfo.Payload.User,
|
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.boundUsersMutex.Unlock()
|
||||||
pi.delayDeleteUserInfo(username)
|
pi.delayDeleteUserInfo(username)
|
||||||
return ldap.LDAPResultSuccess, nil
|
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) {
|
func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
|
||||||
ticker := time.NewTicker(30 * time.Second)
|
ticker := time.NewTicker(30 * time.Second)
|
||||||
quit := make(chan struct{})
|
quit := make(chan struct{})
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ldap
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"goauthentik.io/outpost/pkg/ak"
|
"goauthentik.io/outpost/pkg/ak"
|
||||||
"goauthentik.io/outpost/pkg/models"
|
"goauthentik.io/outpost/pkg/models"
|
||||||
|
@ -24,6 +25,7 @@ type ProviderInstance struct {
|
||||||
s *LDAPServer
|
s *LDAPServer
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
|
|
||||||
|
searchAllowedGroups []*strfmt.UUID
|
||||||
boundUsersMutex sync.RWMutex
|
boundUsersMutex sync.RWMutex
|
||||||
boundUsers map[string]UserFlags
|
boundUsers map[string]UserFlags
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue