PR feedback
This commit is contained in:
parent
5d23068690
commit
cc3875f048
@ -84,7 +84,7 @@ while (("$#")); do
|
|||||||
shift
|
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 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
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
get_active_directory_vars=$1
|
get_active_directory_vars=$1
|
||||||
@ -107,10 +107,11 @@ if [[ "$help" == "yes" ]]; then
|
|||||||
log_note " $me [flags]"
|
log_note " $me [flags]"
|
||||||
log_note
|
log_note
|
||||||
log_note "Flags:"
|
log_note "Flags:"
|
||||||
log_note " -h, --help: print this usage"
|
log_note " -h, --help: print this usage"
|
||||||
log_note " -c, --clean: destroy the current kind cluster and make a new one"
|
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 " -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 " -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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -100,9 +100,9 @@ func (s *activeDirectoryUpstreamGenericLDAPSpec) DetectAndSetSearchBase(ctx cont
|
|||||||
if config.GroupSearch.Base != "" && config.UserSearch.Base != "" {
|
if config.GroupSearch.Base != "" && config.UserSearch.Base != "" {
|
||||||
// Both were already set in spec so just return; no need to query the RootDSE
|
// Both were already set in spec so just return; no need to query the RootDSE
|
||||||
return &v1alpha1.Condition{
|
return &v1alpha1.Condition{
|
||||||
Type: "SearchBaseFound",
|
Type: upstreamwatchers.TypeSearchBaseFound,
|
||||||
Status: v1alpha1.ConditionTrue,
|
Status: v1alpha1.ConditionTrue,
|
||||||
Reason: "Success",
|
Reason: upstreamwatchers.ReasonUsingConfigurationFromSpec,
|
||||||
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ func (s *activeDirectoryUpstreamGenericLDAPSpec) DetectAndSetSearchBase(ctx cont
|
|||||||
return &v1alpha1.Condition{
|
return &v1alpha1.Condition{
|
||||||
Type: upstreamwatchers.TypeSearchBaseFound,
|
Type: upstreamwatchers.TypeSearchBaseFound,
|
||||||
Status: v1alpha1.ConditionFalse,
|
Status: v1alpha1.ConditionFalse,
|
||||||
Reason: "Error",
|
Reason: upstreamwatchers.ReasonErrorFetchingSearchBase,
|
||||||
Message: fmt.Sprintf(`Error finding search base: %s`, err.Error()),
|
Message: fmt.Sprintf(`Error finding search base: %s`, err.Error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
Type: "SearchBaseFound",
|
Type: "SearchBaseFound",
|
||||||
Status: "True",
|
Status: "True",
|
||||||
LastTransitionTime: now,
|
LastTransitionTime: now,
|
||||||
Reason: "Success",
|
Reason: "UsingConfigurationFromSpec",
|
||||||
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
Message: "Using search base from ActiveDirectoryIdentityProvider config.",
|
||||||
ObservedGeneration: gen,
|
ObservedGeneration: gen,
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
Type: "SearchBaseFound",
|
Type: "SearchBaseFound",
|
||||||
Status: "False",
|
Status: "False",
|
||||||
LastTransitionTime: now,
|
LastTransitionTime: now,
|
||||||
Reason: "Error",
|
Reason: "ErrorFetchingSearchBase",
|
||||||
Message: message,
|
Message: message,
|
||||||
ObservedGeneration: gen,
|
ObservedGeneration: gen,
|
||||||
}
|
}
|
||||||
@ -878,7 +878,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
testHost, testBindUsername, testBindUsername),
|
testHost, testBindUsername, testBindUsername),
|
||||||
ObservedGeneration: 1234,
|
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),
|
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",
|
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) {
|
inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *v1alpha1.ActiveDirectoryIdentityProvider) {
|
||||||
upstream.Spec.UserSearch.Attributes = v1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{}
|
upstream.Spec.UserSearch.Attributes = v1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{}
|
||||||
upstream.Spec.GroupSearch.Base = ""
|
upstream.Spec.GroupSearch.Base = ""
|
||||||
|
@ -33,13 +33,15 @@ const (
|
|||||||
TestLDAPConnectionTimeout = 90 * time.Second
|
TestLDAPConnectionTimeout = 90 * time.Second
|
||||||
|
|
||||||
// Constants related to conditions.
|
// Constants related to conditions.
|
||||||
typeBindSecretValid = "BindSecretValid"
|
typeBindSecretValid = "BindSecretValid"
|
||||||
typeTLSConfigurationValid = "TLSConfigurationValid"
|
typeTLSConfigurationValid = "TLSConfigurationValid"
|
||||||
typeLDAPConnectionValid = "LDAPConnectionValid"
|
typeLDAPConnectionValid = "LDAPConnectionValid"
|
||||||
TypeSearchBaseFound = "SearchBaseFound"
|
TypeSearchBaseFound = "SearchBaseFound"
|
||||||
reasonLDAPConnectionError = "LDAPConnectionError"
|
reasonLDAPConnectionError = "LDAPConnectionError"
|
||||||
noTLSConfigurationMessage = "no TLS configuration provided"
|
noTLSConfigurationMessage = "no TLS configuration provided"
|
||||||
loadedTLSConfigurationMessage = "loaded TLS configuration"
|
loadedTLSConfigurationMessage = "loaded TLS configuration"
|
||||||
|
ReasonUsingConfigurationFromSpec = "UsingConfigurationFromSpec"
|
||||||
|
ReasonErrorFetchingSearchBase = "ErrorFetchingSearchBase"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An in-memory cache with an entry for each ActiveDirectoryIdentityProvider, to keep track of which ResourceVersion
|
// 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.
|
// 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.
|
// Now figure out which version of the bind Secret was used during that previous validation, if any.
|
||||||
validatedSettings := secretVersionCache.ValidatedSettingsByName[upstreamName]
|
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.UserSearch.Base = validatedSettings.UserSearchBase
|
||||||
config.GroupSearch.Base = validatedSettings.GroupSearchBase
|
config.GroupSearch.Base = validatedSettings.GroupSearchBase
|
||||||
return true
|
return true
|
||||||
|
@ -612,6 +612,17 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantContentType: htmlContentType,
|
wantContentType: htmlContentType,
|
||||||
wantBodyString: "Bad Gateway: unexpected error during upstream authentication\n",
|
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",
|
name: "wrong upstream password for LDAP authentication",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||||
@ -624,6 +635,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "wrong upstream username for LDAP authentication",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||||
@ -636,6 +659,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithBadUsernamePasswordHintErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "missing upstream username on request for LDAP authentication",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||||
@ -648,6 +683,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "missing upstream password on request for LDAP authentication",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider).Build(),
|
||||||
@ -660,6 +707,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeAccessDeniedWithMissingUsernamePasswordHintErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "downstream redirect uri does not match what is configured for client when using OIDC upstream",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
@ -764,6 +823,18 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "missing response type in request using OIDC upstream",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
@ -789,6 +860,16 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery),
|
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery),
|
||||||
wantBodyString: "",
|
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",
|
name: "missing client id in request using OIDC upstream",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
@ -1122,6 +1203,15 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
wantContentType: "text/plain; charset=utf-8",
|
wantContentType: "text/plain; charset=utf-8",
|
||||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
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",
|
name: "too many upstream providers are configured: both OIDC and LDAP",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).WithLDAP(&upstreamLDAPIdentityProvider).Build(), // more than one not allowed
|
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",
|
wantContentType: "text/plain; charset=utf-8",
|
||||||
wantBodyString: "Unprocessable Entity: Too many upstream providers are configured (support for multiple upstreams is not yet implemented)\n",
|
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",
|
name: "PUT is a bad method",
|
||||||
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
|
@ -41,6 +41,7 @@ func TestIDPDiscovery(t *testing.T) {
|
|||||||
{Name: "a-some-oidc-idp", Type: "oidc"},
|
{Name: "a-some-oidc-idp", Type: "oidc"},
|
||||||
{Name: "x-some-idp", Type: "ldap"},
|
{Name: "x-some-idp", Type: "ldap"},
|
||||||
{Name: "x-some-idp", Type: "oidc"},
|
{Name: "x-some-idp", Type: "oidc"},
|
||||||
|
{Name: "y-some-ad-idp", Type: "activedirectory"},
|
||||||
{Name: "z-some-ad-idp", Type: "activedirectory"},
|
{Name: "z-some-ad-idp", Type: "activedirectory"},
|
||||||
{Name: "z-some-ldap-idp", Type: "ldap"},
|
{Name: "z-some-ldap-idp", Type: "ldap"},
|
||||||
{Name: "z-some-oidc-idp", Type: "oidc"},
|
{Name: "z-some-oidc-idp", Type: "oidc"},
|
||||||
@ -49,6 +50,7 @@ func TestIDPDiscovery(t *testing.T) {
|
|||||||
wantSecondResponseBodyJSON: &response{
|
wantSecondResponseBodyJSON: &response{
|
||||||
IDPs: []identityProviderResponse{
|
IDPs: []identityProviderResponse{
|
||||||
{Name: "some-other-ad-idp-1", Type: "activedirectory"},
|
{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-1", Type: "ldap"},
|
||||||
{Name: "some-other-ldap-idp-2", Type: "ldap"},
|
{Name: "some-other-ldap-idp-2", Type: "ldap"},
|
||||||
{Name: "some-other-oidc-idp-1", Type: "oidc"},
|
{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: "z-some-ldap-idp"}).
|
||||||
WithLDAP(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "x-some-idp"}).
|
WithLDAP(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "x-some-idp"}).
|
||||||
WithActiveDirectory(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "z-some-ad-idp"}).
|
WithActiveDirectory(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "z-some-ad-idp"}).
|
||||||
|
WithActiveDirectory(&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "y-some-ad-idp"}).
|
||||||
Build()
|
Build()
|
||||||
|
|
||||||
handler := NewHandler(idpLister)
|
handler := NewHandler(idpLister)
|
||||||
@ -108,6 +111,7 @@ func TestIDPDiscovery(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
idpLister.SetActiveDirectoryIdentityProviders([]provider.UpstreamLDAPIdentityProviderI{
|
idpLister.SetActiveDirectoryIdentityProviders([]provider.UpstreamLDAPIdentityProviderI{
|
||||||
|
&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "some-other-ad-idp-2"},
|
||||||
&oidctestutil.TestUpstreamLDAPIdentityProvider{Name: "some-other-ad-idp-1"},
|
&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) {
|
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
|
defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches
|
||||||
|
|
||||||
conn, err := p.dial(ctx)
|
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)
|
err = conn.Bind(p.c.BindUsername, p.c.BindPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.traceAuthFailure(t, err)
|
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())
|
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
|
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(
|
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(
|
||||||
"ldaps://" + env.SupervisorUpstreamActiveDirectory.Host +
|
"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,
|
"&sub=" + env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||||
),
|
),
|
||||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue),
|
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue),
|
||||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountNames,
|
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",
|
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{
|
require.ElementsMatch(t, [][]string{
|
||||||
{"BindSecretValid", "True", "Success"},
|
{"BindSecretValid", "True", "Success"},
|
||||||
{"TLSConfigurationValid", "True", "Success"},
|
{"TLSConfigurationValid", "True", "Success"},
|
||||||
{"LDAPConnectionValid", "True", "Success"},
|
{"LDAPConnectionValid", "True", "Success"},
|
||||||
{"SearchBaseFound", "True", "Success"},
|
{"SearchBaseFound", "True", expectedUserSearchReason},
|
||||||
}, conditionsSummary)
|
}, conditionsSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,7 @@ type TestLDAPUpstream struct {
|
|||||||
BindUsername string `json:"bindUsername"`
|
BindUsername string `json:"bindUsername"`
|
||||||
BindPassword string `json:"bindPassword"`
|
BindPassword string `json:"bindPassword"`
|
||||||
UserSearchBase string `json:"userSearchBase"`
|
UserSearchBase string `json:"userSearchBase"`
|
||||||
|
DefaultNamingContextSearchBase string `json:"defaultNamingContextSearchBase"`
|
||||||
GroupSearchBase string `json:"groupSearchBase"`
|
GroupSearchBase string `json:"groupSearchBase"`
|
||||||
TestUserDN string `json:"testUserDN"`
|
TestUserDN string `json:"testUserDN"`
|
||||||
TestUserCN string `json:"testUserCN"`
|
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", ""),
|
TestUserUniqueIDAttributeName: wantEnv("PINNIPED_TEST_AD_USER_UNIQUE_ID_ATTRIBUTE_NAME", ""),
|
||||||
TestUserUniqueIDAttributeValue: wantEnv("PINNIPED_TEST_AD_USER_UNIQUE_ID_ATTRIBUTE_VALUE", ""),
|
TestUserUniqueIDAttributeValue: wantEnv("PINNIPED_TEST_AD_USER_UNIQUE_ID_ATTRIBUTE_VALUE", ""),
|
||||||
TestUserSAMAccountNameValue: wantEnv("PINNIPED_TEST_AD_USERNAME_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", ""), ";")),
|
TestUserDirectGroupsDNs: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_DN", ""), ";")),
|
||||||
TestUserDirectGroupsCNs: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_CN", ""), ";")),
|
TestUserDirectGroupsCNs: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_CN", ""), ";")),
|
||||||
TestUserIndirectGroupsSAMAccountNames: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_SAMACCOUNTNAME", ""), ";")),
|
TestUserIndirectGroupsSAMAccountNames: filterEmpty(strings.Split(wantEnv("PINNIPED_TEST_AD_USER_EXPECTED_GROUPS_SAMACCOUNTNAME", ""), ";")),
|
||||||
TestDeactivatedUserSAMAccountNameValue: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_SAMACCOUNTNAME", ""),
|
TestDeactivatedUserSAMAccountNameValue: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_SAMACCOUNTNAME", ""),
|
||||||
TestDeactivatedUserPassword: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_PASSWORD", ""),
|
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)
|
sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs)
|
||||||
|
Loading…
Reference in New Issue
Block a user