Add GetUser() interface and implement LoginFromCallback() for GitHub

ALso fixed some of the GitHub test helpers
This commit is contained in:
Ryan Richard
2024-05-22 21:21:45 -05:00
committed by Joshua Casey
parent b7f79f0adc
commit 49c468f00a
9 changed files with 422 additions and 165 deletions
@@ -13,6 +13,21 @@ import (
"go.pinniped.dev/internal/idtransform"
)
// ExchangeAuthcodeArgs is used to spy on calls to
// TestUpstreamGitHubIdentityProvider.ExchangeAuthcodeFunc().
type ExchangeAuthcodeArgs struct {
Ctx context.Context
Authcode string
RedirectURI string
}
// GetUserArgs is used to spy on calls to
// TestUpstreamGitHubIdentityProvider.GetUserFunc().
type GetUserArgs struct {
Ctx context.Context
AccessToken string
}
type TestUpstreamGitHubIdentityProviderBuilder struct {
name string
resourceUID types.UID
@@ -24,10 +39,10 @@ type TestUpstreamGitHubIdentityProviderBuilder struct {
groupNameAttribute v1alpha1.GitHubGroupNameAttribute
allowedOrganizations []string
authorizationURL string
// Assertions stuff
authcodeExchangeErr error
accessToken string
authcodeExchangeErr error
accessToken string
getUserErr error
getUserUser *upstreamprovider.GitHubUser
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithName(value string) *TestUpstreamGitHubIdentityProviderBuilder {
@@ -80,8 +95,18 @@ func (u *TestUpstreamGitHubIdentityProviderBuilder) WithAccessToken(token string
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithEmptyAccessToken() *TestUpstreamGitHubIdentityProviderBuilder {
u.accessToken = ""
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithAuthcodeExchangeError(err error) *TestUpstreamGitHubIdentityProviderBuilder {
u.authcodeExchangeErr = err
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithUser(user *upstreamprovider.GitHubUser) *TestUpstreamGitHubIdentityProviderBuilder {
u.getUserUser = user
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithGetUserError(err error) *TestUpstreamGitHubIdentityProviderBuilder {
u.getUserErr = err
return u
}
@@ -96,8 +121,8 @@ func (u *TestUpstreamGitHubIdentityProviderBuilder) Build() *TestUpstreamGitHubI
}
return &TestUpstreamGitHubIdentityProvider{
Name: u.name,
ResourceUID: u.resourceUID,
ClientID: u.clientID,
ResourceUID: u.resourceUID,
Scopes: u.scopes,
DisplayNameForFederationDomain: u.displayNameForFederationDomain,
TransformsForFederationDomain: u.transformsForFederationDomain,
@@ -105,7 +130,12 @@ func (u *TestUpstreamGitHubIdentityProviderBuilder) Build() *TestUpstreamGitHubI
GroupNameAttribute: u.groupNameAttribute,
AllowedOrganizations: u.allowedOrganizations,
AuthorizationURL: u.authorizationURL,
GetUserFunc: func(ctx context.Context, accessToken string) (*upstreamprovider.GitHubUser, error) {
if u.getUserErr != nil {
return nil, u.getUserErr
}
return u.getUserUser, nil
},
ExchangeAuthcodeFunc: func(ctx context.Context, authcode string) (string, error) {
if u.authcodeExchangeErr != nil {
return "", u.authcodeExchangeErr
@@ -130,16 +160,14 @@ type TestUpstreamGitHubIdentityProvider struct {
GroupNameAttribute v1alpha1.GitHubGroupNameAttribute
AllowedOrganizations []string
AuthorizationURL string
GetUserFunc func(ctx context.Context, accessToken string) (*upstreamprovider.GitHubUser, error)
ExchangeAuthcodeFunc func(ctx context.Context, authcode string) (string, error)
authcodeExchangeErr error
ExchangeAuthcodeFunc func(
ctx context.Context,
authcode string,
) (string, error)
// Fields for tracking actual calls make to mock functions.
exchangeAuthcodeCallCount int
exchangeAuthcodeArgs []*ExchangeAuthcodeArgs
getUserCallCount int
getUserArgs []*GetUserArgs
}
var _ upstreamprovider.UpstreamGithubIdentityProviderI = &TestUpstreamGitHubIdentityProvider{}
@@ -203,3 +231,26 @@ func (u *TestUpstreamGitHubIdentityProvider) ExchangeAuthcodeArgs(call int) *Exc
}
return u.exchangeAuthcodeArgs[call]
}
func (u *TestUpstreamGitHubIdentityProvider) GetUser(ctx context.Context, accessToken string) (*upstreamprovider.GitHubUser, error) {
if u.getUserArgs == nil {
u.getUserArgs = make([]*GetUserArgs, 0)
}
u.getUserCallCount++
u.getUserArgs = append(u.getUserArgs, &GetUserArgs{
Ctx: ctx,
AccessToken: accessToken,
})
return u.GetUserFunc(ctx, accessToken)
}
func (u *TestUpstreamGitHubIdentityProvider) GetUserCallCount() int {
return u.getUserCallCount
}
func (u *TestUpstreamGitHubIdentityProvider) GetUserArgs(call int) *GetUserArgs {
if u.getUserArgs == nil {
u.getUserArgs = make([]*GetUserArgs, 0)
}
return u.getUserArgs[call]
}
@@ -18,9 +18,9 @@ import (
oidcpkce "go.pinniped.dev/pkg/oidcclient/pkce"
)
// ExchangeAuthcodeArgs is used to spy on calls to
// ExchangeAuthcodeAndValidateTokenArgs is used to spy on calls to
// TestUpstreamOIDCIdentityProvider.ExchangeAuthcodeAndValidateTokensFunc().
type ExchangeAuthcodeArgs struct {
type ExchangeAuthcodeAndValidateTokenArgs struct {
Ctx context.Context
Authcode string
PKCECodeVerifier oidcpkce.Code
@@ -101,7 +101,7 @@ type TestUpstreamOIDCIdentityProvider struct {
// Fields for tracking actual calls make to mock functions.
exchangeAuthcodeAndValidateTokensCallCount int
exchangeAuthcodeAndValidateTokensArgs []*ExchangeAuthcodeArgs
exchangeAuthcodeAndValidateTokensArgs []*ExchangeAuthcodeAndValidateTokenArgs
passwordCredentialsGrantAndValidateTokensCallCount int
passwordCredentialsGrantAndValidateTokensArgs []*PasswordCredentialsGrantAndValidateTokensArgs
performRefreshCallCount int
@@ -180,10 +180,10 @@ func (u *TestUpstreamOIDCIdentityProvider) ExchangeAuthcodeAndValidateTokens(
redirectURI string,
) (*oidctypes.Token, error) {
if u.exchangeAuthcodeAndValidateTokensArgs == nil {
u.exchangeAuthcodeAndValidateTokensArgs = make([]*ExchangeAuthcodeArgs, 0)
u.exchangeAuthcodeAndValidateTokensArgs = make([]*ExchangeAuthcodeAndValidateTokenArgs, 0)
}
u.exchangeAuthcodeAndValidateTokensCallCount++
u.exchangeAuthcodeAndValidateTokensArgs = append(u.exchangeAuthcodeAndValidateTokensArgs, &ExchangeAuthcodeArgs{
u.exchangeAuthcodeAndValidateTokensArgs = append(u.exchangeAuthcodeAndValidateTokensArgs, &ExchangeAuthcodeAndValidateTokenArgs{
Ctx: ctx,
Authcode: authcode,
PKCECodeVerifier: pkceCodeVerifier,
@@ -197,9 +197,9 @@ func (u *TestUpstreamOIDCIdentityProvider) ExchangeAuthcodeAndValidateTokensCall
return u.exchangeAuthcodeAndValidateTokensCallCount
}
func (u *TestUpstreamOIDCIdentityProvider) ExchangeAuthcodeAndValidateTokensArgs(call int) *ExchangeAuthcodeArgs {
func (u *TestUpstreamOIDCIdentityProvider) ExchangeAuthcodeAndValidateTokensArgs(call int) *ExchangeAuthcodeAndValidateTokenArgs {
if u.exchangeAuthcodeAndValidateTokensArgs == nil {
u.exchangeAuthcodeAndValidateTokensArgs = make([]*ExchangeAuthcodeArgs, 0)
u.exchangeAuthcodeAndValidateTokensArgs = make([]*ExchangeAuthcodeAndValidateTokenArgs, 0)
}
return u.exchangeAuthcodeAndValidateTokensArgs[call]
}
@@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/require"
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedgithub"
@@ -264,48 +263,59 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToPasswordCredentialsG
)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToExchangeAuthcodeAndValidateTokens(
func (b *UpstreamIDPListerBuilder) RequireExactlyOneOIDCAuthcodeExchange(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedArgs *oidctestutil.ExchangeAuthcodeAndValidateTokenArgs,
) {
t.Helper()
var actualArgs *oidctestutil.ExchangeAuthcodeAndValidateTokenArgs
var actualNameOfUpstreamWhichMadeCall string
actualCallCount := 0
for _, upstream := range b.upstreamOIDCIdentityProviders {
callCountOnThisUpstream := upstream.ExchangeAuthcodeAndValidateTokensCallCount()
actualCallCount += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = upstream.ExchangeAuthcodeAndValidateTokensArgs(0)
}
}
require.Equal(t, 1, actualCallCount,
"expected exactly one call to OIDC ExchangeAuthcodeAndValidateTokens()",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"OIDC ExchangeAuthcodeAndValidateTokens() was called on the wrong upstream name",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneGitHubAuthcodeExchange(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedPerformedByUpstreamType idpdiscoveryv1alpha1.IDPType,
expectedArgs *oidctestutil.ExchangeAuthcodeArgs,
) {
t.Helper()
var actualArgs *oidctestutil.ExchangeAuthcodeArgs
var actualNameOfUpstreamWhichMadeCall string
var actualTypeOfUpstreamWhichMadeCall idpdiscoveryv1alpha1.IDPType
actualCallCountAcrossAllOIDCAndGitHubUpstreams := 0
for _, upstreamOIDC := range b.upstreamOIDCIdentityProviders {
callCountOnThisUpstream := upstreamOIDC.ExchangeAuthcodeAndValidateTokensCallCount()
actualCallCountAcrossAllOIDCAndGitHubUpstreams += callCountOnThisUpstream
actualCallCount := 0
for _, upstream := range b.upstreamGitHubIdentityProviders {
callCountOnThisUpstream := upstream.ExchangeAuthcodeCallCount()
actualCallCount += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstreamOIDC.Name
actualTypeOfUpstreamWhichMadeCall = idpdiscoveryv1alpha1.IDPTypeOIDC
actualArgs = upstreamOIDC.ExchangeAuthcodeAndValidateTokensArgs(0)
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = upstream.ExchangeAuthcodeArgs(0)
}
}
for _, upstreamGitHub := range b.upstreamGitHubIdentityProviders {
callCountOnThisUpstream := upstreamGitHub.ExchangeAuthcodeCallCount()
actualCallCountAcrossAllOIDCAndGitHubUpstreams += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstreamGitHub.Name
actualTypeOfUpstreamWhichMadeCall = idpdiscoveryv1alpha1.IDPTypeGitHub
actualArgs = upstreamGitHub.ExchangeAuthcodeArgs(0)
}
}
require.Equal(t, 1, actualCallCountAcrossAllOIDCAndGitHubUpstreams,
"expected exactly one call to (OIDC) ExchangeAuthcodeAndValidateTokensCallCount() or (GitHub) ExchangeAuthcodeCallCount()",
require.Equal(t, 1, actualCallCount,
"expected exactly one call to GitHub ExchangeAuthcode()",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"(OIDC) ExchangeAuthcodeAndValidateTokensCallCount() or (GitHub) ExchangeAuthcodeCallCount() was called on the wrong upstream name",
)
require.Equal(t, expectedPerformedByUpstreamType, actualTypeOfUpstreamWhichMadeCall,
"(OIDC) ExchangeAuthcodeAndValidateTokensCallCount() or (GitHub) ExchangeAuthcodeCallCount() was called on the wrong upstream type",
"GitHub ExchangeAuthcode() was called on the wrong upstream name",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToExchangeAuthcodeAndValidateTokens(t *testing.T) {
func (b *UpstreamIDPListerBuilder) RequireExactlyZeroAuthcodeExchanges(t *testing.T) {
t.Helper()
actualCallCount := 0
for _, upstreamOIDC := range b.upstreamOIDCIdentityProviders {
@@ -316,7 +326,7 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToExchangeAuthcodeAndV
}
require.Equal(t, 0, actualCallCount,
"expected exactly zero calls to (OIDC) ExchangeAuthcodeAndValidateTokensCallCount() or (GitHub) ExchangeAuthcodeCallCount()",
"expected exactly zero calls to OIDC ExchangeAuthcodeAndValidateTokens() or GitHub ExchangeAuthcode()",
)
}