PR feedback
This commit is contained in:
parent
5d23068690
commit
cc3875f048
@ -84,7 +84,7 @@ while (("$#")); do
|
||||
shift
|
||||
# If there are no more command line arguments, or there is another command line argument but it starts with a dash, then error
|
||||
if [[ "$#" == "0" || "$1" == -* ]]; then
|
||||
log_error "-g|--get-active-directory-vars requires a script name to be specified"
|
||||
log_error "--get-active-directory-vars requires a script name to be specified"
|
||||
exit 1
|
||||
fi
|
||||
get_active_directory_vars=$1
|
||||
@ -107,10 +107,11 @@ if [[ "$help" == "yes" ]]; then
|
||||
log_note " $me [flags]"
|
||||
log_note
|
||||
log_note "Flags:"
|
||||
log_note " -h, --help: print this usage"
|
||||
log_note " -c, --clean: destroy the current kind cluster and make a new one"
|
||||
log_note " -g, --api-group-suffix: deploy Pinniped with an alternate API group suffix"
|
||||
log_note " -s, --skip-build: reuse the most recently built image of the app instead of building"
|
||||
log_note " -h, --help: print this usage"
|
||||
log_note " -c, --clean: destroy the current kind cluster and make a new one"
|
||||
log_note " -g, --api-group-suffix: deploy Pinniped with an alternate API group suffix"
|
||||
log_note " -s, --skip-build: reuse the most recently built image of the app instead of building"
|
||||
log_note " --get-active-directory-vars: specify a script that exports active directory environment variables"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -100,9 +100,9 @@ func (s *activeDirectoryUpstreamGenericLDAPSpec) DetectAndSetSearchBase(ctx cont
|
||||
if config.GroupSearch.Base != "" && config.UserSearch.Base != "" {
|
||||
// Both were already set in spec so just return; no need to query the RootDSE
|
||||
return &v1alpha1.Condition{
|
||||
Type: "SearchBaseFound",
|
||||
Type: upstreamwatchers.TypeSearchBaseFound,
|
||||
Status: v1alpha1.ConditionTrue,
|
||||
Reason: "Success",
|
||||
Reason: upstreamwatchers.ReasonUsingConfigurationFromSpec,
|
||||
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
||||
}
|
||||
}
|
||||
@ -115,7 +115,7 @@ func (s *activeDirectoryUpstreamGenericLDAPSpec) DetectAndSetSearchBase(ctx cont
|
||||
return &v1alpha1.Condition{
|
||||
Type: upstreamwatchers.TypeSearchBaseFound,
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Reason: upstreamwatchers.ReasonErrorFetchingSearchBase,
|
||||
Message: fmt.Sprintf(`Error finding search base: %s`, err.Error()),
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Type: "SearchBaseFound",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
Reason: "UsingConfigurationFromSpec",
|
||||
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
||||
ObservedGeneration: gen,
|
||||
}
|
||||
@ -284,7 +284,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Type: "SearchBaseFound",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Error",
|
||||
Reason: "ErrorFetchingSearchBase",
|
||||
Message: message,
|
||||
ObservedGeneration: gen,
|
||||
}
|
||||
@ -878,7 +878,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
testHost, testBindUsername, testBindUsername),
|
||||
ObservedGeneration: 1234,
|
||||
},
|
||||
searchBaseFoundErrorCondition(1234, "Error finding search base: error binding as \"test-bind-username\" before user search: some bind error"),
|
||||
searchBaseFoundErrorCondition(1234, "Error finding search base: error binding as \"test-bind-username\" before querying for defaultNamingContext: some bind error"),
|
||||
tlsConfigurationValidLoadedTrueCondition(1234),
|
||||
},
|
||||
},
|
||||
@ -1344,9 +1344,6 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "when the input activedirectoryidentityprovider leaves group search base blank and query for defaultNamingContext fails",
|
||||
// TODO is this a fatal error? I think so because leaving the search base blank and trying anyway does not seem expected.
|
||||
// queries with an empty search base could potentially succeed but return something unexpected, like if you were
|
||||
// pointing at global catalog but not intending to use the GC functionality...
|
||||
inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *v1alpha1.ActiveDirectoryIdentityProvider) {
|
||||
upstream.Spec.UserSearch.Attributes = v1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{}
|
||||
upstream.Spec.GroupSearch.Base = ""
|
||||
|
@ -33,13 +33,15 @@ const (
|
||||
TestLDAPConnectionTimeout = 90 * time.Second
|
||||
|
||||
// Constants related to conditions.
|
||||
typeBindSecretValid = "BindSecretValid"
|
||||
typeTLSConfigurationValid = "TLSConfigurationValid"
|
||||
typeLDAPConnectionValid = "LDAPConnectionValid"
|
||||
TypeSearchBaseFound = "SearchBaseFound"
|
||||
reasonLDAPConnectionError = "LDAPConnectionError"
|
||||
noTLSConfigurationMessage = "no TLS configuration provided"
|
||||
loadedTLSConfigurationMessage = "loaded TLS configuration"
|
||||
typeBindSecretValid = "BindSecretValid"
|
||||
typeTLSConfigurationValid = "TLSConfigurationValid"
|
||||
typeLDAPConnectionValid = "LDAPConnectionValid"
|
||||
TypeSearchBaseFound = "SearchBaseFound"
|
||||
reasonLDAPConnectionError = "LDAPConnectionError"
|
||||
noTLSConfigurationMessage = "no TLS configuration provided"
|
||||
loadedTLSConfigurationMessage = "loaded TLS configuration"
|
||||
ReasonUsingConfigurationFromSpec = "UsingConfigurationFromSpec"
|
||||
ReasonErrorFetchingSearchBase = "ErrorFetchingSearchBase"
|
||||
)
|
||||
|
||||
// An in-memory cache with an entry for each ActiveDirectoryIdentityProvider, to keep track of which ResourceVersion
|
||||
@ -187,7 +189,7 @@ func HasPreviousSuccessfulSearchBaseConditionForCurrentGeneration(secretVersionC
|
||||
// Found a previously successful condition for the current spec generation.
|
||||
// Now figure out which version of the bind Secret was used during that previous validation, if any.
|
||||
validatedSettings := secretVersionCache.ValidatedSettingsByName[upstreamName]
|
||||
// Reload the TLS vs StartTLS setting that was previously validated.
|
||||
// Reload the user search and group search base settings that were previously validated.
|
||||
config.UserSearch.Base = validatedSettings.UserSearchBase
|
||||
config.GroupSearch.Base = validatedSettings.GroupSearchBase
|
||||
return true
|
||||
|
@ -612,6 +612,17 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "Bad Gateway: unexpected error during upstream authentication\n",
|
||||
},
|
||||
{
|
||||
name: "error during upstream Active Directory authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&erroringUpstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
customUsernameHeader: pointer.StringPtr(happyLDAPUsername),
|
||||
customPasswordHeader: pointer.StringPtr(happyLDAPPassword),
|
||||
wantStatus: http.StatusBadGateway,
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "Bad Gateway: unexpected error during upstream authentication\n",
|
||||
},
|
||||
{
|
||||
name: "wrong upstream password for LDAP authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||
@ -624,6 +635,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "wrong upstream password for Active Directory authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
customUsernameHeader: pointer.StringPtr(happyLDAPUsername),
|
||||
customPasswordHeader: pointer.StringPtr("wrong-password"),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "wrong upstream username for LDAP authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||
@ -636,6 +659,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "wrong upstream username for Active Directory authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
customUsernameHeader: pointer.StringPtr("wrong-username"),
|
||||
customPasswordHeader: pointer.StringPtr(happyLDAPPassword),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing upstream username on request for LDAP authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||
@ -648,6 +683,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing upstream username on request for Active Directory authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
customUsernameHeader: nil, // do not send header
|
||||
customPasswordHeader: pointer.StringPtr(happyLDAPPassword),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing upstream password on request for LDAP authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||
@ -660,6 +707,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing upstream password on request for Active Directory authentication",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
customUsernameHeader: pointer.StringPtr(happyLDAPUsername),
|
||||
customPasswordHeader: nil, // do not send header
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "downstream redirect uri does not match what is configured for client when using OIDC upstream",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||
@ -764,6 +823,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "downstream scopes do not match what is configured for client using LDAP upstream",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid tuna"}),
|
||||
customUsernameHeader: pointer.StringPtr(happyLDAPUsername),
|
||||
customPasswordHeader: pointer.StringPtr(happyLDAPPassword),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing response type in request using OIDC upstream",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||
@ -789,6 +860,16 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing response type in request using Active Directory upstream",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(),
|
||||
method: http.MethodGet,
|
||||
path: modifiedHappyGetRequestPath(map[string]string{"response_type": ""}),
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: "application/json; charset=utf-8",
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "missing client id in request using OIDC upstream",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||
@ -1122,6 +1203,15 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
||||
},
|
||||
{
|
||||
name: "too many upstream providers are configured: multiple Active Directory",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithActiveDirectory(&upstreamLDAPIdentityProvider, &upstreamLDAPIdentityProvider).Build(), // more than one not allowed
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
wantStatus: http.StatusUnprocessableEntity,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
||||
},
|
||||
{
|
||||
name: "too many upstream providers are configured: both OIDC and LDAP",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).WithLDAP(&upstreamLDAPIdentityProvider).Build(), // more than one not allowed
|
||||
@ -1131,6 +1221,15 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
||||
},
|
||||
{
|
||||
name: "too many upstream providers are configured: OIDC, LDAP and AD",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).WithLDAP(&upstreamLDAPIdentityProvider).WithActiveDirectory(&upstreamLDAPIdentityProvider).Build(), // more than one not allowed
|
||||
method: http.MethodGet,
|
||||
path: happyGetRequestPath,
|
||||
wantStatus: http.StatusUnprocessableEntity,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
||||
},
|
||||
{
|
||||
name: "PUT is a bad method",
|
||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||
|
@ -41,6 +41,7 @@ func TestIDPDiscovery(t *testing.T) {
|
||||
{Name: "a-some-oidc-idp", Type: "oidc"},
|
||||
{Name: "x-some-idp", Type: "ldap"},
|
||||
{Name: "x-some-idp", Type: "oidc"},
|
||||
{Name: "y-some-ad-idp", Type: "activedirectory"},
|
||||
{Name: "z-some-ad-idp", Type: "activedirectory"},
|
||||
{Name: "z-some-ldap-idp", Type: "ldap"},
|
||||
{Name: "z-some-oidc-idp", Type: "oidc"},
|
||||
@ -49,6 +50,7 @@ func TestIDPDiscovery(t *testing.T) {
|
||||
wantSecondResponseBodyJSON: &response{
|
||||
IDPs: []identityProviderResponse{
|
||||
{Name: "some-other-ad-idp-1", Type: "activedirectory"},
|
||||
{Name: "some-other-ad-idp-2", Type: "activedirectory"},
|
||||
{Name: "some-other-ldap-idp-1", Type: "ldap"},
|
||||
{Name: "some-other-ldap-idp-2", Type: "ldap"},
|
||||
{Name: "some-other-oidc-idp-1", Type: "oidc"},
|
||||
@ -76,6 +78,7 @@ func TestIDPDiscovery(t *testing.T) {
|
||||
WithLDAP(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "z-some-ldap-idp"}).
|
||||
WithLDAP(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "x-some-idp"}).
|
||||
WithActiveDirectory(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "z-some-ad-idp"}).
|
||||
WithActiveDirectory(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "y-some-ad-idp"}).
|
||||
Build()
|
||||
|
||||
handler := NewHandler(idpLister)
|
||||
@ -108,6 +111,7 @@ func TestIDPDiscovery(t *testing.T) {
|
||||
})
|
||||
|
||||
idpLister.SetActiveDirectoryIdentityProviders([]provider.UpstreamLDAPIdentityProviderI{
|
||||
&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "some-other-ad-idp-2"},
|
||||
&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "some-other-ad-idp-1"},
|
||||
})
|
||||
|
||||
|
@ -393,7 +393,7 @@ func (p *Provider) validateConfig() error {
|
||||
}
|
||||
|
||||
func (p *Provider) SearchForDefaultNamingContext(ctx context.Context) (string, error) {
|
||||
t := trace.FromContext(ctx).Nest("slow ldap authenticate user attempt", trace.Field{Key: "providerName", Value: p.GetName()})
|
||||
t := trace.FromContext(ctx).Nest("slow ldap attempt when searching for default naming context", trace.Field{Key: "providerName", Value: p.GetName()})
|
||||
defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches
|
||||
|
||||
conn, err := p.dial(ctx)
|
||||
@ -406,7 +406,7 @@ func (p *Provider) SearchForDefaultNamingContext(ctx context.Context) (string, e
|
||||
err = conn.Bind(p.c.BindUsername, p.c.BindPassword)
|
||||
if err != nil {
|
||||
p.traceAuthFailure(t, err)
|
||||
return "", fmt.Errorf(`error binding as "%s" before user search: %w`, p.c.BindUsername, err)
|
||||
return "", fmt.Errorf(`error binding as "%s" before querying for defaultNamingContext: %w`, p.c.BindUsername, err)
|
||||
}
|
||||
|
||||
searchResult, err := conn.Search(p.defaultNamingContextRequest())
|
||||
|
@ -281,12 +281,79 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(
|
||||
"ldaps://" + env.SupervisorUpstreamActiveDirectory.Host +
|
||||
"?base=" + url.QueryEscape("DC=activedirectory,DC=test,DC=pinniped,DC=dev") +
|
||||
"?base=" + url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase) +
|
||||
"&sub=" + env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
),
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue),
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountNames,
|
||||
}, {
|
||||
name: "activedirectory with custom options",
|
||||
maybeSkip: func(t *testing.T) {
|
||||
t.Helper()
|
||||
if len(env.ToolsNamespace) == 0 && !env.HasCapability(testlib.CanReachInternetLDAPPorts) {
|
||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||
}
|
||||
if env.SupervisorUpstreamActiveDirectory.Host == "" {
|
||||
t.Skip("Active Directory hostname not specified")
|
||||
}
|
||||
},
|
||||
createIDP: func(t *testing.T) {
|
||||
t.Helper()
|
||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||
map[string]string{
|
||||
v1.BasicAuthUsernameKey: env.SupervisorUpstreamActiveDirectory.BindUsername,
|
||||
v1.BasicAuthPasswordKey: env.SupervisorUpstreamActiveDirectory.BindPassword,
|
||||
},
|
||||
)
|
||||
adIDP := testlib.CreateTestActiveDirectoryIdentityProvider(t, idpv1alpha1.ActiveDirectoryIdentityProviderSpec{
|
||||
Host: env.SupervisorUpstreamActiveDirectory.Host,
|
||||
TLS: &idpv1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamActiveDirectory.CABundle)),
|
||||
},
|
||||
Bind: idpv1alpha1.ActiveDirectoryIdentityProviderBind{
|
||||
SecretName: secret.Name,
|
||||
},
|
||||
UserSearch: idpv1alpha1.ActiveDirectoryIdentityProviderUserSearch{
|
||||
Base: env.SupervisorUpstreamActiveDirectory.UserSearchBase,
|
||||
Filter: env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeName + "={}",
|
||||
Attributes: idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{
|
||||
Username: env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeName,
|
||||
},
|
||||
},
|
||||
GroupSearch: idpv1alpha1.ActiveDirectoryIdentityProviderGroupSearch{
|
||||
Filter: "member={}", // excluding nested groups
|
||||
Base: env.SupervisorUpstreamActiveDirectory.GroupSearchBase,
|
||||
Attributes: idpv1alpha1.ActiveDirectoryIdentityProviderGroupSearchAttributes{
|
||||
GroupName: "dn",
|
||||
},
|
||||
},
|
||||
}, idpv1alpha1.ActiveDirectoryPhaseReady)
|
||||
expectedMsg := fmt.Sprintf(
|
||||
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
||||
env.SupervisorUpstreamActiveDirectory.Host, env.SupervisorUpstreamActiveDirectory.BindUsername,
|
||||
secret.Name, secret.ResourceVersion,
|
||||
)
|
||||
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
||||
},
|
||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||
requestAuthorizationUsingLDAPIdentityProvider(t,
|
||||
downstreamAuthorizeURL,
|
||||
env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue, // username to present to server during login
|
||||
env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login
|
||||
httpClient,
|
||||
false,
|
||||
)
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(
|
||||
"ldaps://" + env.SupervisorUpstreamActiveDirectory.Host +
|
||||
"?base=" + url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase) +
|
||||
"&sub=" + env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
),
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue),
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserDirectGroupsDNs,
|
||||
},
|
||||
{
|
||||
name: "logging in to activedirectory with a deactivated user fails",
|
||||
@ -395,11 +462,18 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, ad
|
||||
}
|
||||
}
|
||||
|
||||
expectedUserSearchReason := ""
|
||||
if adIDP.Spec.UserSearch.Base == "" || adIDP.Spec.GroupSearch.Base == "" {
|
||||
expectedUserSearchReason = "Success"
|
||||
} else {
|
||||
expectedUserSearchReason = "UsingConfigurationFromSpec"
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, [][]string{
|
||||
{"BindSecretValid", "True", "Success"},
|
||||
{"TLSConfigurationValid", "True", "Success"},
|
||||
{"LDAPConnectionValid", "True", "Success"},
|
||||
{"SearchBaseFound", "True", "Success"},
|
||||
{"SearchBaseFound", "True", expectedUserSearchReason},
|
||||
}, conditionsSummary)
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ type TestLDAPUpstream struct {
|
||||
BindUsername string `json:"bindUsername"`
|
||||
BindPassword string `json:"bindPassword"`
|
||||
UserSearchBase string `json:"userSearchBase"`
|
||||
DefaultNamingContextSearchBase string `json:"defaultNamingContextSearchBase"`
|
||||
GroupSearchBase string `json:"groupSearchBase"`
|
||||
TestUserDN string `json:"testUserDN"`
|
||||
TestUserCN string `json:"testUserCN"`
|
||||
@ -281,11 +282,16 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
|
||||
TestUserUniqueIDAttributeName: wantEnv("PINNIPED_TEST_AD_USER_UNIQUE_ID_ATTRIBUTE_NAME", ""),
|
||||
TestUserUniqueIDAttributeValue: wantEnv("PINNIPED_TEST_AD_USER_UNIQUE_ID_ATTRIBUTE_VALUE", ""),
|
||||
TestUserSAMAccountNameValue: wantEnv("PINNIPED_TEST_AD_USERNAME_ATTRIBUTE_VALUE", ""),
|
||||
TestUserMailAttributeValue: wantEnv("PINNIPED_TEST_AD_USER_EMAIL_ATTRIBUTE_VALUE", ""),
|
||||
TestUserMailAttributeName: wantEnv("PINNIPED_TEST_AD_USER_EMAIL_ATTRIBUTE_NAME", ""),
|
||||
TestUserDirectGroupsDNs: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_DN", ""), ";")),
|
||||
TestUserDirectGroupsCNs: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_CN", ""), ";")),
|
||||
TestUserIndirectGroupsSAMAccountNames: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_SAMACCOUNTNAME", ""), ";")),
|
||||
TestDeactivatedUserSAMAccountNameValue: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_SAMACCOUNTNAME", ""),
|
||||
TestDeactivatedUserPassword: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_PASSWORD", ""),
|
||||
DefaultNamingContextSearchBase: wantEnv("PINNIPED_TEST_AD_DEFAULTNAMINGCONTEXT_DN", ""),
|
||||
UserSearchBase: wantEnv("PINNIPED_TEST_AD_USERS_DN", ""),
|
||||
GroupSearchBase: wantEnv("PINNIPED_TEST_AD_USERS_DN", ""),
|
||||
}
|
||||
|
||||
sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs)
|
||||
|
Loading…
Reference in New Issue
Block a user