ContainerImage.Pinniped/internal/fositestorage/accesstoken/accesstoken.go
Matt Moyer 551249fb69
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>
2021-06-15 15:31:48 -05:00

117 lines
3.4 KiB
Go

// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package accesstoken
import (
"context"
"fmt"
"time"
"github.com/ory/fosite"
"github.com/ory/fosite/handler/oauth2"
"github.com/ory/fosite/handler/openid"
"k8s.io/apimachinery/pkg/api/errors"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry"
)
const (
TypeLabelValue = "access-token"
ErrInvalidAccessTokenRequestVersion = constable.Error("access token request data has wrong version")
ErrInvalidAccessTokenRequestData = constable.Error("access token request data must be present")
accessTokenStorageVersion = "1"
)
type RevocationStorage interface {
oauth2.AccessTokenStorage
RevokeAccessToken(ctx context.Context, requestID string) error
}
var _ RevocationStorage = &accessTokenStorage{}
type accessTokenStorage struct {
storage crud.Storage
}
type session struct {
Request *fosite.Request `json:"request"`
Version string `json:"version"`
}
func New(secrets corev1client.SecretInterface, clock func() time.Time, sessionStorageLifetime time.Duration) RevocationStorage {
return &accessTokenStorage{storage: crud.New(TypeLabelValue, secrets, clock, sessionStorageLifetime)}
}
func (a *accessTokenStorage) RevokeAccessToken(ctx context.Context, requestID string) error {
return a.storage.DeleteByLabel(ctx, fositestorage.StorageRequestIDLabelName, requestID)
}
func (a *accessTokenStorage) CreateAccessTokenSession(ctx context.Context, signature string, requester fosite.Requester) error {
request, err := fositestorage.ValidateAndExtractAuthorizeRequest(requester)
if err != nil {
return err
}
_, err = a.storage.Create(
ctx,
signature,
&session{Request: request, Version: accessTokenStorageVersion},
map[string]string{fositestorage.StorageRequestIDLabelName: requester.GetID()},
)
return err
}
func (a *accessTokenStorage) GetAccessTokenSession(ctx context.Context, signature string, _ fosite.Session) (fosite.Requester, error) {
session, _, err := a.getSession(ctx, signature)
if err != nil {
return nil, err
}
return session.Request, err
}
func (a *accessTokenStorage) DeleteAccessTokenSession(ctx context.Context, signature string) error {
return a.storage.Delete(ctx, signature)
}
func (a *accessTokenStorage) getSession(ctx context.Context, signature string) (*session, string, error) {
session := newValidEmptyAccessTokenSession()
rv, err := a.storage.Get(ctx, signature, session)
if errors.IsNotFound(err) {
return nil, "", fosite.ErrNotFound.WithWrap(err).WithDebug(err.Error())
}
if err != nil {
return nil, "", fmt.Errorf("failed to get access token session for %s: %w", signature, err)
}
if version := session.Version; version != accessTokenStorageVersion {
return nil, "", fmt.Errorf("%w: access token session for %s has version %s instead of %s",
ErrInvalidAccessTokenRequestVersion, signature, version, accessTokenStorageVersion)
}
if session.Request.ID == "" {
return nil, "", fmt.Errorf("malformed access token session for %s: %w", signature, ErrInvalidAccessTokenRequestData)
}
return session, rv, nil
}
func newValidEmptyAccessTokenSession() *session {
return &session{
Request: &fosite.Request{
Client: &clientregistry.Client{},
Session: &openid.DefaultSession{},
},
}
}