0ce41a1b2d
* providers/ldap: add StartTLS support Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add starttls test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update form and docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-add tls server name Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update release notes Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
183 lines
4.7 KiB
Go
183 lines
4.7 KiB
Go
package ldap
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"beryju.io/ldap"
|
|
"github.com/go-openapi/strfmt"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"goauthentik.io/api/v3"
|
|
"goauthentik.io/internal/constants"
|
|
"goauthentik.io/internal/outpost/ldap/bind"
|
|
ldapConstants "goauthentik.io/internal/outpost/ldap/constants"
|
|
"goauthentik.io/internal/outpost/ldap/flags"
|
|
"goauthentik.io/internal/outpost/ldap/search"
|
|
"goauthentik.io/internal/outpost/ldap/utils"
|
|
)
|
|
|
|
type ProviderInstance struct {
|
|
BaseDN string
|
|
UserDN string
|
|
VirtualGroupDN string
|
|
GroupDN string
|
|
|
|
searcher search.Searcher
|
|
binder bind.Binder
|
|
|
|
appSlug string
|
|
authenticationFlowSlug string
|
|
invalidationFlowSlug string
|
|
s *LDAPServer
|
|
log *log.Entry
|
|
|
|
tlsServerName *string
|
|
cert *tls.Certificate
|
|
certUUID string
|
|
outpostName string
|
|
outpostPk int32
|
|
searchAllowedGroups []*strfmt.UUID
|
|
boundUsersMutex *sync.RWMutex
|
|
boundUsers map[string]*flags.UserFlags
|
|
|
|
uidStartNumber int32
|
|
gidStartNumber int32
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetAPIClient() *api.APIClient {
|
|
return pi.s.ac.Client
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetBaseDN() string {
|
|
return pi.BaseDN
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetBaseGroupDN() string {
|
|
return pi.GroupDN
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetBaseVirtualGroupDN() string {
|
|
return pi.VirtualGroupDN
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetBaseUserDN() string {
|
|
return pi.UserDN
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetOutpostName() string {
|
|
return pi.outpostName
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetFlags(dn string) *flags.UserFlags {
|
|
pi.boundUsersMutex.RLock()
|
|
defer pi.boundUsersMutex.RUnlock()
|
|
flags, ok := pi.boundUsers[dn]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return flags
|
|
}
|
|
|
|
func (pi *ProviderInstance) SetFlags(dn string, flag *flags.UserFlags) {
|
|
pi.boundUsersMutex.Lock()
|
|
if flag == nil {
|
|
delete(pi.boundUsers, dn)
|
|
} else {
|
|
pi.boundUsers[dn] = flag
|
|
}
|
|
pi.boundUsersMutex.Unlock()
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetAppSlug() string {
|
|
return pi.appSlug
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetAuthenticationFlowSlug() string {
|
|
return pi.authenticationFlowSlug
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetInvalidationFlowSlug() string {
|
|
return pi.invalidationFlowSlug
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetSearchAllowedGroups() []*strfmt.UUID {
|
|
return pi.searchAllowedGroups
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetBaseEntry() *ldap.Entry {
|
|
return &ldap.Entry{
|
|
DN: pi.GetBaseDN(),
|
|
Attributes: []*ldap.EntryAttribute{
|
|
{
|
|
Name: "distinguishedName",
|
|
Values: []string{pi.GetBaseDN()},
|
|
},
|
|
{
|
|
Name: "objectClass",
|
|
Values: []string{ldapConstants.OCTop, ldapConstants.OCDomain},
|
|
},
|
|
{
|
|
Name: "supportedLDAPVersion",
|
|
Values: []string{"3"},
|
|
},
|
|
{
|
|
Name: "namingContexts",
|
|
Values: []string{
|
|
pi.GetBaseDN(),
|
|
pi.GetBaseUserDN(),
|
|
pi.GetBaseGroupDN(),
|
|
pi.GetBaseVirtualGroupDN(),
|
|
},
|
|
},
|
|
{
|
|
Name: "vendorName",
|
|
Values: []string{"goauthentik.io"},
|
|
},
|
|
{
|
|
Name: "vendorVersion",
|
|
Values: []string{fmt.Sprintf("authentik LDAP Outpost Version %s", constants.FullVersion())},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (pi *ProviderInstance) GetNeededObjects(scope int, baseDN string, filterOC string) (bool, bool) {
|
|
needUsers := false
|
|
needGroups := false
|
|
|
|
// We only want to load users/groups if we're actually going to be asked
|
|
// for at least one user or group based on the search's base DN and scope.
|
|
//
|
|
// If our requested base DN doesn't match any of the container DNs, then
|
|
// we're probably loading a user or group. If it does, then make sure our
|
|
// scope will eventually take us to users or groups.
|
|
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.UserDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetUserOCs()) {
|
|
if baseDN != pi.UserDN && baseDN != pi.BaseDN ||
|
|
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
|
|
strings.EqualFold(baseDN, pi.UserDN) && scope > 0 {
|
|
needUsers = true
|
|
}
|
|
}
|
|
|
|
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.GroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetGroupOCs()) {
|
|
if baseDN != pi.GroupDN && baseDN != pi.BaseDN ||
|
|
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
|
|
strings.EqualFold(baseDN, pi.GroupDN) && scope > 0 {
|
|
needGroups = true
|
|
}
|
|
}
|
|
|
|
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.VirtualGroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetVirtualGroupOCs()) {
|
|
if baseDN != pi.VirtualGroupDN && baseDN != pi.BaseDN ||
|
|
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
|
|
strings.EqualFold(baseDN, pi.VirtualGroupDN) && scope > 0 {
|
|
needUsers = true
|
|
}
|
|
}
|
|
|
|
return needUsers, needGroups
|
|
}
|