2021-04-09 00:28:01 +00:00
|
|
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
2020-12-01 19:01:23 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package oidc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ory/fosite"
|
|
|
|
"github.com/ory/fosite/handler/oauth2"
|
2020-12-02 01:18:32 +00:00
|
|
|
"github.com/ory/fosite/handler/openid"
|
|
|
|
fositepkce "github.com/ory/fosite/handler/pkce"
|
2020-12-01 19:01:23 +00:00
|
|
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
|
|
|
|
"go.pinniped.dev/internal/constable"
|
2020-12-04 22:31:06 +00:00
|
|
|
"go.pinniped.dev/internal/fositestorage/accesstoken"
|
2020-12-01 19:01:23 +00:00
|
|
|
"go.pinniped.dev/internal/fositestorage/authorizationcode"
|
2020-12-02 01:18:32 +00:00
|
|
|
"go.pinniped.dev/internal/fositestorage/openidconnect"
|
|
|
|
"go.pinniped.dev/internal/fositestorage/pkce"
|
2020-12-04 22:31:06 +00:00
|
|
|
"go.pinniped.dev/internal/fositestorage/refreshtoken"
|
2021-04-09 00:28:01 +00:00
|
|
|
"go.pinniped.dev/internal/fositestoragei"
|
2020-12-01 19:01:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const errKubeStorageNotImplemented = constable.Error("KubeStorage does not implement this method. It should not have been called.")
|
|
|
|
|
|
|
|
type KubeStorage struct {
|
|
|
|
authorizationCodeStorage oauth2.AuthorizeCodeStorage
|
2020-12-01 22:53:22 +00:00
|
|
|
pkceStorage fositepkce.PKCERequestStorage
|
2020-12-02 01:18:32 +00:00
|
|
|
oidcStorage openid.OpenIDConnectRequestStorage
|
2020-12-04 22:31:06 +00:00
|
|
|
accessTokenStorage accesstoken.RevocationStorage
|
|
|
|
refreshTokenStorage refreshtoken.RevocationStorage
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 00:28:01 +00:00
|
|
|
var _ fositestoragei.AllFositeStorage = &KubeStorage{}
|
|
|
|
|
2020-12-10 20:15:40 +00:00
|
|
|
func NewKubeStorage(secrets corev1client.SecretInterface, timeoutsConfiguration TimeoutsConfiguration) *KubeStorage {
|
2020-12-10 22:47:58 +00:00
|
|
|
nowFunc := time.Now
|
2020-12-01 22:53:22 +00:00
|
|
|
return &KubeStorage{
|
2020-12-10 22:47:58 +00:00
|
|
|
authorizationCodeStorage: authorizationcode.New(secrets, nowFunc, timeoutsConfiguration.AuthorizationCodeSessionStorageLifetime),
|
|
|
|
pkceStorage: pkce.New(secrets, nowFunc, timeoutsConfiguration.PKCESessionStorageLifetime),
|
|
|
|
oidcStorage: openidconnect.New(secrets, nowFunc, timeoutsConfiguration.OIDCSessionStorageLifetime),
|
|
|
|
accessTokenStorage: accesstoken.New(secrets, nowFunc, timeoutsConfiguration.AccessTokenSessionStorageLifetime),
|
|
|
|
refreshTokenStorage: refreshtoken.New(secrets, nowFunc, timeoutsConfiguration.RefreshTokenSessionStorageLifetime),
|
2020-12-01 22:53:22 +00:00
|
|
|
}
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// Authorization Code sessions:
|
|
|
|
//
|
|
|
|
// These are keyed by the signature of the authcode.
|
|
|
|
//
|
|
|
|
// Fosite will create these in the authorize endpoint.
|
|
|
|
//
|
|
|
|
// Fosite will never delete them. Instead, it wants to mark them as invalidated once the authcode is used to redeem tokens.
|
|
|
|
// That way, it can later detect the case where an authcode that was already redeemed gets used again.
|
|
|
|
//
|
|
|
|
|
|
|
|
func (k KubeStorage) CreateAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string, r fosite.Requester) (err error) {
|
|
|
|
return k.authorizationCodeStorage.CreateAuthorizeCodeSession(ctx, signatureOfAuthcode, r)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) GetAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string, s fosite.Session) (request fosite.Requester, err error) {
|
|
|
|
return k.authorizationCodeStorage.GetAuthorizeCodeSession(ctx, signatureOfAuthcode, s)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signatureOfAuthcode string) (err error) {
|
|
|
|
return k.authorizationCodeStorage.InvalidateAuthorizeCodeSession(ctx, signatureOfAuthcode)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// PKCE sessions:
|
|
|
|
//
|
|
|
|
// These are keyed by the signature of the authcode.
|
|
|
|
//
|
|
|
|
// Fosite will create these in the authorize endpoint at the same time that it is creating an authcode.
|
|
|
|
//
|
|
|
|
// Fosite will delete these in the token endpoint during authcode redemption since they are no longer needed after that.
|
|
|
|
// If the user chooses to never redeem their authcode, then fosite will never delete these.
|
|
|
|
//
|
|
|
|
|
|
|
|
func (k KubeStorage) CreatePKCERequestSession(ctx context.Context, signatureOfAuthcode string, requester fosite.Requester) error {
|
|
|
|
return k.pkceStorage.CreatePKCERequestSession(ctx, signatureOfAuthcode, requester)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) GetPKCERequestSession(ctx context.Context, signatureOfAuthcode string, session fosite.Session) (fosite.Requester, error) {
|
|
|
|
return k.pkceStorage.GetPKCERequestSession(ctx, signatureOfAuthcode, session)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) DeletePKCERequestSession(ctx context.Context, signatureOfAuthcode string) error {
|
|
|
|
return k.pkceStorage.DeletePKCERequestSession(ctx, signatureOfAuthcode)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// OpenID Connect sessions:
|
|
|
|
//
|
|
|
|
// These are keyed by the full value of the authcode (not just the signature).
|
|
|
|
//
|
|
|
|
// Fosite will create these in the authorize endpoint when it creates an authcode, but only if the user
|
|
|
|
// requested the openid scope.
|
|
|
|
//
|
|
|
|
// Fosite will never delete these, which is likely a bug in fosite. Although there is a delete method below, fosite
|
|
|
|
// never calls it. Used during authcode redemption, they will never be accessed again after a successful authcode
|
|
|
|
// redemption. Although that implies that they should probably follow a lifecycle similar the the PKCE storage, they
|
|
|
|
// are, in fact, not deleted.
|
|
|
|
//
|
|
|
|
|
|
|
|
func (k KubeStorage) CreateOpenIDConnectSession(ctx context.Context, fullAuthcode string, requester fosite.Requester) error {
|
|
|
|
return k.oidcStorage.CreateOpenIDConnectSession(ctx, fullAuthcode, requester)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) GetOpenIDConnectSession(ctx context.Context, fullAuthcode string, requester fosite.Requester) (fosite.Requester, error) {
|
|
|
|
return k.oidcStorage.GetOpenIDConnectSession(ctx, fullAuthcode, requester)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, fullAuthcode string) error {
|
|
|
|
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// Access token sessions:
|
|
|
|
//
|
|
|
|
// These are keyed by the signature of the access token.
|
|
|
|
//
|
|
|
|
// Fosite will create these in the token endpoint whenever it wants to hand out an access token, including the original
|
|
|
|
// authcode redemption and also during refresh.
|
|
|
|
//
|
|
|
|
// Fosite will not use the delete method. Instead, it will use the revoke method to delete them.
|
|
|
|
// During a refresh in the token endpoint, the old access token is revoked just before the new access token is created.
|
|
|
|
// Also, if the token endpoint receives an authcode that was already used successfully, then it revokes the access token
|
|
|
|
// that was previously handed out for that authcode. If a user stops coming back to refresh their tokens, then that
|
|
|
|
// access token will never be deleted.
|
|
|
|
//
|
|
|
|
|
|
|
|
func (k KubeStorage) CreateAccessTokenSession(ctx context.Context, signatureOfAccessToken string, requester fosite.Requester) (err error) {
|
|
|
|
return k.accessTokenStorage.CreateAccessTokenSession(ctx, signatureOfAccessToken, requester)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) GetAccessTokenSession(ctx context.Context, signatureOfAccessToken string, session fosite.Session) (request fosite.Requester, err error) {
|
|
|
|
return k.accessTokenStorage.GetAccessTokenSession(ctx, signatureOfAccessToken, session)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) DeleteAccessTokenSession(ctx context.Context, signatureOfAccessToken string) (err error) {
|
|
|
|
return k.accessTokenStorage.DeleteAccessTokenSession(ctx, signatureOfAccessToken)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) RevokeAccessToken(ctx context.Context, requestID string) error {
|
|
|
|
return k.accessTokenStorage.RevokeAccessToken(ctx, requestID)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// Refresh token sessions:
|
|
|
|
//
|
|
|
|
// These are keyed by the signature of the refresh token.
|
|
|
|
//
|
|
|
|
// Fosite will create these in the token endpoint whenever it wants to hand out an refresh token, including the original
|
|
|
|
// authcode redemption and also during refresh. Refresh tokens are only handed out when the user requested the
|
|
|
|
// offline_access scope on the original authorization request.
|
|
|
|
//
|
|
|
|
// Fosite will not use the delete method. Instead, it will use the revoke method to delete them.
|
|
|
|
// During a refresh in the token endpoint, the old refresh token is revoked just before the new refresh token is created.
|
|
|
|
// Also, if the token endpoint receives an authcode that was already used successfully, then it revokes the refresh token
|
|
|
|
// that was previously handed out for that authcode. If a user stops coming back to refresh their tokens, then that
|
|
|
|
// refresh token will never be deleted.
|
|
|
|
//
|
|
|
|
|
|
|
|
func (k KubeStorage) CreateRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string, request fosite.Requester) (err error) {
|
|
|
|
return k.refreshTokenStorage.CreateRefreshTokenSession(ctx, signatureOfRefreshToken, request)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) GetRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string, session fosite.Session) (request fosite.Requester, err error) {
|
|
|
|
return k.refreshTokenStorage.GetRefreshTokenSession(ctx, signatureOfRefreshToken, session)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) DeleteRefreshTokenSession(ctx context.Context, signatureOfRefreshToken string) (err error) {
|
|
|
|
return k.refreshTokenStorage.DeleteRefreshTokenSession(ctx, signatureOfRefreshToken)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
func (k KubeStorage) RevokeRefreshToken(ctx context.Context, requestID string) error {
|
|
|
|
return k.refreshTokenStorage.RevokeRefreshToken(ctx, requestID)
|
2020-12-01 19:01:23 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
|
2020-12-01 19:01:23 +00:00
|
|
|
func (KubeStorage) GetClient(_ context.Context, id string) (fosite.Client, error) {
|
|
|
|
client := PinnipedCLIOIDCClient()
|
|
|
|
if client.ID == id {
|
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
return nil, fosite.ErrNotFound
|
|
|
|
}
|
|
|
|
|
2020-12-08 19:47:39 +00:00
|
|
|
//
|
|
|
|
// Unused interface methods.
|
|
|
|
//
|
|
|
|
|
2020-12-01 19:01:23 +00:00
|
|
|
func (KubeStorage) ClientAssertionJWTValid(_ context.Context, _ string) error {
|
|
|
|
return errKubeStorageNotImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
func (KubeStorage) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error {
|
|
|
|
return errKubeStorageNotImplemented
|
|
|
|
}
|