mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-05 23:57:12 +00:00
Add GetUser() interface and implement LoginFromCallback() for GitHub
ALso fixed some of the GitHub test helpers
This commit is contained in:
committed by
Joshua Casey
parent
b7f79f0adc
commit
49c468f00a
+18
-11
@@ -97,22 +97,29 @@ func (p *FederationDomainResolvedGitHubIdentityProvider) LoginFromCallback(
|
||||
_ nonce.Nonce, // GitHub does not support OIDC, therefore there is no ID token that could contain the "nonce".
|
||||
redirectURI string,
|
||||
) (*resolvedprovider.Identity, *resolvedprovider.IdentityLoginExtras, error) {
|
||||
token, _ := p.Provider.ExchangeAuthcode(
|
||||
ctx,
|
||||
authCode,
|
||||
redirectURI,
|
||||
)
|
||||
accessToken, err := p.Provider.ExchangeAuthcode(ctx, authCode, redirectURI)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to exchange auth code using GitHub API: %w", err)
|
||||
}
|
||||
|
||||
user, err := p.Provider.GetUser(ctx, accessToken)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get user info from GitHub API: %w", err)
|
||||
}
|
||||
|
||||
return &resolvedprovider.Identity{
|
||||
UpstreamUsername: "some-github-login",
|
||||
UpstreamGroups: []string{"org1/team1", "org2/team2"},
|
||||
DownstreamSubject: "https://github.com?idpName=upstream-github-idp-name&sub=some-github-login",
|
||||
UpstreamUsername: user.Username,
|
||||
UpstreamGroups: user.Groups,
|
||||
DownstreamSubject: user.DownstreamSubject,
|
||||
IDPSpecificSessionData: &psession.GitHubSessionData{
|
||||
UpstreamAccessToken: token,
|
||||
UpstreamAccessToken: accessToken,
|
||||
},
|
||||
},
|
||||
&resolvedprovider.IdentityLoginExtras{},
|
||||
nil
|
||||
&resolvedprovider.IdentityLoginExtras{
|
||||
DownstreamAdditionalClaims: nil, // not using this for GitHub
|
||||
Warnings: nil, // not using this for GitHub
|
||||
},
|
||||
nil // no error
|
||||
}
|
||||
|
||||
func (p *FederationDomainResolvedGitHubIdentityProvider) UpstreamRefresh(
|
||||
|
||||
+144
-5
@@ -4,6 +4,8 @@
|
||||
package resolvedgithub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -12,10 +14,11 @@ import (
|
||||
idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
||||
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
|
||||
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
|
||||
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil/oidctestutil"
|
||||
"go.pinniped.dev/internal/testutil/transformtestutil"
|
||||
"go.pinniped.dev/internal/upstreamgithub"
|
||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
||||
)
|
||||
|
||||
func TestFederationDomainResolvedGitHubIdentityProvider(t *testing.T) {
|
||||
@@ -59,11 +62,11 @@ func TestFederationDomainResolvedGitHubIdentityProvider(t *testing.T) {
|
||||
originalCustomSession := &psession.CustomSessionData{
|
||||
Username: "fake-username",
|
||||
UpstreamUsername: "fake-upstream-username",
|
||||
GitHub: &psession.GitHubSessionData{UpstreamAccessToken: &oidctypes.Token{AccessToken: &oidctypes.AccessToken{Token: "fake-upstream-access-token"}}},
|
||||
GitHub: &psession.GitHubSessionData{UpstreamAccessToken: "fake-upstream-access-token"},
|
||||
}
|
||||
clonedCustomSession := subject.CloneIDPSpecificSessionDataFromSession(originalCustomSession)
|
||||
require.Equal(t,
|
||||
&psession.GitHubSessionData{UpstreamAccessToken: &oidctypes.Token{AccessToken: &oidctypes.AccessToken{Token: "fake-upstream-access-token"}}},
|
||||
&psession.GitHubSessionData{UpstreamAccessToken: "fake-upstream-access-token"},
|
||||
clonedCustomSession,
|
||||
)
|
||||
require.NotSame(t, originalCustomSession, clonedCustomSession)
|
||||
@@ -72,11 +75,11 @@ func TestFederationDomainResolvedGitHubIdentityProvider(t *testing.T) {
|
||||
Username: "fake-username2",
|
||||
UpstreamUsername: "fake-upstream-username2",
|
||||
}
|
||||
subject.ApplyIDPSpecificSessionDataToSession(customSessionToBeMutated, &psession.GitHubSessionData{UpstreamAccessToken: &oidctypes.Token{AccessToken: &oidctypes.AccessToken{Token: "OTHER-upstream-access-token"}}})
|
||||
subject.ApplyIDPSpecificSessionDataToSession(customSessionToBeMutated, &psession.GitHubSessionData{UpstreamAccessToken: "OTHER-upstream-access-token"})
|
||||
require.Equal(t, &psession.CustomSessionData{
|
||||
Username: "fake-username2",
|
||||
UpstreamUsername: "fake-upstream-username2",
|
||||
GitHub: &psession.GitHubSessionData{UpstreamAccessToken: &oidctypes.Token{AccessToken: &oidctypes.AccessToken{Token: "OTHER-upstream-access-token"}}},
|
||||
GitHub: &psession.GitHubSessionData{UpstreamAccessToken: "OTHER-upstream-access-token"},
|
||||
}, customSessionToBeMutated)
|
||||
|
||||
redirectURL, err := subject.UpstreamAuthorizeRedirectURL(
|
||||
@@ -101,3 +104,139 @@ func TestFederationDomainResolvedGitHubIdentityProvider(t *testing.T) {
|
||||
redirectURL,
|
||||
)
|
||||
}
|
||||
|
||||
func TestLoginFromCallback(t *testing.T) {
|
||||
uniqueCtx := context.WithValue(context.Background(), "some-unique-key", "some-value") //nolint:staticcheck // okay to use string key for test
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
provider *oidctestutil.TestUpstreamGitHubIdentityProvider
|
||||
authcode string
|
||||
redirectURI string
|
||||
|
||||
wantExchangeAuthcodeCall bool
|
||||
wantExchangeAuthcodeArgs *oidctestutil.ExchangeAuthcodeArgs
|
||||
wantGetUserCall bool
|
||||
wantGetUserArgs *oidctestutil.GetUserArgs
|
||||
wantIdentity *resolvedprovider.Identity
|
||||
wantExtras *resolvedprovider.IdentityLoginExtras
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
provider: oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
|
||||
WithAccessToken("fake-access-token").
|
||||
WithUser(&upstreamprovider.GitHubUser{
|
||||
Username: "fake-username",
|
||||
Groups: []string{"fake-group1", "fake-group2"},
|
||||
DownstreamSubject: "https://fake-downstream-subject",
|
||||
}).
|
||||
Build(),
|
||||
authcode: "fake-authcode",
|
||||
redirectURI: "https://fake-redirect-uri",
|
||||
wantExchangeAuthcodeCall: true,
|
||||
wantExchangeAuthcodeArgs: &oidctestutil.ExchangeAuthcodeArgs{
|
||||
Ctx: uniqueCtx,
|
||||
Authcode: "fake-authcode",
|
||||
RedirectURI: "https://fake-redirect-uri",
|
||||
},
|
||||
wantGetUserCall: true,
|
||||
wantGetUserArgs: &oidctestutil.GetUserArgs{
|
||||
Ctx: uniqueCtx,
|
||||
AccessToken: "fake-access-token",
|
||||
},
|
||||
wantIdentity: &resolvedprovider.Identity{
|
||||
UpstreamUsername: "fake-username",
|
||||
UpstreamGroups: []string{"fake-group1", "fake-group2"},
|
||||
DownstreamSubject: "https://fake-downstream-subject",
|
||||
IDPSpecificSessionData: &psession.GitHubSessionData{
|
||||
UpstreamAccessToken: "fake-access-token",
|
||||
},
|
||||
},
|
||||
wantExtras: &resolvedprovider.IdentityLoginExtras{},
|
||||
},
|
||||
{
|
||||
name: "error while exchanging authcode",
|
||||
provider: oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
|
||||
WithAuthcodeExchangeError(errors.New("fake authcode exchange error")).
|
||||
Build(),
|
||||
authcode: "fake-authcode",
|
||||
redirectURI: "https://fake-redirect-uri",
|
||||
wantExchangeAuthcodeCall: true,
|
||||
wantExchangeAuthcodeArgs: &oidctestutil.ExchangeAuthcodeArgs{
|
||||
Ctx: uniqueCtx,
|
||||
Authcode: "fake-authcode",
|
||||
RedirectURI: "https://fake-redirect-uri",
|
||||
},
|
||||
wantGetUserCall: false,
|
||||
wantIdentity: nil,
|
||||
wantExtras: nil,
|
||||
wantErr: "failed to exchange auth code using GitHub API: fake authcode exchange error",
|
||||
},
|
||||
{
|
||||
name: "error while getting user info",
|
||||
provider: oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
|
||||
WithAccessToken("fake-access-token").
|
||||
WithGetUserError(errors.New("fake user info error")).
|
||||
Build(),
|
||||
authcode: "fake-authcode",
|
||||
redirectURI: "https://fake-redirect-uri",
|
||||
wantExchangeAuthcodeCall: true,
|
||||
wantExchangeAuthcodeArgs: &oidctestutil.ExchangeAuthcodeArgs{
|
||||
Ctx: uniqueCtx,
|
||||
Authcode: "fake-authcode",
|
||||
RedirectURI: "https://fake-redirect-uri",
|
||||
},
|
||||
wantGetUserCall: true,
|
||||
wantGetUserArgs: &oidctestutil.GetUserArgs{
|
||||
Ctx: uniqueCtx,
|
||||
AccessToken: "fake-access-token",
|
||||
},
|
||||
wantIdentity: nil,
|
||||
wantExtras: nil,
|
||||
wantErr: "failed to get user info from GitHub API: fake user info error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
transforms := transformtestutil.NewRejectAllAuthPipeline(t)
|
||||
|
||||
subject := FederationDomainResolvedGitHubIdentityProvider{
|
||||
DisplayName: "fake-display-name",
|
||||
Provider: test.provider,
|
||||
SessionProviderType: psession.ProviderTypeGitHub,
|
||||
Transforms: transforms,
|
||||
}
|
||||
|
||||
identity, loginExtras, err := subject.LoginFromCallback(uniqueCtx,
|
||||
test.authcode,
|
||||
"pkce-will-be-ignored",
|
||||
"nonce-will-be-ignored",
|
||||
test.redirectURI,
|
||||
)
|
||||
|
||||
if test.wantExchangeAuthcodeCall {
|
||||
require.Equal(t, 1, test.provider.ExchangeAuthcodeCallCount())
|
||||
require.Equal(t, test.wantExchangeAuthcodeArgs, test.provider.ExchangeAuthcodeArgs(0))
|
||||
} else {
|
||||
require.Zero(t, test.provider.ExchangeAuthcodeCallCount())
|
||||
}
|
||||
|
||||
if test.wantGetUserCall {
|
||||
require.Equal(t, 1, test.provider.GetUserCallCount())
|
||||
require.Equal(t, test.wantGetUserArgs, test.provider.GetUserArgs(0))
|
||||
} else {
|
||||
require.Zero(t, test.provider.GetUserCallCount())
|
||||
}
|
||||
|
||||
if test.wantErr == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, test.wantErr)
|
||||
}
|
||||
require.Equal(t, test.wantExtras, loginExtras)
|
||||
require.Equal(t, test.wantIdentity, identity)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user