Merge branch 'main' into reenable-max-inflight-checks
This commit is contained in:
commit
93ba1b54f2
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package callback provides a handler for the OIDC callback endpoint.
|
// Package callback provides a handler for the OIDC callback endpoint.
|
||||||
@ -255,7 +255,7 @@ func getSubjectAndUsernameFromUpstreamIDToken(
|
|||||||
func getGroupsFromUpstreamIDToken(
|
func getGroupsFromUpstreamIDToken(
|
||||||
upstreamIDPConfig provider.UpstreamOIDCIdentityProviderI,
|
upstreamIDPConfig provider.UpstreamOIDCIdentityProviderI,
|
||||||
idTokenClaims map[string]interface{},
|
idTokenClaims map[string]interface{},
|
||||||
) (interface{}, error) {
|
) ([]string, error) {
|
||||||
groupsClaim := upstreamIDPConfig.GetGroupsClaim()
|
groupsClaim := upstreamIDPConfig.GetGroupsClaim()
|
||||||
if groupsClaim == "" {
|
if groupsClaim == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -272,9 +272,8 @@ func getGroupsFromUpstreamIDToken(
|
|||||||
return nil, nil // the upstream IDP may have omitted the claim if the user has no groups
|
return nil, nil // the upstream IDP may have omitted the claim if the user has no groups
|
||||||
}
|
}
|
||||||
|
|
||||||
groupsAsArray, okAsArray := groupsAsInterface.([]string)
|
groupsAsArray, okAsArray := extractGroups(groupsAsInterface)
|
||||||
groupsAsString, okAsString := groupsAsInterface.(string)
|
if !okAsArray {
|
||||||
if !okAsArray && !okAsString {
|
|
||||||
plog.Warning(
|
plog.Warning(
|
||||||
"groups claim in upstream ID token has invalid format",
|
"groups claim in upstream ID token has invalid format",
|
||||||
"upstreamName", upstreamIDPConfig.GetName(),
|
"upstreamName", upstreamIDPConfig.GetName(),
|
||||||
@ -284,13 +283,40 @@ func getGroupsFromUpstreamIDToken(
|
|||||||
return nil, httperr.New(http.StatusUnprocessableEntity, "groups claim in upstream ID token has invalid format")
|
return nil, httperr.New(http.StatusUnprocessableEntity, "groups claim in upstream ID token has invalid format")
|
||||||
}
|
}
|
||||||
|
|
||||||
if okAsArray {
|
|
||||||
return groupsAsArray, nil
|
return groupsAsArray, nil
|
||||||
}
|
}
|
||||||
return groupsAsString, nil
|
|
||||||
|
func extractGroups(groupsAsInterface interface{}) ([]string, bool) {
|
||||||
|
groupsAsString, okAsString := groupsAsInterface.(string)
|
||||||
|
if okAsString {
|
||||||
|
return []string{groupsAsString}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDownstreamSession(subject string, username string, groups interface{}) *openid.DefaultSession {
|
groupsAsStringArray, okAsStringArray := groupsAsInterface.([]string)
|
||||||
|
if okAsStringArray {
|
||||||
|
return groupsAsStringArray, true
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsAsInterfaceArray, okAsArray := groupsAsInterface.([]interface{})
|
||||||
|
if !okAsArray {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupsAsStrings []string
|
||||||
|
for _, groupAsInterface := range groupsAsInterfaceArray {
|
||||||
|
groupAsString, okAsString := groupAsInterface.(string)
|
||||||
|
if !okAsString {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if groupAsString != "" {
|
||||||
|
groupsAsStrings = append(groupsAsStrings, groupAsString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupsAsStrings, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDownstreamSession(subject string, username string, groups []string) *openid.DefaultSession {
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
openIDSession := &openid.DefaultSession{
|
openIDSession := &openid.DefaultSession{
|
||||||
Claims: &jwt.IDTokenClaims{
|
Claims: &jwt.IDTokenClaims{
|
||||||
@ -299,11 +325,12 @@ func makeDownstreamSession(subject string, username string, groups interface{})
|
|||||||
AuthTime: now,
|
AuthTime: now,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if groups == nil {
|
||||||
|
groups = []string{}
|
||||||
|
}
|
||||||
openIDSession.Claims.Extra = map[string]interface{}{
|
openIDSession.Claims.Extra = map[string]interface{}{
|
||||||
oidc.DownstreamUsernameClaim: username,
|
oidc.DownstreamUsernameClaim: username,
|
||||||
}
|
oidc.DownstreamGroupsClaim: groups,
|
||||||
if groups != nil {
|
|
||||||
openIDSession.Claims.Extra[oidc.DownstreamGroupsClaim] = groups
|
|
||||||
}
|
}
|
||||||
return openIDSession
|
return openIDSession
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package callback
|
package callback
|
||||||
@ -134,7 +134,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
wantDownstreamGrantedScopes []string
|
wantDownstreamGrantedScopes []string
|
||||||
wantDownstreamIDTokenSubject string
|
wantDownstreamIDTokenSubject string
|
||||||
wantDownstreamIDTokenUsername string
|
wantDownstreamIDTokenUsername string
|
||||||
wantDownstreamIDTokenGroups interface{}
|
wantDownstreamIDTokenGroups []string
|
||||||
wantDownstreamRequestedScopes []string
|
wantDownstreamRequestedScopes []string
|
||||||
wantDownstreamNonce string
|
wantDownstreamNonce string
|
||||||
wantDownstreamPKCEChallenge string
|
wantDownstreamPKCEChallenge string
|
||||||
@ -172,7 +172,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
wantBody: "",
|
wantBody: "",
|
||||||
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
||||||
wantDownstreamIDTokenUsername: upstreamIssuer + "?sub=" + upstreamSubject,
|
wantDownstreamIDTokenUsername: upstreamIssuer + "?sub=" + upstreamSubject,
|
||||||
wantDownstreamIDTokenGroups: nil,
|
wantDownstreamIDTokenGroups: []string{},
|
||||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
wantDownstreamNonce: downstreamNonce,
|
wantDownstreamNonce: downstreamNonce,
|
||||||
@ -210,7 +210,26 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
wantBody: "",
|
wantBody: "",
|
||||||
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
||||||
wantDownstreamIDTokenUsername: upstreamUsername,
|
wantDownstreamIDTokenUsername: upstreamUsername,
|
||||||
wantDownstreamIDTokenGroups: "notAnArrayGroup1 notAnArrayGroup2",
|
wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"},
|
||||||
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
|
wantDownstreamNonce: downstreamNonce,
|
||||||
|
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||||
|
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||||
|
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "upstream IDP's configured groups claim in the ID token is a slice of interfaces",
|
||||||
|
idp: happyUpstream().WithIDTokenClaim(upstreamGroupsClaim, []interface{}{"group1", "group2"}).Build(),
|
||||||
|
method: http.MethodGet,
|
||||||
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
|
csrfCookie: happyCSRFCookie,
|
||||||
|
wantStatus: http.StatusFound,
|
||||||
|
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||||
|
wantBody: "",
|
||||||
|
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + upstreamSubject,
|
||||||
|
wantDownstreamIDTokenUsername: upstreamUsername,
|
||||||
|
wantDownstreamIDTokenGroups: []string{"group1", "group2"},
|
||||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
wantDownstreamNonce: downstreamNonce,
|
wantDownstreamNonce: downstreamNonce,
|
||||||
@ -437,6 +456,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
wantDownstreamIDTokenUsername: upstreamUsername,
|
wantDownstreamIDTokenUsername: upstreamUsername,
|
||||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||||
|
wantDownstreamIDTokenGroups: []string{},
|
||||||
wantDownstreamNonce: downstreamNonce,
|
wantDownstreamNonce: downstreamNonce,
|
||||||
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
|
||||||
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
|
||||||
@ -482,6 +502,26 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
wantBody: "Unprocessable Entity: groups claim in upstream ID token has invalid format\n",
|
wantBody: "Unprocessable Entity: groups claim in upstream ID token has invalid format\n",
|
||||||
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "upstream ID token contains groups claim where one element is invalid",
|
||||||
|
idp: happyUpstream().WithIDTokenClaim(upstreamGroupsClaim, []interface{}{"foo", 7}).Build(),
|
||||||
|
method: http.MethodGet,
|
||||||
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
|
csrfCookie: happyCSRFCookie,
|
||||||
|
wantStatus: http.StatusUnprocessableEntity,
|
||||||
|
wantBody: "Unprocessable Entity: groups claim in upstream ID token has invalid format\n",
|
||||||
|
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "upstream ID token contains groups claim with invalid null type",
|
||||||
|
idp: happyUpstream().WithIDTokenClaim(upstreamGroupsClaim, nil).Build(),
|
||||||
|
method: http.MethodGet,
|
||||||
|
path: newRequestPath().WithState(happyState).String(),
|
||||||
|
csrfCookie: happyCSRFCookie,
|
||||||
|
wantStatus: http.StatusUnprocessableEntity,
|
||||||
|
wantBody: "Unprocessable Entity: groups claim in upstream ID token has invalid format\n",
|
||||||
|
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
@ -779,7 +819,7 @@ func validateAuthcodeStorage(
|
|||||||
wantDownstreamGrantedScopes []string,
|
wantDownstreamGrantedScopes []string,
|
||||||
wantDownstreamIDTokenSubject string,
|
wantDownstreamIDTokenSubject string,
|
||||||
wantDownstreamIDTokenUsername string,
|
wantDownstreamIDTokenUsername string,
|
||||||
wantDownstreamIDTokenGroups interface{},
|
wantDownstreamIDTokenGroups []string,
|
||||||
wantDownstreamRequestedScopes []string,
|
wantDownstreamRequestedScopes []string,
|
||||||
) (*fosite.Request, *openid.DefaultSession) {
|
) (*fosite.Request, *openid.DefaultSession) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@ -818,23 +858,10 @@ func validateAuthcodeStorage(
|
|||||||
// Check the user's identity, which are put into the downstream ID token's subject, username and groups claims.
|
// Check the user's identity, which are put into the downstream ID token's subject, username and groups claims.
|
||||||
require.Equal(t, wantDownstreamIDTokenSubject, actualClaims.Subject)
|
require.Equal(t, wantDownstreamIDTokenSubject, actualClaims.Subject)
|
||||||
require.Equal(t, wantDownstreamIDTokenUsername, actualClaims.Extra["username"])
|
require.Equal(t, wantDownstreamIDTokenUsername, actualClaims.Extra["username"])
|
||||||
if wantDownstreamIDTokenGroups != nil { //nolint:nestif // there are some nested if's here but its probably fine for a test
|
|
||||||
require.Len(t, actualClaims.Extra, 2)
|
require.Len(t, actualClaims.Extra, 2)
|
||||||
wantArray, ok := wantDownstreamIDTokenGroups.([]string)
|
actualDownstreamIDTokenGroups := actualClaims.Extra["groups"]
|
||||||
if ok {
|
require.NotNil(t, actualDownstreamIDTokenGroups)
|
||||||
require.ElementsMatch(t, wantArray, actualClaims.Extra["groups"])
|
require.ElementsMatch(t, wantDownstreamIDTokenGroups, actualDownstreamIDTokenGroups)
|
||||||
} else {
|
|
||||||
wantString, ok := wantDownstreamIDTokenGroups.(string)
|
|
||||||
if ok {
|
|
||||||
require.Equal(t, wantString, actualClaims.Extra["groups"])
|
|
||||||
} else {
|
|
||||||
require.Fail(t, "wantDownstreamIDTokenGroups should be of type: either []string or string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
require.Len(t, actualClaims.Extra, 1)
|
|
||||||
require.NotContains(t, actualClaims.Extra, "groups")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the rest of the downstream ID token's claims. Fosite wants us to set these (in UTC time).
|
// Check the rest of the downstream ID token's claims. Fosite wants us to set these (in UTC time).
|
||||||
testutil.RequireTimeInDelta(t, time.Now().UTC(), actualClaims.RequestedAt, timeComparisonFudgeFactor)
|
testutil.RequireTimeInDelta(t, time.Now().UTC(), actualClaims.RequestedAt, timeComparisonFudgeFactor)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package upstreamoidc implements an abstraction of upstream OIDC provider interactions.
|
// Package upstreamoidc implements an abstraction of upstream OIDC provider interactions.
|
||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"go.pinniped.dev/internal/httputil/httperr"
|
"go.pinniped.dev/internal/httputil/httperr"
|
||||||
"go.pinniped.dev/internal/oidc"
|
"go.pinniped.dev/internal/oidc"
|
||||||
"go.pinniped.dev/internal/oidc/provider"
|
"go.pinniped.dev/internal/oidc/provider"
|
||||||
|
"go.pinniped.dev/internal/plog"
|
||||||
"go.pinniped.dev/pkg/oidcclient/nonce"
|
"go.pinniped.dev/pkg/oidcclient/nonce"
|
||||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
||||||
"go.pinniped.dev/pkg/oidcclient/pkce"
|
"go.pinniped.dev/pkg/oidcclient/pkce"
|
||||||
@ -101,10 +102,12 @@ func (p *ProviderConfig) ValidateToken(ctx context.Context, tok *oauth2.Token, e
|
|||||||
if err := validated.Claims(&validatedClaims); err != nil {
|
if err := validated.Claims(&validatedClaims); err != nil {
|
||||||
return nil, httperr.Wrap(http.StatusInternalServerError, "could not unmarshal id token claims", err)
|
return nil, httperr.Wrap(http.StatusInternalServerError, "could not unmarshal id token claims", err)
|
||||||
}
|
}
|
||||||
|
plog.All("claims from ID token", "providerName", p.Name, "claims", validatedClaims)
|
||||||
|
|
||||||
if err := p.fetchUserInfo(ctx, tok, validatedClaims); err != nil {
|
if err := p.fetchUserInfo(ctx, tok, validatedClaims); err != nil {
|
||||||
return nil, httperr.Wrap(http.StatusInternalServerError, "could not fetch user info claims", err)
|
return nil, httperr.Wrap(http.StatusInternalServerError, "could not fetch user info claims", err)
|
||||||
}
|
}
|
||||||
|
plog.All("claims from ID token and userinfo", "providerName", p.Name, "claims", validatedClaims)
|
||||||
|
|
||||||
return &oidctypes.Token{
|
return &oidctypes.Token{
|
||||||
AccessToken: &oidctypes.AccessToken{
|
AccessToken: &oidctypes.AccessToken{
|
||||||
|
@ -292,14 +292,12 @@ func TestE2EFullIntegration(t *testing.T) {
|
|||||||
require.NotNil(t, token)
|
require.NotNil(t, token)
|
||||||
|
|
||||||
idTokenClaims := token.IDToken.Claims
|
idTokenClaims := token.IDToken.Claims
|
||||||
username := idTokenClaims[oidc.DownstreamUsernameClaim].(string)
|
require.Equal(t, env.SupervisorTestUpstream.Username, idTokenClaims[oidc.DownstreamUsernameClaim])
|
||||||
groups, _ := idTokenClaims[oidc.DownstreamGroupsClaim].([]string)
|
|
||||||
|
|
||||||
require.Equal(t, env.SupervisorTestUpstream.Username, username)
|
// The groups claim in the file ends up as an []interface{}, so adjust our expectation to match.
|
||||||
if len(env.SupervisorTestUpstream.ExpectedGroups) == 0 {
|
expectedGroups := make([]interface{}, 0, len(env.SupervisorTestUpstream.ExpectedGroups))
|
||||||
// We only put a groups claim in our downstream ID token if we got groups from the upstream.
|
for _, g := range env.SupervisorTestUpstream.ExpectedGroups {
|
||||||
require.Nil(t, groups)
|
expectedGroups = append(expectedGroups, g)
|
||||||
} else {
|
|
||||||
require.Equal(t, env.SupervisorTestUpstream.ExpectedGroups, groups)
|
|
||||||
}
|
}
|
||||||
|
require.Equal(t, expectedGroups, idTokenClaims[oidc.DownstreamGroupsClaim])
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,19 @@ func TestKubeClientOwnerRef(t *testing.T) {
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: update middleware code to not set owner references on cluster-scoped objects.
|
||||||
|
//
|
||||||
|
// The Kube 1.20 garbage collector asserts some new behavior in regards to invalid owner
|
||||||
|
// references (i.e., when you have a namespace-scoped owner references for a cluster-scoped
|
||||||
|
// dependent, the cluster-scoped dependent is not removed). We also found a bug in the 1.20
|
||||||
|
// garbage collector where namespace-scoped dependents are not garbage collected if their owner
|
||||||
|
// had been used as an invalid owner reference before - this bug causes our test to fallover
|
||||||
|
// because we are setting a namespace-scoped owner ref on this APIService.
|
||||||
|
//
|
||||||
|
// We believe that the best way to get around this problem is to update our kubeclient code to
|
||||||
|
// never set owner references on cluster-scoped objects. After we do that, we will uncomment this
|
||||||
|
// part of the test.
|
||||||
|
if false {
|
||||||
// sanity check API service client
|
// sanity check API service client
|
||||||
apiService, err := ownerRefClient.Aggregation.ApiregistrationV1().APIServices().Create(
|
apiService, err := ownerRefClient.Aggregation.ApiregistrationV1().APIServices().Create(
|
||||||
ctx,
|
ctx,
|
||||||
@ -160,6 +173,7 @@ func TestKubeClientOwnerRef(t *testing.T) {
|
|||||||
_, err := ownerRefClient.Aggregation.ApiregistrationV1().APIServices().Get(ctx, apiService.Name, metav1.GetOptions{})
|
_, err := ownerRefClient.Aggregation.ApiregistrationV1().APIServices().Get(ctx, apiService.Name, metav1.GetOptions{})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// sanity check concierge client
|
// sanity check concierge client
|
||||||
credentialIssuer, err := ownerRefClient.PinnipedConcierge.ConfigV1alpha1().CredentialIssuers(namespace.Name).Create(
|
credentialIssuer, err := ownerRefClient.PinnipedConcierge.ConfigV1alpha1().CredentialIssuers(namespace.Name).Create(
|
||||||
@ -244,16 +258,15 @@ func hasOwnerRef(t *testing.T, obj metav1.Object, ref metav1.OwnerReference) {
|
|||||||
func isEventuallyDeleted(t *testing.T, f func() error) {
|
func isEventuallyDeleted(t *testing.T, f func() error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
require.Eventually(t, func() bool {
|
library.RequireEventuallyWithoutError(t, func() (bool, error) {
|
||||||
err := f()
|
err := f()
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
return false
|
return false, nil
|
||||||
case errors.IsNotFound(err):
|
case errors.IsNotFound(err):
|
||||||
return true
|
return true, nil
|
||||||
default:
|
default:
|
||||||
require.NoError(t, err)
|
return false, err
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}, time.Minute, time.Second)
|
}, time.Minute, time.Second)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
|
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username"}
|
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username", "groups"}
|
||||||
verifyTokenResponse(t, tokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, nonceParam, expectedIDTokenClaims)
|
verifyTokenResponse(t, tokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, nonceParam, expectedIDTokenClaims)
|
||||||
|
|
||||||
// token exchange on the original token
|
// token exchange on the original token
|
||||||
|
26
test/library/assertions.go
Normal file
26
test/library/assertions.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package library
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequireEventuallyWithoutError is a wrapper around require.Eventually() that allows the caller to
|
||||||
|
// return an error from the condition function. If the condition function returns an error at any
|
||||||
|
// point, the assertion will immediately fail.
|
||||||
|
func RequireEventuallyWithoutError(
|
||||||
|
t *testing.T,
|
||||||
|
f func() (bool, error),
|
||||||
|
waitFor time.Duration,
|
||||||
|
tick time.Duration,
|
||||||
|
msgAndArgs ...interface{},
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, wait.PollImmediate(tick, waitFor, f), msgAndArgs...)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user