Use a custom type for our static CLI client (smaller change).
Before this change, we used the `fosite.DefaultOpenIDConnectClient{}` struct, which implements the `fosite.Client` and `fosite.OpenIDConnectClient` interfaces. For a future change, we also need to implement some additional optional interfaces, so we can no longer use the provided default types. Instead, we now use a custom `clientregistry.Client{}` struct, which implements all the requisite interfaces and can be extended to handle the new functionality (in a future change). There is also a new `clientregistry.StaticRegistry{}` struct, which implements the `fosite.ClientManager` and looks up our single static client. We could potentially extend this in the future with a registry backed by Kubernetes API, for example. This should be 100% refactor, with no user-observable change. Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
parent
1a610022cf
commit
551249fb69
@ -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
|
||||
|
||||
package accesstoken
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/crud"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -108,7 +109,7 @@ func (a *accessTokenStorage) getSession(ctx context.Context, signature string) (
|
||||
func newValidEmptyAccessTokenSession() *session {
|
||||
return &session{
|
||||
Request: &fosite.Request{
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package accesstoken
|
||||
@ -20,6 +20,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const namespace = "test-ns"
|
||||
@ -63,24 +65,25 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
}},
|
||||
RequestedScope: nil,
|
||||
GrantedScope: nil,
|
||||
Form: url.Values{"key": []string{"val"}},
|
||||
@ -138,13 +141,15 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
TokenEndpointAuthMethod: "something",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
TokenEndpointAuthMethod: "something",
|
||||
},
|
||||
Form: url.Values{"key": []string{"val"}},
|
||||
Session: &openid.DefaultSession{
|
||||
@ -238,7 +243,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's session must be of type openid.DefaultSession")
|
||||
@ -248,7 +253,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
Client: nil,
|
||||
}
|
||||
err = storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
@ -257,7 +262,7 @@ func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "", // empty ID
|
||||
Session: &openid.DefaultSession{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.NoError(t, err)
|
||||
|
@ -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
|
||||
|
||||
package authorizationcode
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/crud"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -137,7 +138,7 @@ func (a *authorizeCodeStorage) getSession(ctx context.Context, signature string)
|
||||
func NewValidEmptyAuthorizeCodeSession() *AuthorizeCodeSession {
|
||||
return &AuthorizeCodeSession{
|
||||
Request: &fosite.Request{
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package authorizationcode
|
||||
@ -33,6 +33,7 @@ import (
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const namespace = "test-ns"
|
||||
@ -92,23 +93,25 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
RequestedScope: nil,
|
||||
GrantedScope: nil,
|
||||
@ -169,7 +172,7 @@ func TestInvalidateWhenConflictOnUpdateHappens(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "some-request-id",
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
}
|
||||
err := storage.CreateAuthorizeCodeSession(ctx, "fancy-signature", request)
|
||||
@ -240,7 +243,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateAuthorizeCodeSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's session must be of type openid.DefaultSession")
|
||||
@ -250,7 +253,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
Client: nil,
|
||||
}
|
||||
err = storage.CreateAuthorizeCodeSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, oauth2.AuthorizeCodeStorage) {
|
||||
@ -270,7 +273,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) {
|
||||
require.Equal(t, validSession.Request, extractedRequest)
|
||||
|
||||
// checked above
|
||||
defaultClient := validSession.Request.Client.(*fosite.DefaultOpenIDConnectClient)
|
||||
defaultClient := validSession.Request.Client.(*clientregistry.Client)
|
||||
defaultSession := validSession.Request.Session.(*openid.DefaultSession)
|
||||
|
||||
// makes it easier to use a raw string
|
||||
|
@ -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
|
||||
|
||||
package fositestorage
|
||||
@ -8,11 +8,12 @@ import (
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrInvalidRequestType = constable.Error("requester must be of type fosite.Request")
|
||||
ErrInvalidClientType = constable.Error("requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
ErrInvalidClientType = constable.Error("requester's client must be of type clientregistry.Client")
|
||||
ErrInvalidSessionType = constable.Error("requester's session must be of type openid.DefaultSession")
|
||||
StorageRequestIDLabelName = "storage.pinniped.dev/request-id" //nolint:gosec // this is not a credential
|
||||
)
|
||||
@ -22,7 +23,7 @@ func ValidateAndExtractAuthorizeRequest(requester fosite.Requester) (*fosite.Req
|
||||
if !ok1 {
|
||||
return nil, ErrInvalidRequestType
|
||||
}
|
||||
_, ok2 := request.Client.(*fosite.DefaultOpenIDConnectClient)
|
||||
_, ok2 := request.Client.(*clientregistry.Client)
|
||||
if !ok2 {
|
||||
return nil, ErrInvalidClientType
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package openidconnect
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/crud"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -110,7 +111,7 @@ func (a *openIDConnectRequestStorage) getSession(ctx context.Context, signature
|
||||
func newValidEmptyOIDCSession() *session {
|
||||
return &session{
|
||||
Request: &fosite.Request{
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package openidconnect
|
||||
@ -20,6 +20,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const namespace = "test-ns"
|
||||
@ -62,23 +64,25 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
RequestedScope: nil,
|
||||
GrantedScope: nil,
|
||||
@ -176,7 +180,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateOpenIDConnectSession(ctx, "authcode.signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's session must be of type openid.DefaultSession")
|
||||
@ -186,7 +190,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
Client: nil,
|
||||
}
|
||||
err = storage.CreateOpenIDConnectSession(ctx, "authcode.signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func TestAuthcodeHasNoDot(t *testing.T) {
|
||||
|
@ -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
|
||||
|
||||
package pkce
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/crud"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -94,7 +95,7 @@ func (a *pkceStorage) getSession(ctx context.Context, signature string) (*sessio
|
||||
func newValidEmptyPKCESession() *session {
|
||||
return &session{
|
||||
Request: &fosite.Request{
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package pkce
|
||||
@ -21,6 +21,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const namespace = "test-ns"
|
||||
@ -63,23 +65,25 @@ func TestPKCEStorage(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
RequestedScope: nil,
|
||||
GrantedScope: nil,
|
||||
@ -183,7 +187,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreatePKCERequestSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's session must be of type openid.DefaultSession")
|
||||
@ -193,7 +197,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
Client: nil,
|
||||
}
|
||||
err = storage.CreatePKCERequestSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, pkce.PKCERequestStorage) {
|
||||
|
@ -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
|
||||
|
||||
package refreshtoken
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/crud"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -108,7 +109,7 @@ func (a *refreshTokenStorage) getSession(ctx context.Context, signature string)
|
||||
func newValidEmptyRefreshTokenSession() *session {
|
||||
return &session{
|
||||
Request: &fosite.Request{
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &openid.DefaultSession{},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
package refreshtoken
|
||||
@ -20,6 +20,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const namespace = "test-ns"
|
||||
@ -62,23 +64,25 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Secret: nil,
|
||||
RedirectURIs: nil,
|
||||
GrantTypes: nil,
|
||||
ResponseTypes: nil,
|
||||
Scopes: nil,
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
JSONWebKeys: nil,
|
||||
TokenEndpointAuthMethod: "something",
|
||||
RequestURIs: nil,
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: "",
|
||||
},
|
||||
RequestedScope: nil,
|
||||
GrantedScope: nil,
|
||||
@ -137,13 +141,15 @@ func TestRefreshTokenStorageRevocation(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Public: true,
|
||||
Client: &clientregistry.Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
Public: true,
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
TokenEndpointAuthMethod: "something",
|
||||
},
|
||||
JSONWebKeysURI: "where",
|
||||
TokenEndpointAuthMethod: "something",
|
||||
},
|
||||
Form: url.Values{"key": []string{"val"}},
|
||||
Session: &openid.DefaultSession{
|
||||
@ -237,7 +243,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's session must be of type openid.DefaultSession")
|
||||
@ -247,7 +253,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
Client: nil,
|
||||
}
|
||||
err = storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.EqualError(t, err, "requester's client must be of type fosite.DefaultOpenIDConnectClient")
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
@ -256,7 +262,7 @@ func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
request := &fosite.Request{
|
||||
ID: "", // empty ID
|
||||
Session: &openid.DefaultSession{},
|
||||
Client: &fosite.DefaultOpenIDConnectClient{},
|
||||
Client: &clientregistry.Client{},
|
||||
}
|
||||
err := storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", request)
|
||||
require.NoError(t, err)
|
||||
|
94
internal/oidc/clientregistry/clientregistry.go
Normal file
94
internal/oidc/clientregistry/clientregistry.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package clientregistry defines Pinniped's OAuth2/OIDC clients.
|
||||
package clientregistry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/ory/fosite"
|
||||
)
|
||||
|
||||
// Client represents a Pinniped OAuth/OIDC client.
|
||||
type Client struct {
|
||||
fosite.DefaultOpenIDConnectClient
|
||||
}
|
||||
|
||||
// It implements both the base and OIDC client interfaces of Fosite.
|
||||
var (
|
||||
_ fosite.Client = (*Client)(nil)
|
||||
_ fosite.OpenIDConnectClient = (*Client)(nil)
|
||||
)
|
||||
|
||||
// StaticClientManager is a fosite.ClientManager with statically-defined clients.
|
||||
type StaticClientManager struct{}
|
||||
|
||||
var _ fosite.ClientManager = (*StaticClientManager)(nil)
|
||||
|
||||
// GetClient returns a static client specified by the given ID.
|
||||
//
|
||||
// It returns a fosite.ErrNotFound if an unknown client is specified.
|
||||
func (StaticClientManager) GetClient(_ context.Context, id string) (fosite.Client, error) {
|
||||
switch id {
|
||||
case "pinniped-cli":
|
||||
return PinnipedCLI(), nil
|
||||
default:
|
||||
return nil, fosite.ErrNotFound.WithDescription("no such client")
|
||||
}
|
||||
}
|
||||
|
||||
// ClientAssertionJWTValid returns an error if the JTI is
|
||||
// known or the DB check failed and nil if the JTI is not known.
|
||||
//
|
||||
// This functionality is not supported by the StaticClientManager.
|
||||
func (StaticClientManager) ClientAssertionJWTValid(ctx context.Context, jti string) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// SetClientAssertionJWT marks a JTI as known for the given
|
||||
// expiry time. Before inserting the new JTI, it will clean
|
||||
// up any existing JTIs that have expired as those tokens can
|
||||
// not be replayed due to the expiry.
|
||||
//
|
||||
// This functionality is not supported by the StaticClientManager.
|
||||
func (StaticClientManager) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// PinnipedCLI returns the static Client corresponding to the Pinniped CLI.
|
||||
func PinnipedCLI() *Client {
|
||||
return &Client{
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinniped-cli",
|
||||
Secret: nil,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
GrantTypes: fosite.Arguments{
|
||||
"authorization_code",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:token-exchange",
|
||||
},
|
||||
ResponseTypes: []string{"code"},
|
||||
Scopes: fosite.Arguments{
|
||||
oidc.ScopeOpenID,
|
||||
oidc.ScopeOfflineAccess,
|
||||
"profile",
|
||||
"email",
|
||||
"pinniped:request-audience",
|
||||
},
|
||||
Audience: nil,
|
||||
Public: true,
|
||||
},
|
||||
RequestURIs: nil,
|
||||
JSONWebKeys: nil,
|
||||
JSONWebKeysURI: "",
|
||||
RequestObjectSigningAlgorithm: "",
|
||||
TokenEndpointAuthSigningAlgorithm: oidc.RS256,
|
||||
TokenEndpointAuthMethod: "none",
|
||||
},
|
||||
}
|
||||
}
|
95
internal/oidc/clientregistry/clientregistry_test.go
Normal file
95
internal/oidc/clientregistry/clientregistry_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package clientregistry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStaticRegistry(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("unimplemented methods", func(t *testing.T) {
|
||||
registry := StaticClientManager{}
|
||||
require.EqualError(t, registry.ClientAssertionJWTValid(ctx, "some-token-id"), "not implemented")
|
||||
require.EqualError(t, registry.SetClientAssertionJWT(ctx, "some-token-id", time.Now()), "not implemented")
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
registry := StaticClientManager{}
|
||||
got, err := registry.GetClient(ctx, "does-not-exist")
|
||||
require.Error(t, err)
|
||||
require.Nil(t, got)
|
||||
rfcErr := fosite.ErrorToRFC6749Error(err)
|
||||
require.NotNil(t, rfcErr)
|
||||
require.Equal(t, rfcErr.CodeField, 404)
|
||||
require.Equal(t, rfcErr.GetDescription(), "no such client")
|
||||
})
|
||||
|
||||
t.Run("pinniped CLI", func(t *testing.T) {
|
||||
registry := StaticClientManager{}
|
||||
got, err := registry.GetClient(ctx, "pinniped-cli")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
require.IsType(t, &Client{}, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPinnipedCLI(t *testing.T) {
|
||||
c := PinnipedCLI()
|
||||
require.Equal(t, "pinniped-cli", c.GetID())
|
||||
require.Nil(t, c.GetHashedSecret())
|
||||
require.Equal(t, []string{"http://127.0.0.1/callback"}, c.GetRedirectURIs())
|
||||
require.Equal(t, fosite.Arguments{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"}, c.GetGrantTypes())
|
||||
require.Equal(t, fosite.Arguments{"code"}, c.GetResponseTypes())
|
||||
require.Equal(t, fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile", "email", "pinniped:request-audience"}, c.GetScopes())
|
||||
require.True(t, c.IsPublic())
|
||||
require.Nil(t, c.GetAudience())
|
||||
require.Nil(t, c.GetRequestURIs())
|
||||
require.Nil(t, c.GetJSONWebKeys())
|
||||
require.Equal(t, "", c.GetJSONWebKeysURI())
|
||||
require.Equal(t, "", c.GetRequestObjectSigningAlgorithm())
|
||||
require.Equal(t, "none", c.GetTokenEndpointAuthMethod())
|
||||
require.Equal(t, "RS256", c.GetTokenEndpointAuthSigningAlgorithm())
|
||||
|
||||
marshaled, err := json.Marshal(c)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `
|
||||
{
|
||||
"id": "pinniped-cli",
|
||||
"redirect_uris": [
|
||||
"http://127.0.0.1/callback"
|
||||
],
|
||||
"grant_types": [
|
||||
"authorization_code",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:token-exchange"
|
||||
],
|
||||
"response_types": [
|
||||
"code"
|
||||
],
|
||||
"scopes": [
|
||||
"openid",
|
||||
"offline_access",
|
||||
"profile",
|
||||
"email",
|
||||
"pinniped:request-audience"
|
||||
],
|
||||
"audience": null,
|
||||
"public": true,
|
||||
"jwks_uri": "",
|
||||
"jwks": null,
|
||||
"token_endpoint_auth_method": "none",
|
||||
"request_uris": null,
|
||||
"request_object_signing_alg": "",
|
||||
"token_endpoint_auth_signing_alg": "RS256"
|
||||
}`, string(marshaled))
|
||||
}
|
@ -13,18 +13,17 @@ import (
|
||||
fositepkce "github.com/ory/fosite/handler/pkce"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/fositestorage/accesstoken"
|
||||
"go.pinniped.dev/internal/fositestorage/authorizationcode"
|
||||
"go.pinniped.dev/internal/fositestorage/openidconnect"
|
||||
"go.pinniped.dev/internal/fositestorage/pkce"
|
||||
"go.pinniped.dev/internal/fositestorage/refreshtoken"
|
||||
"go.pinniped.dev/internal/fositestoragei"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const errKubeStorageNotImplemented = constable.Error("KubeStorage does not implement this method. It should not have been called.")
|
||||
|
||||
type KubeStorage struct {
|
||||
clientManager fosite.ClientManager
|
||||
authorizationCodeStorage oauth2.AuthorizeCodeStorage
|
||||
pkceStorage fositepkce.PKCERequestStorage
|
||||
oidcStorage openid.OpenIDConnectRequestStorage
|
||||
@ -37,6 +36,7 @@ var _ fositestoragei.AllFositeStorage = &KubeStorage{}
|
||||
func NewKubeStorage(secrets corev1client.SecretInterface, timeoutsConfiguration TimeoutsConfiguration) *KubeStorage {
|
||||
nowFunc := time.Now
|
||||
return &KubeStorage{
|
||||
clientManager: &clientregistry.StaticClientManager{},
|
||||
authorizationCodeStorage: authorizationcode.New(secrets, nowFunc, timeoutsConfiguration.AuthorizationCodeSessionStorageLifetime),
|
||||
pkceStorage: pkce.New(secrets, nowFunc, timeoutsConfiguration.PKCESessionStorageLifetime),
|
||||
oidcStorage: openidconnect.New(secrets, nowFunc, timeoutsConfiguration.OIDCSessionStorageLifetime),
|
||||
@ -183,26 +183,15 @@ func (k KubeStorage) RevokeRefreshToken(ctx context.Context, requestID string) e
|
||||
//
|
||||
// OAuth client definitions:
|
||||
//
|
||||
// For the time being, we only allow a single pre-defined client, so we do not need to interact with any underlying
|
||||
// storage mechanism to fetch them.
|
||||
//
|
||||
|
||||
func (KubeStorage) GetClient(_ context.Context, id string) (fosite.Client, error) {
|
||||
client := PinnipedCLIOIDCClient()
|
||||
if client.ID == id {
|
||||
return client, nil
|
||||
}
|
||||
return nil, fosite.ErrNotFound
|
||||
func (k KubeStorage) GetClient(ctx context.Context, id string) (fosite.Client, error) {
|
||||
return k.clientManager.GetClient(ctx, id)
|
||||
}
|
||||
|
||||
//
|
||||
// Unused interface methods.
|
||||
//
|
||||
|
||||
func (KubeStorage) ClientAssertionJWTValid(_ context.Context, _ string) error {
|
||||
return errKubeStorageNotImplemented
|
||||
func (k KubeStorage) ClientAssertionJWTValid(ctx context.Context, jti string) error {
|
||||
return k.clientManager.ClientAssertionJWTValid(ctx, jti)
|
||||
}
|
||||
|
||||
func (KubeStorage) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error {
|
||||
return errKubeStorageNotImplemented
|
||||
func (k KubeStorage) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error {
|
||||
return k.clientManager.SetClientAssertionJWT(ctx, jti, exp)
|
||||
}
|
||||
|
@ -5,17 +5,19 @@ package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/fositestoragei"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
)
|
||||
|
||||
const errNullStorageNotImplemented = constable.Error("NullStorage does not implement this method. It should not have been called.")
|
||||
|
||||
type NullStorage struct{}
|
||||
type NullStorage struct {
|
||||
clientregistry.StaticClientManager
|
||||
}
|
||||
|
||||
var _ fositestoragei.AllFositeStorage = &NullStorage{}
|
||||
|
||||
@ -86,19 +88,3 @@ func (NullStorage) GetAuthorizeCodeSession(_ context.Context, _ string, _ fosite
|
||||
func (NullStorage) InvalidateAuthorizeCodeSession(_ context.Context, _ string) (err error) {
|
||||
return errNullStorageNotImplemented
|
||||
}
|
||||
|
||||
func (NullStorage) GetClient(_ context.Context, id string) (fosite.Client, error) {
|
||||
client := PinnipedCLIOIDCClient()
|
||||
if client.ID == id {
|
||||
return client, nil
|
||||
}
|
||||
return nil, fosite.ErrNotFound
|
||||
}
|
||||
|
||||
func (NullStorage) ClientAssertionJWTValid(_ context.Context, _ string) error {
|
||||
return errNullStorageNotImplemented
|
||||
}
|
||||
|
||||
func (NullStorage) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error {
|
||||
return errNullStorageNotImplemented
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNullStorage_GetClient(t *testing.T) {
|
||||
storage := NullStorage{}
|
||||
|
||||
client, err := storage.GetClient(context.Background(), "some-other-client")
|
||||
require.Equal(t, fosite.ErrNotFound, err)
|
||||
require.Zero(t, client)
|
||||
|
||||
client, err = storage.GetClient(context.Background(), "pinniped-cli")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
&fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinniped-cli",
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{"openid", "offline_access", "profile", "email", "pinniped:request-audience"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
},
|
||||
client,
|
||||
)
|
||||
}
|
@ -98,20 +98,6 @@ type UpstreamStateParamData struct {
|
||||
FormatVersion string `json:"v"`
|
||||
}
|
||||
|
||||
func PinnipedCLIOIDCClient() *fosite.DefaultOpenIDConnectClient {
|
||||
return &fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinniped-cli",
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{coreosoidc.ScopeOpenID, coreosoidc.ScopeOfflineAccess, "profile", "email", "pinniped:request-audience"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
}
|
||||
}
|
||||
|
||||
type TimeoutsConfiguration struct {
|
||||
// The length of time that our state param that we encrypt and pass to the upstream OIDC IDP should be considered
|
||||
// valid. If a state param generated by the authorize endpoint is sent to the callback endpoint after this much
|
||||
|
Loading…
Reference in New Issue
Block a user