extract ldap refresh search into helper function
also added an integration test for refresh failing after updating the username attribute
This commit is contained in:
parent
b5b8cab717
commit
cb60a44f8a
@ -172,26 +172,10 @@ func (p *Provider) GetConfig() ProviderConfig {
|
|||||||
func (p *Provider) PerformRefresh(ctx context.Context, userDN, expectedUsername, expectedSubject string) error {
|
func (p *Provider) PerformRefresh(ctx context.Context, userDN, expectedUsername, expectedSubject string) error {
|
||||||
t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", trace.Field{Key: "providerName", Value: p.GetName()})
|
t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", 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
|
||||||
search := p.refreshUserSearchRequest(userDN)
|
searchResult, err := p.performRefresh(ctx, userDN)
|
||||||
|
|
||||||
conn, err := p.dial(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.traceRefreshFailure(t, err)
|
p.traceRefreshFailure(t, err)
|
||||||
return fmt.Errorf(`error dialing host "%s": %w`, p.c.Host, err)
|
return err
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
err = conn.Bind(p.c.BindUsername, p.c.BindPassword)
|
|
||||||
if err != nil {
|
|
||||||
p.traceRefreshFailure(t, err)
|
|
||||||
return fmt.Errorf(`error binding as "%s" before user search: %w`, p.c.BindUsername, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
searchResult, err := conn.Search(search)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
p.traceRefreshFailure(t, err)
|
|
||||||
return fmt.Errorf(`error searching for user "%s": %w`, userDN, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any more or less than one entry, error.
|
// if any more or less than one entry, error.
|
||||||
@ -230,6 +214,28 @@ func (p *Provider) PerformRefresh(ctx context.Context, userDN, expectedUsername,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) performRefresh(ctx context.Context, userDN string) (*ldap.SearchResult, error) {
|
||||||
|
search := p.refreshUserSearchRequest(userDN)
|
||||||
|
|
||||||
|
conn, err := p.dial(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(`error dialing host "%s": %w`, p.c.Host, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
err = conn.Bind(p.c.BindUsername, p.c.BindPassword)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(`error binding as "%s" before user search: %w`, p.c.BindUsername, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResult, err := conn.Search(search)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(`error searching for user "%s": %w`, userDN, err)
|
||||||
|
}
|
||||||
|
return searchResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) dial(ctx context.Context) (Conn, error) {
|
func (p *Provider) dial(ctx context.Context) (Conn, error) {
|
||||||
tlsAddr, err := endpointaddr.Parse(p.c.Host, defaultLDAPSPort)
|
tlsAddr, err := endpointaddr.Parse(p.c.Host, defaultLDAPSPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1511,8 +1511,8 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, tt := range tests {
|
||||||
tt := test
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
t.Cleanup(ctrl.Finish)
|
t.Cleanup(ctrl.Finish)
|
||||||
|
@ -44,7 +44,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
maybeSkip func(t *testing.T)
|
maybeSkip func(t *testing.T)
|
||||||
createIDP func(t *testing.T)
|
createIDP func(t *testing.T) string
|
||||||
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client)
|
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client)
|
||||||
wantDownstreamIDTokenSubjectToMatch string
|
wantDownstreamIDTokenSubjectToMatch string
|
||||||
wantDownstreamIDTokenUsernameToMatch string
|
wantDownstreamIDTokenUsernameToMatch string
|
||||||
@ -55,16 +55,16 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
// We don't necessarily have any way to revoke the user's session on the upstream provider,
|
// We don't necessarily have any way to revoke the user's session on the upstream provider,
|
||||||
// so to cause the upstream refresh to fail we can cheat by manipulating the user's session
|
// so to cause the upstream refresh to fail we can cheat by manipulating the user's session
|
||||||
// data in such a way that it should cause the next upstream refresh attempt to fail.
|
// data in such a way that it should cause the next upstream refresh attempt to fail.
|
||||||
breakRefreshSessionData func(t *testing.T, sessionData *psession.PinnipedSession)
|
breakRefreshSessionData func(t *testing.T, sessionData *psession.PinnipedSession, idpName string)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "oidc with default username and groups claim settings",
|
name: "oidc with default username and groups claim settings",
|
||||||
maybeSkip: func(t *testing.T) {
|
maybeSkip: func(t *testing.T) {
|
||||||
// never need to skip this test
|
// never need to skip this test
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
oidcIDP := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||||
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
||||||
TLS: &idpv1alpha1.TLSSpec{
|
TLS: &idpv1alpha1.TLSSpec{
|
||||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
||||||
@ -73,9 +73,10 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
SecretName: testlib.CreateClientCredsSecret(t, env.SupervisorUpstreamOIDC.ClientID, env.SupervisorUpstreamOIDC.ClientSecret).Name,
|
SecretName: testlib.CreateClientCredsSecret(t, env.SupervisorUpstreamOIDC.ClientID, env.SupervisorUpstreamOIDC.ClientSecret).Name,
|
||||||
},
|
},
|
||||||
}, idpv1alpha1.PhaseReady)
|
}, idpv1alpha1.PhaseReady)
|
||||||
|
return oidcIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlow,
|
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlow,
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
||||||
@ -91,9 +92,9 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
maybeSkip: func(t *testing.T) {
|
maybeSkip: func(t *testing.T) {
|
||||||
// never need to skip this test
|
// never need to skip this test
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
oidcIDP := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||||
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
||||||
TLS: &idpv1alpha1.TLSSpec{
|
TLS: &idpv1alpha1.TLSSpec{
|
||||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
||||||
@ -109,9 +110,10 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
AdditionalScopes: env.SupervisorUpstreamOIDC.AdditionalScopes,
|
AdditionalScopes: env.SupervisorUpstreamOIDC.AdditionalScopes,
|
||||||
},
|
},
|
||||||
}, idpv1alpha1.PhaseReady)
|
}, idpv1alpha1.PhaseReady)
|
||||||
|
return oidcIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlow,
|
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlow,
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
||||||
@ -126,9 +128,9 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
maybeSkip: func(t *testing.T) {
|
maybeSkip: func(t *testing.T) {
|
||||||
// never need to skip this test
|
// never need to skip this test
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
oidcIDP := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||||
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
||||||
TLS: &idpv1alpha1.TLSSpec{
|
TLS: &idpv1alpha1.TLSSpec{
|
||||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
||||||
@ -140,6 +142,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
AllowPasswordGrant: true, // allow the CLI password flow for this OIDCIdentityProvider
|
AllowPasswordGrant: true, // allow the CLI password flow for this OIDCIdentityProvider
|
||||||
},
|
},
|
||||||
}, idpv1alpha1.PhaseReady)
|
}, idpv1alpha1.PhaseReady)
|
||||||
|
return oidcIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -150,7 +153,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeOIDC, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
||||||
@ -169,7 +172,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -207,6 +210,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
||||||
|
return ldapIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -217,7 +221,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||||
@ -242,7 +246,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -280,6 +284,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
||||||
|
return ldapIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -290,7 +295,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||||
@ -315,7 +320,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -353,6 +358,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
||||||
|
return ldapIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -374,7 +380,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -430,6 +436,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
requireEventually.NoError(err)
|
requireEventually.NoError(err)
|
||||||
requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, ldapIDP, expectedMsg)
|
requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, ldapIDP, expectedMsg)
|
||||||
}, time.Minute, 500*time.Millisecond)
|
}, time.Minute, 500*time.Millisecond)
|
||||||
|
return ldapIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -440,7 +447,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||||
@ -464,7 +471,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -534,6 +541,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
requireEventually.NoError(err)
|
requireEventually.NoError(err)
|
||||||
requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, ldapIDP, expectedMsg)
|
requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, ldapIDP, expectedMsg)
|
||||||
}, time.Minute, 500*time.Millisecond)
|
}, time.Minute, 500*time.Millisecond)
|
||||||
|
return ldapIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -544,7 +552,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||||
@ -571,7 +579,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("Active Directory hostname not specified")
|
t.Skip("Active Directory hostname not specified")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -594,6 +602,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
||||||
|
return adIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -604,7 +613,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||||
@ -631,7 +640,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("Active Directory hostname not specified")
|
t.Skip("Active Directory hostname not specified")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -668,6 +677,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
||||||
|
return adIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -678,7 +688,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||||
@ -706,7 +716,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("Active Directory hostname not specified")
|
t.Skip("Active Directory hostname not specified")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -747,6 +757,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
requireEventually.NoError(err)
|
requireEventually.NoError(err)
|
||||||
requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, adIDP, expectedMsg)
|
requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, adIDP, expectedMsg)
|
||||||
}, time.Minute, 500*time.Millisecond)
|
}, time.Minute, 500*time.Millisecond)
|
||||||
|
return adIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -757,7 +768,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||||
@ -784,7 +795,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("Active Directory hostname not specified")
|
t.Skip("Active Directory hostname not specified")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -840,6 +851,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
requireEventually.NoError(err)
|
requireEventually.NoError(err)
|
||||||
requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, adIDP, expectedMsg)
|
requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, adIDP, expectedMsg)
|
||||||
}, time.Minute, 500*time.Millisecond)
|
}, time.Minute, 500*time.Millisecond)
|
||||||
|
return adIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -850,7 +862,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession) {
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _ string) {
|
||||||
customSessionData := pinnipedSession.Custom
|
customSessionData := pinnipedSession.Custom
|
||||||
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType)
|
||||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||||
@ -877,7 +889,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
t.Skip("Active Directory hostname not specified")
|
t.Skip("Active Directory hostname not specified")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ad-service-account", v1.SecretTypeBasicAuth,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -900,6 +912,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
secret.Name, secret.ResourceVersion,
|
secret.Name, secret.ResourceVersion,
|
||||||
)
|
)
|
||||||
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg)
|
||||||
|
return adIDP.Name
|
||||||
},
|
},
|
||||||
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
requestAuthorizationUsingCLIPasswordFlow(t,
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
@ -914,6 +927,89 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
wantErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
wantErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
||||||
wantErrorType: "access_denied",
|
wantErrorType: "access_denied",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ldap refresh fails when username changes from email as username to dn as username",
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createIDP: func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
|
map[string]string{
|
||||||
|
v1.BasicAuthUsernameKey: env.SupervisorUpstreamLDAP.BindUsername,
|
||||||
|
v1.BasicAuthPasswordKey: env.SupervisorUpstreamLDAP.BindPassword,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ldapIDP := testlib.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{
|
||||||
|
Host: env.SupervisorUpstreamLDAP.Host,
|
||||||
|
TLS: &idpv1alpha1.TLSSpec{
|
||||||
|
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.CABundle)),
|
||||||
|
},
|
||||||
|
Bind: idpv1alpha1.LDAPIdentityProviderBind{
|
||||||
|
SecretName: secret.Name,
|
||||||
|
},
|
||||||
|
UserSearch: idpv1alpha1.LDAPIdentityProviderUserSearch{
|
||||||
|
Base: env.SupervisorUpstreamLDAP.UserSearchBase,
|
||||||
|
Filter: "",
|
||||||
|
Attributes: idpv1alpha1.LDAPIdentityProviderUserSearchAttributes{
|
||||||
|
Username: env.SupervisorUpstreamLDAP.TestUserMailAttributeName,
|
||||||
|
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupSearch: idpv1alpha1.LDAPIdentityProviderGroupSearch{
|
||||||
|
Base: env.SupervisorUpstreamLDAP.GroupSearchBase,
|
||||||
|
Filter: "",
|
||||||
|
Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||||
|
GroupName: "dn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, idpv1alpha1.LDAPPhaseReady)
|
||||||
|
expectedMsg := fmt.Sprintf(
|
||||||
|
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
||||||
|
env.SupervisorUpstreamLDAP.Host, env.SupervisorUpstreamLDAP.BindUsername,
|
||||||
|
secret.Name, secret.ResourceVersion,
|
||||||
|
)
|
||||||
|
requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg)
|
||||||
|
return ldapIDP.Name
|
||||||
|
},
|
||||||
|
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
|
||||||
|
requestAuthorizationUsingCLIPasswordFlow(t,
|
||||||
|
downstreamAuthorizeURL,
|
||||||
|
env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login
|
||||||
|
env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login
|
||||||
|
httpClient,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName string) {
|
||||||
|
// get the idp, update the config.
|
||||||
|
client := testlib.NewSupervisorClientset(t)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Create the LDAPIdentityProvider using GenerateName to get a random name.
|
||||||
|
upstreams := client.IDPV1alpha1().LDAPIdentityProviders(env.SupervisorNamespace)
|
||||||
|
ldapIDP, err := upstreams.Get(ctx, idpName, metav1.GetOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
ldapIDP.Spec.UserSearch.Attributes.Username = "dn"
|
||||||
|
|
||||||
|
_, err = upstreams.Update(ctx, ldapIDP, metav1.UpdateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
time.Sleep(10 * time.Second) // wait for controllers to pick up the change
|
||||||
|
},
|
||||||
|
// 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.SupervisorUpstreamLDAP.Host+
|
||||||
|
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||||
|
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||||
|
) + "$",
|
||||||
|
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||||
|
wantDownstreamIDTokenUsernameToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$",
|
||||||
|
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tt := test
|
tt := test
|
||||||
@ -1053,9 +1149,9 @@ func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t *tes
|
|||||||
|
|
||||||
func testSupervisorLogin(
|
func testSupervisorLogin(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
createIDP func(t *testing.T),
|
createIDP func(t *testing.T) string,
|
||||||
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client),
|
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client),
|
||||||
breakRefreshSessionData func(t *testing.T, pinnipedSession *psession.PinnipedSession),
|
breakRefreshSessionData func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName string),
|
||||||
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string,
|
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string,
|
||||||
wantErrorDescription string, wantErrorType string,
|
wantErrorDescription string, wantErrorType string,
|
||||||
) {
|
) {
|
||||||
@ -1143,7 +1239,7 @@ func testSupervisorLogin(
|
|||||||
}, 30*time.Second, 200*time.Millisecond)
|
}, 30*time.Second, 200*time.Millisecond)
|
||||||
|
|
||||||
// Create upstream IDP and wait for it to become ready.
|
// Create upstream IDP and wait for it to become ready.
|
||||||
createIDP(t)
|
idpName := createIDP(t)
|
||||||
|
|
||||||
// Perform OIDC discovery for our downstream.
|
// Perform OIDC discovery for our downstream.
|
||||||
var discovery *coreosoidc.Provider
|
var discovery *coreosoidc.Provider
|
||||||
@ -1237,7 +1333,7 @@ func testSupervisorLogin(
|
|||||||
// Next mutate the part of the session that is used during upstream refresh.
|
// Next mutate the part of the session that is used during upstream refresh.
|
||||||
pinnipedSession, ok := storedRefreshSession.GetSession().(*psession.PinnipedSession)
|
pinnipedSession, ok := storedRefreshSession.GetSession().(*psession.PinnipedSession)
|
||||||
require.True(t, ok, "should have been able to cast session data to PinnipedSession")
|
require.True(t, ok, "should have been able to cast session data to PinnipedSession")
|
||||||
breakRefreshSessionData(t, pinnipedSession)
|
breakRefreshSessionData(t, pinnipedSession, idpName)
|
||||||
|
|
||||||
// Then save the mutated Secret back to Kubernetes.
|
// Then save the mutated Secret back to Kubernetes.
|
||||||
// There is no update function, so delete and create again at the same name.
|
// There is no update function, so delete and create again at the same name.
|
||||||
|
Loading…
Reference in New Issue
Block a user