2023-06-05 21:40:39 +00:00
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
2020-11-19 04:30:05 +00:00
// SPDX-License-Identifier: Apache-2.0
package authorizationcode
import (
"context"
"crypto/ed25519"
"crypto/x509"
"encoding/json"
2020-12-02 22:10:41 +00:00
"fmt"
2020-11-19 04:30:05 +00:00
"math/rand"
"net/url"
"strings"
"testing"
"time"
fuzz "github.com/google/gofuzz"
"github.com/ory/fosite"
2020-12-10 22:47:58 +00:00
"github.com/ory/fosite/handler/oauth2"
2021-10-22 21:32:26 +00:00
"github.com/ory/fosite/handler/openid"
2022-12-14 00:18:51 +00:00
"github.com/ory/fosite/token/jwt"
2020-12-02 01:18:32 +00:00
"github.com/pkg/errors"
2020-11-19 04:30:05 +00:00
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
corev1 "k8s.io/api/core/v1"
2020-12-10 22:47:58 +00:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2020-11-19 04:30:05 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-12-10 22:47:58 +00:00
"k8s.io/apimachinery/pkg/runtime"
2020-11-19 04:30:05 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2021-10-08 22:48:21 +00:00
"k8s.io/apimachinery/pkg/types"
2020-11-19 04:30:05 +00:00
"k8s.io/client-go/kubernetes/fake"
2020-12-10 22:47:58 +00:00
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
2020-12-02 22:10:41 +00:00
kubetesting "k8s.io/client-go/testing"
2021-12-10 22:22:36 +00:00
clocktesting "k8s.io/utils/clock/testing"
2020-12-01 22:53:22 +00:00
2023-06-22 22:12:33 +00:00
"go.pinniped.dev/internal/federationdomain/clientregistry"
2020-12-01 22:53:22 +00:00
"go.pinniped.dev/internal/fositestorage"
2021-10-06 22:28:13 +00:00
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil"
2020-11-19 04:30:05 +00:00
)
2020-12-02 01:18:32 +00:00
const namespace = "test-ns"
2020-12-10 20:15:40 +00:00
var fakeNow = time . Date ( 2030 , time . January , 1 , 0 , 0 , 0 , 0 , time . UTC )
2020-12-10 22:47:58 +00:00
var lifetime = time . Minute * 10
var fakeNowPlusLifetimeAsString = metav1 . Time { Time : fakeNow . Add ( lifetime ) } . Format ( time . RFC3339 )
2020-12-10 20:15:40 +00:00
2020-12-02 01:18:32 +00:00
func TestAuthorizationCodeStorage ( t * testing . T ) {
2020-11-19 04:30:05 +00:00
secretsGVR := schema . GroupVersionResource {
Group : "" ,
Version : "v1" ,
Resource : "secrets" ,
}
2020-12-02 22:10:41 +00:00
wantActions := [ ] kubetesting . Action {
kubetesting . NewCreateAction ( secretsGVR , namespace , & corev1 . Secret {
2020-12-02 01:18:32 +00:00
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
2020-12-04 22:39:11 +00:00
"storage.pinniped.dev/type" : "authcode" ,
2020-12-02 01:18:32 +00:00
} ,
2020-12-10 20:15:40 +00:00
Annotations : map [ string ] string {
2020-12-10 22:47:58 +00:00
"storage.pinniped.dev/garbage-collect-after" : fakeNowPlusLifetimeAsString ,
2020-12-10 20:15:40 +00:00
} ,
2020-11-19 04:30:05 +00:00
} ,
2020-12-02 01:18:32 +00:00
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "active":true,"request": { "id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client": { "id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form": { "key":["val"]},"session": { "fosite": { "id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom": { "username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc": { "upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"5"} ` ) ,
2020-12-02 01:18:32 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
2020-11-19 04:30:05 +00:00
} ,
2020-12-02 01:18:32 +00:00
Type : "storage.pinniped.dev/authcode" ,
} ) ,
2020-12-02 22:10:41 +00:00
kubetesting . NewGetAction ( secretsGVR , namespace , "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ) ,
kubetesting . NewGetAction ( secretsGVR , namespace , "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ) ,
2022-08-26 17:57:45 +00:00
kubetesting . NewGetAction ( secretsGVR , namespace , "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ) ,
2020-12-02 22:10:41 +00:00
kubetesting . NewUpdateAction ( secretsGVR , namespace , & corev1 . Secret {
2020-12-02 01:18:32 +00:00
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
2020-12-04 22:39:11 +00:00
"storage.pinniped.dev/type" : "authcode" ,
2020-11-19 04:30:05 +00:00
} ,
2020-12-10 20:15:40 +00:00
Annotations : map [ string ] string {
2020-12-10 22:47:58 +00:00
"storage.pinniped.dev/garbage-collect-after" : fakeNowPlusLifetimeAsString ,
2020-12-10 20:15:40 +00:00
} ,
2020-11-19 04:30:05 +00:00
} ,
2020-12-02 01:18:32 +00:00
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "active":false,"request": { "id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client": { "id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":""},"scopes":null,"grantedScopes":null,"form": { "key":["val"]},"session": { "fosite": { "id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom": { "username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc": { "upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"5"} ` ) ,
2020-12-02 01:18:32 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
} ) ,
}
2020-12-10 22:47:58 +00:00
ctx , client , _ , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
request := & fosite . Request {
ID : "abcd-1" ,
RequestedAt : time . Time { } ,
2021-06-15 16:27:30 +00:00
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 : "" ,
2020-12-02 01:18:32 +00:00
} ,
2020-11-19 04:30:05 +00:00
} ,
2021-10-06 22:28:13 +00:00
RequestedScope : nil ,
GrantedScope : nil ,
Form : url . Values { "key" : [ ] string { "val" } } ,
Session : testutil . NewFakePinnipedSession ( ) ,
2020-12-02 01:18:32 +00:00
RequestedAudience : nil ,
GrantedAudience : nil ,
2020-11-19 04:30:05 +00:00
}
2020-12-02 01:18:32 +00:00
err := storage . CreateAuthorizeCodeSession ( ctx , "fancy-signature" , request )
require . NoError ( t , err )
newRequest , err := storage . GetAuthorizeCodeSession ( ctx , "fancy-signature" , nil )
require . NoError ( t , err )
require . Equal ( t , request , newRequest )
err = storage . InvalidateAuthorizeCodeSession ( ctx , "fancy-signature" )
require . NoError ( t , err )
2021-10-06 22:28:13 +00:00
testutil . LogActualJSONFromCreateAction ( t , client , 0 ) // makes it easier to update expected values when needed
2022-08-26 17:57:45 +00:00
testutil . LogActualJSONFromUpdateAction ( t , client , 4 ) // makes it easier to update expected values when needed
2020-12-02 01:18:32 +00:00
require . Equal ( t , wantActions , client . Actions ( ) )
2020-12-02 22:10:41 +00:00
// Doing a Get on an invalidated session should still return the session, but also return an error.
invalidatedRequest , err := storage . GetAuthorizeCodeSession ( ctx , "fancy-signature" , nil )
require . EqualError ( t , err , "authorization code session for fancy-signature has already been used: Authorization code has ben invalidated" )
require . Equal ( t , "abcd-1" , invalidatedRequest . GetID ( ) )
2020-12-02 01:18:32 +00:00
}
func TestGetNotFound ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , _ , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
_ , notFoundErr := storage . GetAuthorizeCodeSession ( ctx , "non-existent-signature" , nil )
require . EqualError ( t , notFoundErr , "not_found" )
require . True ( t , errors . Is ( notFoundErr , fosite . ErrNotFound ) )
}
2020-12-02 22:10:41 +00:00
func TestInvalidateWhenNotFound ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , _ , storage := makeTestSubject ( )
2020-12-02 22:10:41 +00:00
notFoundErr := storage . InvalidateAuthorizeCodeSession ( ctx , "non-existent-signature" )
require . EqualError ( t , notFoundErr , "not_found" )
require . True ( t , errors . Is ( notFoundErr , fosite . ErrNotFound ) )
}
func TestInvalidateWhenConflictOnUpdateHappens ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , client , _ , storage := makeTestSubject ( )
2020-12-02 22:10:41 +00:00
client . PrependReactor ( "update" , "secrets" , func ( _ kubetesting . Action ) ( bool , runtime . Object , error ) {
return true , nil , apierrors . NewConflict ( schema . GroupResource {
Group : "" ,
Resource : "secrets" ,
} , "some-secret-name" , fmt . Errorf ( "there was a conflict" ) )
} )
request := & fosite . Request {
ID : "some-request-id" ,
2021-06-15 16:27:30 +00:00
Client : & clientregistry . Client { } ,
2021-10-06 22:28:13 +00:00
Session : testutil . NewFakePinnipedSession ( ) ,
2020-12-02 22:10:41 +00:00
}
err := storage . CreateAuthorizeCodeSession ( ctx , "fancy-signature" , request )
require . NoError ( t , err )
err = storage . InvalidateAuthorizeCodeSession ( ctx , "fancy-signature" )
require . EqualError ( t , err , ` The request could not be completed due to concurrent access: failed to update authcode for signature fancy-signature at resource version : Operation cannot be fulfilled on secrets "some-secret-name": there was a conflict ` )
}
2020-12-02 01:18:32 +00:00
func TestWrongVersion ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , secrets , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
secret := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
2020-12-04 22:39:11 +00:00
"storage.pinniped.dev/type" : "authcode" ,
2020-12-02 01:18:32 +00:00
} ,
} ,
Data : map [ string ] [ ] byte {
2021-10-06 22:28:13 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "request": { "id":"abcd-1"},"version":"not-the-right-version","active": true} ` ) ,
2020-12-02 01:18:32 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
}
_ , err := secrets . Create ( ctx , secret , metav1 . CreateOptions { } )
require . NoError ( t , err )
_ , err = storage . GetAuthorizeCodeSession ( ctx , "fancy-signature" , nil )
2023-06-05 21:40:39 +00:00
require . EqualError ( t , err , "authorization request data has wrong version: authorization code session for fancy-signature has version not-the-right-version instead of 5" )
2020-12-02 01:18:32 +00:00
}
func TestNilSessionRequest ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , secrets , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
secret := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
2020-12-04 22:39:11 +00:00
"storage.pinniped.dev/type" : "authcode" ,
2020-12-02 01:18:32 +00:00
} ,
} ,
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "nonsense-key": "nonsense-value", "version":"5", "active": true} ` ) ,
2020-12-02 01:18:32 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
2020-11-19 04:30:05 +00:00
}
2020-12-02 01:18:32 +00:00
_ , err := secrets . Create ( ctx , secret , metav1 . CreateOptions { } )
require . NoError ( t , err )
_ , err = storage . GetAuthorizeCodeSession ( ctx , "fancy-signature" , nil )
require . EqualError ( t , err , "malformed authorization code session for fancy-signature: authorization request data must be present" )
}
func TestCreateWithNilRequester ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , _ , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
err := storage . CreateAuthorizeCodeSession ( ctx , "signature-doesnt-matter" , nil )
require . EqualError ( t , err , "requester must be of type fosite.Request" )
2020-11-19 04:30:05 +00:00
}
2020-12-02 01:18:32 +00:00
func TestCreateWithWrongRequesterDataTypes ( t * testing . T ) {
2020-12-10 22:47:58 +00:00
ctx , _ , _ , storage := makeTestSubject ( )
2020-12-02 01:18:32 +00:00
request := & fosite . Request {
Session : nil ,
2021-06-15 16:27:30 +00:00
Client : & clientregistry . Client { } ,
2020-11-19 04:30:05 +00:00
}
2020-12-02 01:18:32 +00:00
err := storage . CreateAuthorizeCodeSession ( ctx , "signature-doesnt-matter" , request )
2021-10-06 22:28:13 +00:00
require . EqualError ( t , err , "requester's session must be of type PinnipedSession" )
2020-11-19 04:30:05 +00:00
2020-12-02 01:18:32 +00:00
request = & fosite . Request {
2021-10-06 22:28:13 +00:00
Session : & psession . PinnipedSession { } ,
2020-12-02 01:18:32 +00:00
Client : nil ,
}
err = storage . CreateAuthorizeCodeSession ( ctx , "signature-doesnt-matter" , request )
2021-06-15 16:27:30 +00:00
require . EqualError ( t , err , "requester's client must be of type clientregistry.Client" )
2020-11-19 04:30:05 +00:00
}
2020-12-10 22:47:58 +00:00
func makeTestSubject ( ) ( context . Context , * fake . Clientset , corev1client . SecretInterface , oauth2 . AuthorizeCodeStorage ) {
client := fake . NewSimpleClientset ( )
secrets := client . CoreV1 ( ) . Secrets ( namespace )
2021-12-10 22:22:36 +00:00
return context . Background ( ) , client , secrets , New ( secrets , clocktesting . NewFakeClock ( fakeNow ) . Now , lifetime )
2020-12-10 22:47:58 +00:00
}
2020-11-19 04:30:05 +00:00
// TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession asserts that we can correctly round trip our authorize code session.
// It will detect any changes to fosite.AuthorizeRequest and guarantees that all interface types have concrete implementations.
func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession ( t * testing . T ) {
validSession := NewValidEmptyAuthorizeCodeSession ( )
// sanity check our valid session
2020-12-01 22:53:22 +00:00
extractedRequest , err := fositestorage . ValidateAndExtractAuthorizeRequest ( validSession . Request )
2020-11-19 04:30:05 +00:00
require . NoError ( t , err )
require . Equal ( t , validSession . Request , extractedRequest )
// checked above
2021-06-15 16:27:30 +00:00
defaultClient := validSession . Request . Client . ( * clientregistry . Client )
2021-10-06 22:28:13 +00:00
pinnipedSession := validSession . Request . Session . ( * psession . PinnipedSession )
2020-11-19 04:30:05 +00:00
// makes it easier to use a raw string
replacer := strings . NewReplacer ( "`" , "a" )
randString := func ( c fuzz . Continue ) string {
for {
s := c . RandString ( )
if len ( s ) == 0 {
continue // skip empty string
}
return replacer . Replace ( s )
}
}
2020-12-01 19:01:23 +00:00
// deterministic fuzzing of fosite.Request
2020-11-19 04:30:05 +00:00
f := fuzz . New ( ) . RandSource ( rand . NewSource ( 1 ) ) . NilChance ( 0 ) . NumElements ( 1 , 3 ) . Funcs (
// these functions guarantee that these are the only interface types we need to fill out
2020-12-01 19:01:23 +00:00
// if fosite.Request changes to add more, the fuzzer will panic
2020-11-19 04:30:05 +00:00
func ( fc * fosite . Client , c fuzz . Continue ) {
c . Fuzz ( defaultClient )
* fc = defaultClient
} ,
func ( fs * fosite . Session , c fuzz . Continue ) {
2021-10-06 22:28:13 +00:00
c . Fuzz ( pinnipedSession )
* fs = pinnipedSession
2020-11-19 04:30:05 +00:00
} ,
// these types contain an interface{} that we need to handle
2021-10-06 22:28:13 +00:00
// this is safe because we explicitly provide the PinnipedSession concrete type
2020-11-19 04:30:05 +00:00
func ( value * map [ string ] interface { } , c fuzz . Continue ) {
// cover all the JSON data types just in case
* value = map [ string ] interface { } {
randString ( c ) : float64 ( c . Intn ( 1 << 32 ) ) ,
randString ( c ) : map [ string ] interface { } {
randString ( c ) : [ ] interface { } { float64 ( c . Intn ( 1 << 32 ) ) } ,
randString ( c ) : map [ string ] interface { } {
randString ( c ) : nil ,
randString ( c ) : map [ string ] interface { } {
randString ( c ) : c . RandBool ( ) ,
} ,
} ,
} ,
}
} ,
// JWK contains an interface{} Key that we need to handle
// this is safe because JWK explicitly implements JSON marshalling and unmarshalling
func ( jwk * jose . JSONWebKey , c fuzz . Continue ) {
key , _ , err := ed25519 . GenerateKey ( c )
require . NoError ( t , err )
jwk . Key = key
// set these fields to make the .Equal comparison work
jwk . Certificates = [ ] * x509 . Certificate { }
jwk . CertificatesURL = & url . URL { }
jwk . CertificateThumbprintSHA1 = [ ] byte { }
jwk . CertificateThumbprintSHA256 = [ ] byte { }
} ,
// set this to make the .Equal comparison work
// this is safe because Time explicitly implements JSON marshalling and unmarshalling
func ( tp * time . Time , c fuzz . Continue ) {
2020-11-19 19:04:25 +00:00
* tp = time . Unix ( c . Int63n ( 1 << 32 ) , c . Int63n ( 1 << 32 ) ) . UTC ( )
2020-11-19 04:30:05 +00:00
} ,
// make random strings that do not contain any ` characters
func ( s * string , c fuzz . Continue ) {
* s = randString ( c )
} ,
// handle string type alias
func ( s * fosite . TokenType , c fuzz . Continue ) {
* s = fosite . TokenType ( randString ( c ) )
2021-10-08 22:48:21 +00:00
} ,
func ( s * types . UID , c fuzz . Continue ) {
* s = types . UID ( randString ( c ) )
2020-11-19 04:30:05 +00:00
} ,
// handle string type alias
func ( s * fosite . Arguments , c fuzz . Continue ) {
n := c . Intn ( 3 ) + 1 // 1 to 3 items
arguments := make ( fosite . Arguments , n )
for i := range arguments {
arguments [ i ] = randString ( c )
}
* s = arguments
} ,
)
f . Fuzz ( validSession )
const name = "fuzz" // value is irrelevant
ctx := context . Background ( )
secrets := fake . NewSimpleClientset ( ) . CoreV1 ( ) . Secrets ( name )
2020-12-10 22:47:58 +00:00
storage := New ( secrets , func ( ) time . Time { return fakeNow } , lifetime )
2020-11-19 04:30:05 +00:00
// issue a create using the fuzzed request to confirm that marshalling works
err = storage . CreateAuthorizeCodeSession ( ctx , name , validSession . Request )
require . NoError ( t , err )
// retrieve a copy of the fuzzed request from storage to confirm that unmarshalling works
newRequest , err := storage . GetAuthorizeCodeSession ( ctx , name , nil )
require . NoError ( t , err )
// the fuzzed request and the copy from storage should be exactly the same
require . Equal ( t , validSession . Request , newRequest )
secretList , err := secrets . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
require . Len ( t , secretList . Items , 1 )
authorizeCodeSessionJSONFromStorage := string ( secretList . Items [ 0 ] . Data [ "pinniped-storage-data" ] )
// set these to match CreateAuthorizeCodeSession so that .JSONEq works
validSession . Active = true
2023-06-05 21:40:39 +00:00
validSession . Version = "5" // update this when you update the storage version in the production code
2020-11-19 04:30:05 +00:00
validSessionJSONBytes , err := json . MarshalIndent ( validSession , "" , "\t" )
require . NoError ( t , err )
authorizeCodeSessionJSONFromFuzzing := string ( validSessionJSONBytes )
// the fuzzed session and storage session should have identical JSON
require . JSONEq ( t , authorizeCodeSessionJSONFromFuzzing , authorizeCodeSessionJSONFromStorage )
2023-06-05 21:40:39 +00:00
t . Log ( "actual value from fuzzing" , authorizeCodeSessionJSONFromFuzzing ) // can be useful when updating expected value
2021-12-06 22:43:39 +00:00
2020-11-19 04:30:05 +00:00
// while the fuzzer will panic if AuthorizeRequest changes in a way that cannot be fuzzed,
// if it adds a new field that can be fuzzed, this check will fail
// thus if AuthorizeRequest changes, we will detect it here (though we could possibly miss an omitempty field)
2021-12-10 22:22:36 +00:00
require . JSONEq ( t , ExpectedAuthorizeCodeSessionJSONFromFuzzing , authorizeCodeSessionJSONFromFuzzing , "actual:\n%s" , authorizeCodeSessionJSONFromFuzzing )
2020-11-19 04:30:05 +00:00
}
2021-10-22 21:32:26 +00:00
func TestReadFromSecret ( t * testing . T ) {
tests := [ ] struct {
name string
secret * corev1 . Secret
2021-11-10 23:34:19 +00:00
wantSession * Session
2021-10-22 21:32:26 +00:00
wantErr string
} {
{
name : "happy path" ,
secret : & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
"storage.pinniped.dev/type" : "authcode" ,
} ,
} ,
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "request": { "id":"abcd-1","session": { "fosite": { "id_token_claims": { "jti": "xyz"},"headers": { "extra": { "myheader": "foo"}},"expires_at":null,"username":"snorlax","subject":"panda"},"custom": { "username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","oidc": { "upstreamRefreshToken":"fake-upstream-refresh-token"}}}},"version":"5","active": true} ` ) ,
2021-10-22 21:32:26 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
} ,
2021-11-10 23:34:19 +00:00
wantSession : & Session {
2023-06-05 21:40:39 +00:00
Version : "5" ,
2021-10-22 21:32:26 +00:00
Active : true ,
Request : & fosite . Request {
ID : "abcd-1" ,
Client : & clientregistry . Client { } ,
Session : & psession . PinnipedSession {
Fosite : & openid . DefaultSession {
Username : "snorlax" ,
Subject : "panda" ,
2022-12-14 00:18:51 +00:00
Claims : & jwt . IDTokenClaims { JTI : "xyz" } ,
Headers : & jwt . Headers { Extra : map [ string ] interface { } { "myheader" : "foo" } } ,
2021-10-22 21:32:26 +00:00
} ,
Custom : & psession . CustomSessionData {
2023-06-05 21:40:39 +00:00
Username : "fake-username" ,
ProviderUID : "fake-provider-uid" ,
ProviderName : "fake-provider-name" ,
ProviderType : "fake-provider-type" ,
UpstreamUsername : "fake-upstream-username" ,
UpstreamGroups : [ ] string { "fake-upstream-group1" , "fake-upstream-group2" } ,
2021-10-22 21:32:26 +00:00
OIDC : & psession . OIDCSessionData {
UpstreamRefreshToken : "fake-upstream-refresh-token" ,
} ,
} ,
} ,
} ,
} ,
} ,
{
name : "wrong secret type" ,
secret : & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
"storage.pinniped.dev/type" : "authcode" ,
} ,
} ,
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "request": { "id":"abcd-1"},"version":"5","active": true} ` ) ,
2021-10-22 21:32:26 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/not-authcode" ,
} ,
wantErr : "secret storage data has incorrect type: storage.pinniped.dev/not-authcode must equal storage.pinniped.dev/authcode" ,
} ,
{
name : "wrong session version" ,
secret : & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
"storage.pinniped.dev/type" : "authcode" ,
} ,
} ,
Data : map [ string ] [ ] byte {
"pinniped-storage-data" : [ ] byte ( ` { "request": { "id":"abcd-1"},"version":"wrong-version-here","active": true} ` ) ,
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
} ,
2023-06-05 21:40:39 +00:00
wantErr : "authorization request data has wrong version: authorization code session has version wrong-version-here instead of 5" ,
2021-10-22 21:32:26 +00:00
} ,
{
name : "missing request" ,
secret : & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : "pinniped-storage-authcode-pwu5zs7lekbhnln2w4" ,
ResourceVersion : "" ,
Labels : map [ string ] string {
"storage.pinniped.dev/type" : "authcode" ,
} ,
} ,
Data : map [ string ] [ ] byte {
2023-06-05 21:40:39 +00:00
"pinniped-storage-data" : [ ] byte ( ` { "version":"5","active": true} ` ) ,
2021-10-22 21:32:26 +00:00
"pinniped-storage-version" : [ ] byte ( "1" ) ,
} ,
Type : "storage.pinniped.dev/authcode" ,
} ,
wantErr : "malformed authorization code session: authorization request data must be present" ,
} ,
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
t . Parallel ( )
session , err := ReadFromSecret ( tt . secret )
if tt . wantErr == "" {
require . NoError ( t , err )
require . Equal ( t , tt . wantSession , session )
} else {
require . EqualError ( t , err , tt . wantErr )
require . Nil ( t , session )
}
} )
}
}