mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-01-07 05:57:02 +00:00
Move ad specific stuff to controller
also make extra refresh attributes a separate field rather than part of Extra Signed-off-by: Margo Crawford <margaretc@vmware.com>
This commit is contained in:
@@ -7,8 +7,12 @@ package activedirectoryupstreamwatcher
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -52,6 +56,21 @@ const (
|
||||
// - has a member that matches the DN of the user we successfully logged in as.
|
||||
// - perform nested group search by default.
|
||||
defaultActiveDirectoryGroupSearchFilter = "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={}))"
|
||||
|
||||
sAMAccountNameAttribute = "sAMAccountName"
|
||||
// PwdLastSetAttribute is the date and time that the password for this account was last changed.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/adschema/a-pwdlastset
|
||||
PwdLastSetAttribute = "pwdLastSet"
|
||||
// UserAccountControlAttribute represents a bitmap of user properties.
|
||||
// https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
|
||||
UserAccountControlAttribute = "userAccountControl"
|
||||
// UserAccountControlComputedAttribute represents a bitmap of user properties.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/adschema/a-msds-user-account-control-computed
|
||||
UserAccountControlComputedAttribute = "msDS-User-Account-Control-Computed"
|
||||
// 0x0002 ACCOUNTDISABLE in userAccountControl bitmap.
|
||||
accountDisabledBitmapValue = 2
|
||||
// 0x0010 UF_LOCKOUT in msDS-User-Account-Control-Computed bitmap.
|
||||
accountLockedBitmapValue = 16
|
||||
)
|
||||
|
||||
type activeDirectoryUpstreamGenericLDAPImpl struct {
|
||||
@@ -316,16 +335,16 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context,
|
||||
GroupNameAttribute: adUpstreamImpl.Spec().GroupSearch().GroupNameAttribute(),
|
||||
},
|
||||
Dialer: c.ldapDialer,
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
upstreamldap.PwdLastSetAttribute: upstreamldap.AttributeUnchangedSinceLogin(upstreamldap.PwdLastSetAttribute),
|
||||
upstreamldap.UserAccountControlAttribute: upstreamldap.ValidUserAccountControl,
|
||||
upstreamldap.UserAccountControlComputedAttribute: upstreamldap.ValidComputedUserAccountControl,
|
||||
PwdLastSetAttribute: upstreamldap.AttributeUnchangedSinceLogin(PwdLastSetAttribute),
|
||||
UserAccountControlAttribute: ValidUserAccountControl,
|
||||
UserAccountControlComputedAttribute: ValidComputedUserAccountControl,
|
||||
},
|
||||
}
|
||||
|
||||
if spec.GroupSearch.Attributes.GroupName == "" {
|
||||
config.GroupAttributeParsingOverrides = map[string]func(*ldap.Entry) (string, error){defaultActiveDirectoryGroupNameAttributeName: upstreamldap.GroupSAMAccountNameWithDomainSuffix}
|
||||
config.GroupAttributeParsingOverrides = map[string]func(*ldap.Entry) (string, error){defaultActiveDirectoryGroupNameAttributeName: GroupSAMAccountNameWithDomainSuffix}
|
||||
}
|
||||
|
||||
conditions := upstreamwatchers.ValidateGenericLDAP(ctx, adUpstreamImpl, c.secretInformer, c.validatedSecretVersionsCache, config)
|
||||
@@ -358,3 +377,84 @@ func (c *activeDirectoryWatcherController) updateStatus(ctx context.Context, ups
|
||||
log.Error(err, "failed to update status")
|
||||
}
|
||||
}
|
||||
|
||||
func MicrosoftUUIDFromBinary(attributeName string) func(entry *ldap.Entry) (string, error) {
|
||||
// validation has already been done so we can just get the attribute...
|
||||
return func(entry *ldap.Entry) (string, error) {
|
||||
binaryUUID := entry.GetRawAttributeValue(attributeName)
|
||||
return microsoftUUIDFromBinary(binaryUUID)
|
||||
}
|
||||
}
|
||||
|
||||
func microsoftUUIDFromBinary(binaryUUID []byte) (string, error) {
|
||||
uuidVal, err := uuid.FromBytes(binaryUUID) // start out with the RFC4122 version
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// then swap it because AD stores the first 3 fields little-endian rather than the expected
|
||||
// big-endian.
|
||||
uuidVal[0], uuidVal[1], uuidVal[2], uuidVal[3] = uuidVal[3], uuidVal[2], uuidVal[1], uuidVal[0]
|
||||
uuidVal[4], uuidVal[5] = uuidVal[5], uuidVal[4]
|
||||
uuidVal[6], uuidVal[7] = uuidVal[7], uuidVal[6]
|
||||
return uuidVal.String(), nil
|
||||
}
|
||||
|
||||
func GroupSAMAccountNameWithDomainSuffix(entry *ldap.Entry) (string, error) {
|
||||
sAMAccountNameAttributeValues := entry.GetAttributeValues(sAMAccountNameAttribute)
|
||||
|
||||
if len(sAMAccountNameAttributeValues) != 1 {
|
||||
return "", fmt.Errorf(`found %d values for attribute "%s", but expected 1 result`,
|
||||
len(sAMAccountNameAttributeValues), sAMAccountNameAttribute,
|
||||
)
|
||||
}
|
||||
|
||||
sAMAccountName := sAMAccountNameAttributeValues[0]
|
||||
if len(sAMAccountName) == 0 {
|
||||
return "", fmt.Errorf(`found empty value for attribute "%s", but expected value to be non-empty`,
|
||||
sAMAccountNameAttribute,
|
||||
)
|
||||
}
|
||||
|
||||
distinguishedName := entry.DN
|
||||
domain, err := getDomainFromDistinguishedName(distinguishedName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sAMAccountName + "@" + domain, nil
|
||||
}
|
||||
|
||||
var domainComponentsRegexp = regexp.MustCompile(",DC=|,dc=")
|
||||
|
||||
func getDomainFromDistinguishedName(distinguishedName string) (string, error) {
|
||||
domainComponents := domainComponentsRegexp.Split(distinguishedName, -1)
|
||||
if len(domainComponents) == 1 {
|
||||
return "", fmt.Errorf("did not find domain components in group dn: %s", distinguishedName)
|
||||
}
|
||||
return strings.Join(domainComponents[1:], "."), nil
|
||||
}
|
||||
|
||||
func ValidUserAccountControl(entry *ldap.Entry, _ provider.StoredRefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(UserAccountControlAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deactivated := userAccountControl & accountDisabledBitmapValue // bitwise and.
|
||||
if deactivated != 0 {
|
||||
return fmt.Errorf("user has been deactivated")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidComputedUserAccountControl(entry *ldap.Entry, _ provider.StoredRefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(UserAccountControlComputedAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
locked := userAccountControl & accountLockedBitmapValue // bitwise and
|
||||
if locked != 0 {
|
||||
return fmt.Errorf("user has been locked")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -220,11 +220,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -541,11 +541,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -602,11 +602,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: "sAMAccountName",
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -666,11 +666,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -730,11 +730,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -793,11 +793,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -927,11 +927,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1056,11 +1056,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
}},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.ActiveDirectoryIdentityProvider{{
|
||||
@@ -1111,11 +1111,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1315,12 +1315,12 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={}))",
|
||||
GroupNameAttribute: "sAMAccountName",
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": upstreamldap.GroupSAMAccountNameWithDomainSuffix},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": GroupSAMAccountNameWithDomainSuffix},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1373,11 +1373,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1434,11 +1434,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1489,11 +1489,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1690,11 +1690,11 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Filter: testGroupSearchFilter,
|
||||
GroupNameAttribute: testGroupNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": MicrosoftUUIDFromBinary("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||
"userAccountControl": ValidUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": ValidComputedUserAccountControl,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1879,3 +1879,270 @@ func normalizeActiveDirectoryUpstreams(upstreams []v1alpha1.ActiveDirectoryIdent
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func TestGroupSAMAccountNameWithDomainSuffix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
entry *ldap.Entry
|
||||
wantResult string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path with DN and valid sAMAccountName",
|
||||
entry: &ldap.Entry{
|
||||
DN: "CN=animals,OU=Users,OU=pinniped-ad,DC=mycompany,DC=example,DC=com",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals"}),
|
||||
},
|
||||
},
|
||||
wantResult: "Mammals@mycompany.example.com",
|
||||
},
|
||||
{
|
||||
name: "no domain components in DN",
|
||||
entry: &ldap.Entry{
|
||||
DN: "no-domain-components",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals"}),
|
||||
},
|
||||
},
|
||||
wantErr: "did not find domain components in group dn: no-domain-components",
|
||||
},
|
||||
{
|
||||
name: "multiple values for sAMAccountName attribute",
|
||||
entry: &ldap.Entry{
|
||||
DN: "CN=animals,OU=Users,OU=pinniped-ad,DC=mycompany,DC=example,DC=com",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals", "Eukaryotes"}),
|
||||
},
|
||||
},
|
||||
wantErr: "found 2 values for attribute \"sAMAccountName\", but expected 1 result",
|
||||
},
|
||||
{
|
||||
name: "no values for sAMAccountName attribute",
|
||||
entry: &ldap.Entry{
|
||||
DN: "CN=animals,OU=Users,OU=pinniped-ad,DC=mycompany,DC=example,DC=com",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
ldap.NewEntryAttribute("sAMAccountName", []string{}),
|
||||
},
|
||||
},
|
||||
wantErr: "found 0 values for attribute \"sAMAccountName\", but expected 1 result",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
suffixedSAMAccountName, err := GroupSAMAccountNameWithDomainSuffix(tt.entry)
|
||||
if tt.wantErr != "" {
|
||||
require.EqualError(t, err, tt.wantErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tt.wantResult, suffixedSAMAccountName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMicrosoftFormattedUUID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
binaryUUID []byte
|
||||
wantString string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
binaryUUID: []byte("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16"),
|
||||
wantString: "04030201-0605-0807-0910-111213141516",
|
||||
},
|
||||
{
|
||||
name: "not the right length",
|
||||
binaryUUID: []byte("2\xf8\xb0\xaa\xb6V\xb1D\x8b(\xee"),
|
||||
wantErr: "invalid UUID (got 11 bytes)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actualUUIDString, err := microsoftUUIDFromBinary(tt.binaryUUID)
|
||||
if tt.wantErr != "" {
|
||||
require.EqualError(t, err, tt.wantErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tt.wantString, actualUUIDString)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDomainFromDistinguishedName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
distinguishedName string
|
||||
wantDomain string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
distinguishedName: "CN=Mammals,OU=Users,OU=pinniped-ad,DC=activedirectory,DC=mycompany,DC=example,DC=com",
|
||||
wantDomain: "activedirectory.mycompany.example.com",
|
||||
},
|
||||
{
|
||||
name: "lowercased happy path",
|
||||
distinguishedName: "cn=Mammals,ou=Users,ou=pinniped-ad,dc=activedirectory,dc=mycompany,dc=example,dc=com",
|
||||
wantDomain: "activedirectory.mycompany.example.com",
|
||||
},
|
||||
{
|
||||
name: "no domain components",
|
||||
distinguishedName: "not-a-dn",
|
||||
wantErr: "did not find domain components in group dn: not-a-dn",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actualDomain, err := getDomainFromDistinguishedName(tt.distinguishedName)
|
||||
if tt.wantErr != "" {
|
||||
require.EqualError(t, err, tt.wantErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tt.wantDomain, actualDomain)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidUserAccountControl(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
entry *ldap.Entry
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy normal user",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "userAccountControl",
|
||||
Values: []string{"512"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy user whose password doesn't expire",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "userAccountControl",
|
||||
Values: []string{"65536"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deactivated user",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "userAccountControl",
|
||||
Values: []string{"514"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "user has been deactivated",
|
||||
},
|
||||
{
|
||||
name: "non-integer result",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "userAccountControl",
|
||||
Values: []string{"not-an-int"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "strconv.Atoi: parsing \"not-an-int\": invalid syntax",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidUserAccountControl(tt.entry, provider.StoredRefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.wantErr, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidComputedUserAccountControl(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
entry *ldap.Entry
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy normal user",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "msDS-User-Account-Control-Computed",
|
||||
Values: []string{"0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "locked user",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "msDS-User-Account-Control-Computed",
|
||||
Values: []string{"16"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "user has been locked",
|
||||
},
|
||||
{
|
||||
name: "non-integer result",
|
||||
entry: &ldap.Entry{
|
||||
DN: "some-dn",
|
||||
Attributes: []*ldap.EntryAttribute{
|
||||
{
|
||||
Name: "msDS-User-Account-Control-Computed",
|
||||
Values: []string{"not-an-int"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "strconv.Atoi: parsing \"not-an-int\": invalid syntax",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidComputedUserAccountControl(tt.entry, provider.StoredRefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.wantErr, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user