Integration test deactivated ad account

This commit is contained in:
Margo Crawford 2021-07-22 16:15:44 -07:00
parent 00978c15f7
commit 1050f39789
2 changed files with 127 additions and 58 deletions

View File

@ -47,6 +47,8 @@ func TestSupervisorLogin(t *testing.T) {
wantDownstreamIDTokenSubjectToMatch string wantDownstreamIDTokenSubjectToMatch string
wantDownstreamIDTokenUsernameToMatch string wantDownstreamIDTokenUsernameToMatch string
wantDownstreamIDTokenGroups []string wantDownstreamIDTokenGroups []string
wantErrorDescription string
wantErrorType string
}{ }{
{ {
name: "oidc with default username and groups claim settings", name: "oidc with default username and groups claim settings",
@ -153,6 +155,7 @@ func TestSupervisorLogin(t *testing.T) {
env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login
env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login
httpClient, httpClient,
false,
) )
}, },
// 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
@ -218,6 +221,7 @@ func TestSupervisorLogin(t *testing.T) {
env.SupervisorUpstreamLDAP.TestUserCN, // username to present to server during login env.SupervisorUpstreamLDAP.TestUserCN, // username to present to server during login
env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login
httpClient, httpClient,
false,
) )
}, },
// 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
@ -271,6 +275,7 @@ func TestSupervisorLogin(t *testing.T) {
env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue, // username to present to server during login env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue, // username to present to server during login
env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login
httpClient, httpClient,
false,
) )
}, },
// 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
@ -283,6 +288,53 @@ func TestSupervisorLogin(t *testing.T) {
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue), wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserSAMAccountNameValue),
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountNames, wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountNames,
}, },
{
name: "logging in to activedirectory with a deactivated user fails",
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,
},
}, 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.TestDeactivatedUserSAMAccountNameValue, // username to present to server during login
env.SupervisorUpstreamActiveDirectory.TestDeactivatedUserPassword, // password to present to server during login
httpClient,
true,
)
},
wantErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
wantErrorType: "access_denied",
},
} }
for _, test := range tests { for _, test := range tests {
tt := test tt := test
@ -295,6 +347,7 @@ func TestSupervisorLogin(t *testing.T) {
tt.wantDownstreamIDTokenSubjectToMatch, tt.wantDownstreamIDTokenSubjectToMatch,
tt.wantDownstreamIDTokenUsernameToMatch, tt.wantDownstreamIDTokenUsernameToMatch,
tt.wantDownstreamIDTokenGroups, tt.wantDownstreamIDTokenGroups,
tt.wantErrorDescription, tt.wantErrorType,
) )
}) })
} }
@ -355,6 +408,7 @@ func testSupervisorLogin(
createIDP func(t *testing.T), createIDP func(t *testing.T),
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client), requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client),
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string,
wantErrorDescription string, wantErrorType string,
) { ) {
env := testlib.IntegrationEnv(t) env := testlib.IntegrationEnv(t)
@ -482,6 +536,7 @@ func testSupervisorLogin(
// Expect that our callback handler was invoked. // Expect that our callback handler was invoked.
callback := localCallbackServer.waitForCallback(10 * time.Second) callback := localCallbackServer.waitForCallback(10 * time.Second)
t.Logf("got callback request: %s", testlib.MaskTokens(callback.URL.String())) t.Logf("got callback request: %s", testlib.MaskTokens(callback.URL.String()))
if wantErrorType == "" {
require.Equal(t, stateParam.String(), callback.URL.Query().Get("state")) require.Equal(t, stateParam.String(), callback.URL.Query().Get("state"))
require.ElementsMatch(t, []string{"openid", "pinniped:request-audience", "offline_access"}, strings.Split(callback.URL.Query().Get("scope"), " ")) require.ElementsMatch(t, []string{"openid", "pinniped:request-audience", "offline_access"}, strings.Split(callback.URL.Query().Get("scope"), " "))
authcode := callback.URL.Query().Get("code") authcode := callback.URL.Query().Get("code")
@ -516,6 +571,12 @@ func testSupervisorLogin(
// token exchange on the refreshed token // token exchange on the refreshed token
doTokenExchange(t, &downstreamOAuth2Config, refreshedTokenResponse, httpClient, discovery) doTokenExchange(t, &downstreamOAuth2Config, refreshedTokenResponse, httpClient, discovery)
} else {
errorDescription := callback.URL.Query().Get("error_description")
errorType := callback.URL.Query().Get("error")
require.Equal(t, errorDescription, wantErrorDescription)
require.Equal(t, errorType, wantErrorType)
}
} }
func verifyTokenResponse( func verifyTokenResponse(
@ -602,7 +663,7 @@ func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAutho
browsertest.WaitForURL(t, page, callbackURLPattern) browsertest.WaitForURL(t, page, callbackURLPattern)
} }
func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAuthorizeURL, upstreamUsername, upstreamPassword string, httpClient *http.Client) { func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAuthorizeURL, upstreamUsername, upstreamPassword string, httpClient *http.Client, wantErr bool) {
t.Helper() t.Helper()
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Minute) ctx, cancelFunc := context.WithTimeout(context.Background(), time.Minute)
@ -645,7 +706,11 @@ func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAutho
redirectLocation := authResponse.Header.Get("Location") redirectLocation := authResponse.Header.Get("Location")
require.Contains(t, redirectLocation, "127.0.0.1") require.Contains(t, redirectLocation, "127.0.0.1")
require.Contains(t, redirectLocation, "/callback") require.Contains(t, redirectLocation, "/callback")
if wantErr {
require.Contains(t, redirectLocation, "error_description")
} else {
require.Contains(t, redirectLocation, "code=") require.Contains(t, redirectLocation, "code=")
}
// Follow the redirect. // Follow the redirect.
callbackRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, redirectLocation, nil) callbackRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, redirectLocation, nil)

View File

@ -100,6 +100,8 @@ type TestLDAPUpstream struct {
TestUserDirectGroupsDNs []string `json:"testUserDirectGroupsDNs"` //nolint:golint // this is "distinguished names", not "DNS" TestUserDirectGroupsDNs []string `json:"testUserDirectGroupsDNs"` //nolint:golint // this is "distinguished names", not "DNS"
TestUserSAMAccountNameValue string `json:"testUserSAMAccountNameValue"` TestUserSAMAccountNameValue string `json:"testUserSAMAccountNameValue"`
TestUserIndirectGroupsSAMAccountNames []string `json:"TestUserIndirectGroupsSAMAccountNames"` TestUserIndirectGroupsSAMAccountNames []string `json:"TestUserIndirectGroupsSAMAccountNames"`
TestDeactivatedUserSAMAccountNameValue string `json:"TestDeactivatedUserSAMAccountNameValue"`
TestDeactivatedUserPassword string `json:"TestDeactivatedUserPassword"`
} }
// ProxyEnv returns a set of environment variable strings (e.g., to combine with os.Environ()) which set up the configured test HTTP proxy. // ProxyEnv returns a set of environment variable strings (e.g., to combine with os.Environ()) which set up the configured test HTTP proxy.
@ -282,6 +284,8 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
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", ""),
TestDeactivatedUserPassword: wantEnv("PINNIPED_TEST_DEACTIVATED_AD_USER_PASSWORD", ""),
} }
sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs) sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs)