From c8eaa3f383ff7fca98772f2f35cdd4ebc345ccdf Mon Sep 17 00:00:00 2001 From: Margo Crawford Date: Tue, 1 Dec 2020 11:01:23 -0800 Subject: [PATCH] WIP towards using k8s fosite storage in the supervisor's callback endpoint - Note that this WIP commit includes a failing unit test, which will be addressed in the next commit Signed-off-by: Ryan Richard --- internal/crud/crud_test.go | 6 +- .../authorizationcode/authorizationcode.go | 223 +++++++----------- .../authorizationcode_test.go | 115 ++++----- internal/fositestorage/pkce/pkce.go | 115 +++++++++ internal/fositestorage/pkce/pkce_test.go | 100 ++++++++ .../oidc/callback/callback_handler_test.go | 105 +++++---- internal/oidc/kube_storage.go | 110 +++++++++ internal/oidc/nullstorage.go | 30 +-- test/integration/storage_test.go | 4 +- 9 files changed, 548 insertions(+), 260 deletions(-) rename internal/{fosite => fositestorage}/authorizationcode/authorizationcode.go (57%) rename internal/{fosite => fositestorage}/authorizationcode/authorizationcode_test.go (63%) create mode 100644 internal/fositestorage/pkce/pkce.go create mode 100644 internal/fositestorage/pkce/pkce_test.go create mode 100644 internal/oidc/kube_storage.go diff --git a/internal/crud/crud_test.go b/internal/crud/crud_test.go index 93ee9818..cb0fc147 100644 --- a/internal/crud/crud_test.go +++ b/internal/crud/crud_test.go @@ -62,17 +62,17 @@ func TestStorage(t *testing.T) { }{ { name: "get non-existent", - resource: "authorization-codes", + resource: "authcode", mocks: nil, run: func(t *testing.T, storage Storage) error { _, err := storage.Get(ctx, "not-exists", nil) return err }, wantActions: []coretesting.Action{ - coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authorization-codes-t2fx46yyvs3a"), + coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authcode-t2fx46yyvs3a"), }, wantSecrets: nil, - wantErr: `failed to get authorization-codes for signature not-exists: secrets "pinniped-storage-authorization-codes-t2fx46yyvs3a" not found`, + wantErr: `failed to get authcode for signature not-exists: secrets "pinniped-storage-authcode-t2fx46yyvs3a" not found`, }, { name: "delete non-existent", diff --git a/internal/fosite/authorizationcode/authorizationcode.go b/internal/fositestorage/authorizationcode/authorizationcode.go similarity index 57% rename from internal/fosite/authorizationcode/authorizationcode.go rename to internal/fositestorage/authorizationcode/authorizationcode.go index 917c5cc0..e41059b3 100644 --- a/internal/fosite/authorizationcode/authorizationcode.go +++ b/internal/fositestorage/authorizationcode/authorizationcode.go @@ -19,7 +19,7 @@ import ( ) const ( - ErrInvalidAuthorizeRequestType = constable.Error("authorization request must be of type fosite.AuthorizeRequest") + ErrInvalidAuthorizeRequestType = constable.Error("authorization request must be of type fosite.Request") ErrInvalidAuthorizeRequestData = constable.Error("authorization request data must not be nil") ErrInvalidAuthorizeRequestVersion = constable.Error("authorization request data has wrong version") @@ -33,26 +33,25 @@ type authorizeCodeStorage struct { } type AuthorizeCodeSession struct { - Active bool `json:"active"` - Request *fosite.AuthorizeRequest `json:"request"` - Version string `json:"version"` + Active bool `json:"active"` + Request *fosite.Request `json:"request"` + Version string `json:"version"` } func New(secrets corev1client.SecretInterface) oauth2.AuthorizeCodeStorage { - return &authorizeCodeStorage{storage: crud.New("authorization-codes", secrets)} + return &authorizeCodeStorage{storage: crud.New("authcode", secrets)} } func (a *authorizeCodeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, requester fosite.Requester) error { - // this conversion assumes that we do not wrap the default type in any way + // This conversion assumes that we do not wrap the default type in any way // i.e. we use the default fosite.OAuth2Provider.NewAuthorizeRequest implementation // note that because this type is serialized and stored in Kube, we cannot easily change the implementation later - // TODO hydra uses the fosite.Request struct and ignores the extra fields in fosite.AuthorizeRequest request, err := validateAndExtractAuthorizeRequest(requester) if err != nil { return err } - // TODO hydra stores specific fields from the requester + // Note, in case it is helpful, that Hydra stores specific fields from the requester: // request ID // requestedAt // OAuth client ID @@ -70,12 +69,11 @@ func (a *authorizeCodeStorage) CreateAuthorizeCodeSession(ctx context.Context, s } func (a *authorizeCodeStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, _ fosite.Session) (fosite.Requester, error) { - // TODO hydra uses the incoming fosite.Session to provide the type needed to json.Unmarshal their session bytes - - // TODO hydra gets the client from its DB as a concrete type via client ID, - // the hydra memory client just validates that the client ID exists - - // TODO hydra uses the sha512.Sum384 hash of signature when using JWT as access token to reduce length + // Note, in case it is helpful, that Hydra: + // - uses the incoming fosite.Session to provide the type needed to json.Unmarshal their session bytes + // - gets the client from its DB as a concrete type via client ID, the hydra memory client just validates that the + // client ID exists + // - hydra uses the sha512.Sum384 hash of signature when using JWT as access token to reduce length session, _, err := a.getSession(ctx, signature) @@ -88,8 +86,6 @@ func (a *authorizeCodeStorage) GetAuthorizeCodeSession(ctx context.Context, sign } func (a *authorizeCodeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error { - // TODO write garbage collector for these codes - session, rv, err := a.getSession(ctx, signature) if err != nil { return err @@ -137,17 +133,15 @@ func (a *authorizeCodeStorage) getSession(ctx context.Context, signature string) func NewValidEmptyAuthorizeCodeSession() *AuthorizeCodeSession { return &AuthorizeCodeSession{ - Request: &fosite.AuthorizeRequest{ - Request: fosite.Request{ - Client: &fosite.DefaultOpenIDConnectClient{}, - Session: &openid.DefaultSession{}, - }, + Request: &fosite.Request{ + Client: &fosite.DefaultOpenIDConnectClient{}, + Session: &openid.DefaultSession{}, }, } } -func validateAndExtractAuthorizeRequest(requester fosite.Requester) (*fosite.AuthorizeRequest, error) { - request, ok1 := requester.(*fosite.AuthorizeRequest) +func validateAndExtractAuthorizeRequest(requester fosite.Requester) (*fosite.Request, error) { + request, ok1 := requester.(*fosite.Request) if !ok1 { return nil, ErrInvalidAuthorizeRequestType } @@ -189,59 +183,37 @@ func (e *errSerializationFailureWithCause) Error() string { const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{ "active": true, "request": { - "responseTypes": [ - "¥Îʒ襧.ɕ7崛瀇莒AȒ[ɠ牐7#$ɭ", - ".5ȿELj9ûF済(D疻翋膗", - "螤Yɫüeɯ紤邥翔勋\\RBʒ;-" - ], - "redirectUri": { - "Scheme": "ħesƻU赒M喦_ģ", - "Opaque": "Ġ/_章Ņ缘T蝟NJ儱礹燃ɢ", - "User": {}, - "Host": "ȳ4螘Wo", - "Path": "}i{", - "RawPath": "5Dža丝eF0eė鱊hǒx蔼Q", - "ForceQuery": true, - "RawQuery": "熤1bbWV", - "Fragment": "ȋc剠鏯ɽÿ¸", - "RawFragment": "qƤ" - }, - "state": "@n,x竘Şǥ嗾稀'ã击漰怼禝穞梠Ǫs", - "handledResponseTypes": [ - "m\"e尚鬞ƻɼ抹d誉y鿜Ķ" - ], - "id": "ō澩ć|3U2Ǜl霨ǦǵpƉ", - "requestedAt": "1989-11-05T22:02:31.105295894Z", + "id": "嫎l蟲aƖ啘艿", + "requestedAt": "2082-11-10T18:36:11.627253638Z", "client": { - "id": "[:c顎疻紵D", - "client_secret": "mQ==", + "id": "!ſɄĈp[述齛ʘUȻ.5ȿE", + "client_secret": "UQ==", "redirect_uris": [ - "恣S@T嵇LJV,Æ櫔袆鋹奘菲", - "ãƻʚ肈ą8O+a駣Ʉɼk瘸'鴵y" + "ǣ珑 ʑ飶畛Ȳ螤Yɫüeɯ紤邥翔勋\\", + "Bʒ;", + "鿃攴Ųęʍ鎾ʦ©cÏN,Ġ/_" ], "grant_types": [ - ".湆ê\"唐", - "曎餄FxD溪躲珫ÈşɜȨû臓嬣\"ǃŤz" + "憉sHĒ尥窘挼Ŀʼn" ], "response_types": [ - "Ņʘʟ車sʊ儓JǐŪɺǣy|耑ʄ" + "4", + "ʄÔ@}i{絧遗Ū^ȝĸ谋Vʋ鱴閇T" ], "scopes": [ - "Ą", - "萙Į(潶饏熞ĝƌĆ1", - "əȤ4Į筦p煖鵄$睱奐耡q" + "R鴝順諲ŮŚ节ȭŀȋc剠鏯ɽÿ¸" ], "audience": [ - "Ʃǣ鿫/Ò敫ƤV" + "Ƥ" ], "public": true, - "jwks_uri": "ȩđ[嬧鱒Ȁ彆媚杨嶒ĤG", + "jwks_uri": "BA瘪囷ɫCʄɢ雐譄uée'", "jwks": { "keys": [ { "kty": "OKP", "crv": "Ed25519", - "x": "JmA-6KpjzqKu0lq9OiB6ORL4s2UzBFPsE1hm6vESeXM", + "x": "nK9xgX_iN7u3u_i8YOO7ZRT_WK028Vd_nhtsUu7Eo6E", "x5u": { "Scheme": "", "Opaque": "", @@ -258,24 +230,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{ { "kty": "OKP", "crv": "Ed25519", - "x": "LbRC1_3HEe5o7Japk9jFp3_7Ou7Gi2gpqrVrIi0eLDQ", - "x5u": { - "Scheme": "", - "Opaque": "", - "User": null, - "Host": "", - "Path": "", - "RawPath": "", - "ForceQuery": false, - "RawQuery": "", - "Fragment": "", - "RawFragment": "" - } - }, - { - "kty": "OKP", - "crv": "Ed25519", - "x": "Ovk4DF8Yn3mkULuTqnlGJxFnKGu9EL6Xcf2Nql9lK3c", + "x": "UbbswQgzWhfGCRlwQmMp6fw_HoIoqkIaKT-2XN2fuYU", "x5u": { "Scheme": "", "Opaque": "", @@ -291,91 +246,95 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{ } ] }, - "token_endpoint_auth_method": "\u0026(K鵢Kj ŏ9Q韉Ķ%嶑輫ǘ(", + "token_endpoint_auth_method": "ŚǗƳȕ暭Q0ņP羾,塐", "request_uris": [ - ":", - "6ě#嫀^xz Ū胧r" + "lj翻LH^俤µDzɹ@©|\u003eɃ", + "[:c顎疻紵D" ], - "request_object_signing_alg": "^¡!犃ĹĐJí¿ō擫ų懫砰¿", - "token_endpoint_auth_signing_alg": "ƈŮå" + "request_object_signing_alg": "m1Ì恣S@T嵇LJV,Æ櫔袆鋹奘", + "token_endpoint_auth_signing_alg": "Fãƻʚ肈ą8O+a駣" }, "scopes": [ - "阃.Ù頀ʌGa皶竇瞍涘¹", - "ȽŮ切衖庀ŰŒ矠", - "楓)馻řĝǕ菸Tĕ1伞柲\u003c\"ʗȆ\\雤" + "ɼk瘸'鴵yſǮŁ±\u003eFA曎餄FxD溪", + "綻N镪p赌h%桙dĽ" ], "grantedScopes": [ - "ơ鮫R嫁ɍUƞ9+u!Ȱ", - "}Ă岜" + "癗E]Ņʘʟ車s" ], "form": { - "旸Ť/Õ薝隧;綡,鼞纂=": [ - "[滮]憀", - "3\u003eÙœ蓄UK嗤眇疟Țƒ1v¸KĶ" + "蹬器ķ8ŷ萒寎廭#疶昄Ą-Ƃƞ轵": [ + "熞ĝƌĆ1ȇyǴ濎=Tʉȼʁŀ\u003c", + "耡q戨稞R÷mȵg釽[ƞ@", + "đ[嬧鱒Ȁ彆媚杨嶒ĤGÀ吧Lŷ" + ], + "餟": [ + "蒍z\u0026(K鵢Kj ŏ9Q韉Ķ%", + "輫ǘ(¨Ƞ亱6ě#嫀^xz ", + "@耢ɝ^¡!犃ĹĐJí¿ō擫" ] }, "session": { "Claims": { - "JTI": "};Ų斻遟a衪荖舃", - "Issuer": "芠顋敀拲h蝺$!", - "Subject": "}j%(=ſ氆]垲莲顇", + "JTI": "懫砰¿C筽娴ƓaPu镈賆ŗɰ", + "Issuer": "皶竇瞍涘¹焕iǢǽɽĺŧ", + "Subject": "矠M6ɡǜg炾ʙ$%o6肿Ȫ", "Audience": [ - "彑V\\廳蟕Țǡ蔯ʠ浵Ī龉磈螖畭5", - "渇Ȯʕc" + "ƌÙ鯆GQơ鮫R嫁ɍUƞ9+u!Ȱ踾$" ], - "Nonce": "Ǖ=rlƆ褡{ǏS", - "ExpiresAt": "1975-11-17T14:21:34.205609651Z", - "IssuedAt": "2104-07-03T15:40:03.66710966Z", - "RequestedAt": "2031-05-18T05:14:19.449350555Z", - "AuthTime": "2018-01-27T07:55:06.056862114Z", - "AccessTokenHash": "鹰肁躧", - "AuthenticationContextClassReference": "}Ɇ", - "AuthenticationMethodsReference": "DQh:uȣ", - "CodeHash": "ɘȏıȒ諃龟", + "Nonce": "us旸Ť/Õ薝隧;綡,鼞", + "ExpiresAt": "2065-11-30T13:47:03.613000626Z", + "IssuedAt": "1976-02-22T09:57:20.479850437Z", + "RequestedAt": "2016-04-13T04:18:53.648949323Z", + "AuthTime": "2098-07-12T04:38:54.034043015Z", + "AccessTokenHash": "滮]", + "AuthenticationContextClassReference": "°3\u003eÙ", + "AuthenticationMethodsReference": "k?µ鱔ǤÂ", + "CodeHash": "Țƒ1v¸KĶ跭};", "Extra": { - "a": { - "^i臏f恡ƨ彮": { - "DĘ敨ýÏʥZq7烱藌\\": null, - "V": { - "őŧQĝ微'X焌襱ǭɕņ殥!_n": false - } - }, - "Ż猁": [ - 1706822246 - ] + "=ſ氆": { + "Ƿī,廖ʡ彑V\\廳蟕Ț": [ + 843216989 + ], + "蔯ʠ浵Ī": { + "H\"nǕ=rlƆ褡{ǏSȳŅ": { + "Žg": false + }, + "枱鰧ɛ鸁A渇": null + } }, - "Ò椪)ɫqň2搞Ŀ高摠鲒鿮禗O": 1233332227 + "斻遟a衪荖舃9闄岈锘肺ńʥƕU}j%": 2520197933 } }, "Headers": { "Extra": { - "?戋璖$9\u0026": { - "µcɕ餦ÑEǰ哤癨浦浏1R": [ - 3761201123 - ], - "頓ć§蚲6rǦ\u003cqċ": { - "Łʀ§ȏœɽDz斡冭ȸěaʜD捛?½ʀ+": null, - "ɒúIJ誠ƉyÖ.峷1藍殙菥趏": { - "jHȬȆ#)\u003cX": true + "熒ɘȏıȒ諃龟ŴŠ'耐Ƭ扵ƹ玄ɕwL": { + "ýÏʥZq7烱藌\\捀¿őŧQ": { + "微'X焌襱ǭɕņ殥!_": null, + "荇届UȚ?戋璖$9\u00269舋": { + "ɕ餦ÑEǰ哤癨浦浏1Rk頓ć§蚲6": true } - } + }, + "鲒鿮禗O暒aJP鐜?ĮV嫎h譭ȉ]DĘ": [ + 954647573 + ] }, - "U": 1354158262 + "皩Ƭ}Ɇ.雬Ɨ´唁": 1572524915 } }, "ExpiresAt": { - "\"嘬ȹĹaó剺撱Ȱ": "1985-09-09T04:35:40.533197189Z", - "ʆ\u003e": "1998-08-07T05:37:11.759718906Z", - "柏ʒ鴙*鸆偡Ȓ肯Ûx": "2036-12-19T06:36:14.414805124Z" + "\u003cqċ譈8ŪɎP绿MÅ": "2031-10-18T22:07:34.950803105Z", + "ȸěaʜD捛?½ʀ+Ċ偢镳ʬÍɷȓ\u003c": "2049-05-13T15:27:20.968432454Z" }, - "Username": "qmʎaðƠ绗ʢ緦Hū", - "Subject": "屾Ê窢ɋ鄊qɠ谫ǯǵƕ牀1鞊\\ȹ)" + "Username": "1藍殙菥趏酱Nʎ\u0026^横懋ƶ峦Fïȫƅw", + "Subject": "檾ĩĆ爨4犹|v炩f柏ʒ鴙*鸆偡" }, "requestedAudience": [ - "鉍商OɄƣ圔,xĪɏV鵅砍" + "肯Ûx穞Ƀ", + "ź蕴3ǐ薝Ƅ腲=ʐ诂鱰屾Ê窢ɋ鄊qɠ谫" ], "grantedAudience": [ - "C笜嚯\u003cǐšɚĀĥʋ6鉅\\þc涎漄Ɨ腼" + "ǵƕ牀1鞊\\ȹ)}鉍商OɄƣ圔,xĪ", + "悾xn冏裻摼0Ʈ蚵Ȼ塕»£#稏扟X" ] }, "version": "1" diff --git a/internal/fosite/authorizationcode/authorizationcode_test.go b/internal/fositestorage/authorizationcode/authorizationcode_test.go similarity index 63% rename from internal/fosite/authorizationcode/authorizationcode_test.go rename to internal/fositestorage/authorizationcode/authorizationcode_test.go index d97f2b0c..09e0e374 100644 --- a/internal/fosite/authorizationcode/authorizationcode_test.go +++ b/internal/fositestorage/authorizationcode/authorizationcode_test.go @@ -55,56 +55,39 @@ func TestAuthorizeCodeStorage(t *testing.T) { name: "create, get, invalidate standard flow", mocks: nil, run: func(t *testing.T, storage oauth2.AuthorizeCodeStorage) error { - request := &fosite.AuthorizeRequest{ - ResponseTypes: fosite.Arguments{"not-code"}, - RedirectURI: &url.URL{ - Scheme: "", - Opaque: "weee", - User: &url.Userinfo{}, - Host: "", - Path: "/callback", - RawPath: "", - ForceQuery: false, - RawQuery: "", - Fragment: "", - RawFragment: "", - }, - State: "stated", - HandledResponseTypes: fosite.Arguments{"not-type"}, - 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: "", + 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, }, - RequestedScope: nil, - GrantedScope: nil, - Form: url.Values{"key": []string{"val"}}, - Session: &openid.DefaultSession{ - Claims: nil, - Headers: nil, - ExpiresAt: nil, - Username: "snorlax", - Subject: "panda", - }, - RequestedAudience: nil, - GrantedAudience: nil, + JSONWebKeysURI: "where", + JSONWebKeys: nil, + TokenEndpointAuthMethod: "something", + RequestURIs: nil, + RequestObjectSigningAlgorithm: "", + TokenEndpointAuthSigningAlgorithm: "", }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: &openid.DefaultSession{ + Claims: nil, + Headers: nil, + ExpiresAt: nil, + Username: "snorlax", + Subject: "panda", + }, + RequestedAudience: nil, + GrantedAudience: nil, } err := storage.CreateAuthorizeCodeSession(ctx, "fancy-signature", request) require.NoError(t, err) @@ -118,50 +101,50 @@ func TestAuthorizeCodeStorage(t *testing.T) { wantActions: []coretesting.Action{ coretesting.NewCreateAction(secretsGVR, namespace, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "pinniped-storage-authorization-codes-pwu5zs7lekbhnln2w4", + Name: "pinniped-storage-authcode-pwu5zs7lekbhnln2w4", ResourceVersion: "", Labels: map[string]string{ - "storage.pinniped.dev": "authorization-codes", + "storage.pinniped.dev": "authcode", }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"active":true,"request":{"responseTypes":["not-code"],"redirectUri":{"Scheme":"","Opaque":"weee","User":{},"Host":"","Path":"/callback","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"state":"stated","handledResponseTypes":["not-type"],"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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), "pinniped-storage-version": []byte("1"), }, - Type: "storage.pinniped.dev/authorization-codes", + Type: "storage.pinniped.dev/authcode", }), - coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authorization-codes-pwu5zs7lekbhnln2w4"), - coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authorization-codes-pwu5zs7lekbhnln2w4"), + coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authcode-pwu5zs7lekbhnln2w4"), + coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-authcode-pwu5zs7lekbhnln2w4"), coretesting.NewUpdateAction(secretsGVR, namespace, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "pinniped-storage-authorization-codes-pwu5zs7lekbhnln2w4", + Name: "pinniped-storage-authcode-pwu5zs7lekbhnln2w4", ResourceVersion: "", Labels: map[string]string{ - "storage.pinniped.dev": "authorization-codes", + "storage.pinniped.dev": "authcode", }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"active":false,"request":{"responseTypes":["not-code"],"redirectUri":{"Scheme":"","Opaque":"weee","User":{},"Host":"","Path":"/callback","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"state":"stated","handledResponseTypes":["not-type"],"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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), "pinniped-storage-version": []byte("1"), }, - Type: "storage.pinniped.dev/authorization-codes", + Type: "storage.pinniped.dev/authcode", }), }, wantSecrets: []corev1.Secret{ { ObjectMeta: metav1.ObjectMeta{ - Name: "pinniped-storage-authorization-codes-pwu5zs7lekbhnln2w4", + Name: "pinniped-storage-authcode-pwu5zs7lekbhnln2w4", Namespace: namespace, ResourceVersion: "", Labels: map[string]string{ - "storage.pinniped.dev": "authorization-codes", + "storage.pinniped.dev": "authcode", }, }, Data: map[string][]byte{ - "pinniped-storage-data": []byte(`{"active":false,"request":{"responseTypes":["not-code"],"redirectUri":{"Scheme":"","Opaque":"weee","User":{},"Host":"","Path":"/callback","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"state":"stated","handledResponseTypes":["not-type"],"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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), "pinniped-storage-version": []byte("1"), }, - Type: "storage.pinniped.dev/authorization-codes", + Type: "storage.pinniped.dev/authcode", }, }, wantErr: "", @@ -210,8 +193,8 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) { require.Equal(t, validSession.Request, extractedRequest) // checked above - defaultClient := validSession.Request.Request.Client.(*fosite.DefaultOpenIDConnectClient) - defaultSession := validSession.Request.Request.Session.(*openid.DefaultSession) + defaultClient := validSession.Request.Client.(*fosite.DefaultOpenIDConnectClient) + defaultSession := validSession.Request.Session.(*openid.DefaultSession) // makes it easier to use a raw string replacer := strings.NewReplacer("`", "a") @@ -225,10 +208,10 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) { } } - // deterministic fuzzing of fosite.AuthorizeRequest + // deterministic fuzzing of fosite.Request 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 - // if fosite.AuthorizeRequest changes to add more, the fuzzer will panic + // if fosite.Request changes to add more, the fuzzer will panic func(fc *fosite.Client, c fuzz.Continue) { c.Fuzz(defaultClient) *fc = defaultClient diff --git a/internal/fositestorage/pkce/pkce.go b/internal/fositestorage/pkce/pkce.go new file mode 100644 index 00000000..153d93a2 --- /dev/null +++ b/internal/fositestorage/pkce/pkce.go @@ -0,0 +1,115 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package pkce + +import ( + "context" + "fmt" + + "github.com/ory/fosite" + "github.com/ory/fosite/handler/openid" + "github.com/ory/fosite/handler/pkce" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + + "go.pinniped.dev/internal/constable" + "go.pinniped.dev/internal/crud" +) + +const ( + ErrInvalidPKCERequestType = constable.Error("requester must be of type fosite.Request") + + pkceStorageVersion = "1" +) + +var _ pkce.PKCERequestStorage = &pkceStorage{} + +type pkceStorage struct { + storage crud.Storage +} + +type session struct { + Request *fosite.Request `json:"request"` + Version string `json:"version"` +} + +func New(secrets corev1client.SecretInterface) pkce.PKCERequestStorage { + return &pkceStorage{storage: crud.New("pkce", secrets)} +} + +// TODO test what happens when we pass nil as the requester. +func (a *pkceStorage) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error { + request, err := validateAndExtractAuthorizeRequest(requester) + if err != nil { + return err + } + + _, err = a.storage.Create(ctx, signature, &session{Request: request, Version: pkceStorageVersion}) + return err +} + +func (a *pkceStorage) GetPKCERequestSession(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 *pkceStorage) DeletePKCERequestSession(ctx context.Context, signature string) error { + return a.storage.Delete(ctx, signature) +} + +func (a *pkceStorage) getSession(ctx context.Context, signature string) (*session, string, error) { + session := newValidEmptyPKCESession() + rv, err := a.storage.Get(ctx, signature, session) + + // TODO we do want this + // if errors.IsNotFound(err) { + // return nil, "", fosite.ErrNotFound.WithCause(err).WithDebug(err.Error()) + // } + + if err != nil { + return nil, "", fmt.Errorf("failed to get authorization code session for %s: %w", signature, err) + } + + // TODO we probably want this + // if version := session.Version; version != pkceStorageVersion { + // return nil, "", fmt.Errorf("%w: authorization code session for %s has version %s instead of %s", + // ErrInvalidAuthorizeRequestVersion, signature, version, pkceStorageVersion) + // } + + // TODO maybe we want this. it would only apply when a human has edited the secret. + // if session.Request == nil { + // return nil, "", fmt.Errorf("malformed authorization code session for %s: %w", signature, ErrInvalidAuthorizeRequestData) + // } + + return session, rv, nil +} + +func newValidEmptyPKCESession() *session { + return &session{ + Request: &fosite.Request{ + Client: &fosite.DefaultOpenIDConnectClient{}, + Session: &openid.DefaultSession{}, + }, + } +} + +func validateAndExtractAuthorizeRequest(requester fosite.Requester) (*fosite.Request, error) { + request, ok1 := requester.(*fosite.Request) + if !ok1 { + return nil, ErrInvalidPKCERequestType + } + _, ok2 := request.Client.(*fosite.DefaultOpenIDConnectClient) + _, ok3 := request.Session.(*openid.DefaultSession) + + valid := ok2 && ok3 + if !valid { + return nil, ErrInvalidPKCERequestType + } + + return request, nil +} diff --git a/internal/fositestorage/pkce/pkce_test.go b/internal/fositestorage/pkce/pkce_test.go new file mode 100644 index 00000000..82124a9d --- /dev/null +++ b/internal/fositestorage/pkce/pkce_test.go @@ -0,0 +1,100 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package pkce + +import ( + "context" + "net/url" + "testing" + "time" + + "github.com/ory/fosite" + "github.com/ory/fosite/handler/openid" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" + coretesting "k8s.io/client-go/testing" +) + +func TestPKCEStorage(t *testing.T) { + ctx := context.Background() + secretsGVR := schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "secrets", + } + + const namespace = "test-ns" + + wantActions := []coretesting.Action{ + coretesting.NewCreateAction(secretsGVR, namespace, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pinniped-storage-pkce-pwu5zs7lekbhnln2w4", + ResourceVersion: "", + Labels: map[string]string{ + "storage.pinniped.dev": "pkce", + }, + }, + Data: map[string][]byte{ + "pinniped-storage-data": []byte(`{"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":{"Claims":null,"Headers":null,"ExpiresAt":null,"Username":"snorlax","Subject":"panda"},"requestedAudience":null,"grantedAudience":null},"version":"1"}`), + "pinniped-storage-version": []byte("1"), + }, + Type: "storage.pinniped.dev/pkce", + }), + coretesting.NewGetAction(secretsGVR, namespace, "pinniped-storage-pkce-pwu5zs7lekbhnln2w4"), + coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-pkce-pwu5zs7lekbhnln2w4"), + } + + client := fake.NewSimpleClientset() + secrets := client.CoreV1().Secrets(namespace) + storage := New(secrets) + + 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: "", + }, + RequestedScope: nil, + GrantedScope: nil, + Form: url.Values{"key": []string{"val"}}, + Session: &openid.DefaultSession{ + Claims: nil, + Headers: nil, + ExpiresAt: nil, + Username: "snorlax", + Subject: "panda", + }, + RequestedAudience: nil, + GrantedAudience: nil, + } + err := storage.CreatePKCERequestSession(ctx, "fancy-signature", request) + require.NoError(t, err) + + newRequest, err := storage.GetPKCERequestSession(ctx, "fancy-signature", nil) + require.NoError(t, err) + require.Equal(t, request, newRequest) + + err = storage.DeletePKCERequestSession(ctx, "fancy-signature") + require.NoError(t, err) + + require.Equal(t, wantActions, client.Actions()) +} diff --git a/internal/oidc/callback/callback_handler_test.go b/internal/oidc/callback/callback_handler_test.go index 23656da2..6c12394a 100644 --- a/internal/oidc/callback/callback_handler_test.go +++ b/internal/oidc/callback/callback_handler_test.go @@ -17,8 +17,11 @@ import ( "github.com/gorilla/securecookie" "github.com/ory/fosite" "github.com/ory/fosite/handler/openid" - "github.com/ory/fosite/storage" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" + kubetesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/oidctestutil" @@ -419,14 +422,12 @@ func TestCallbackEndpoint(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { - // Configure fosite the same way that the production code would, except use in-memory storage. + client := fake.NewSimpleClientset() + secrets := client.CoreV1().Secrets("some-namespace") + + // Configure fosite the same way that the production code would. // Inject this into our test subject at the last second so we get a fresh storage for every test. - oauthStore := &storage.MemoryStore{ - Clients: map[string]fosite.Client{oidc.PinnipedCLIOIDCClient().ID: oidc.PinnipedCLIOIDCClient()}, - AuthorizeCodes: map[string]storage.StoreAuthorizeCode{}, - PKCES: map[string]fosite.Requester{}, - IDSessions: map[string]fosite.Requester{}, - } + oauthStore := oidc.NewKubeStorage(secrets) hmacSecret := []byte("some secret - must have at least 32 bytes") require.GreaterOrEqual(t, len(hmacSecret), 32, "fosite requires that hmac secrets have at least 32 bytes") oauthHelper := oidc.FositeOauth2Helper(oauthStore, downstreamIssuer, hmacSecret) @@ -471,6 +472,21 @@ func TestCallbackEndpoint(t *testing.T) { authcodeDataAndSignature := strings.Split(capturedAuthCode, ".") require.Len(t, authcodeDataAndSignature, 2) + // Several Secrets should have been created + expectedNumberOfCreatedSecrets := 2 + if test.wantGrantedOpenidScope { + expectedNumberOfCreatedSecrets++ + } + require.Len(t, client.Actions(), expectedNumberOfCreatedSecrets) + + // One authcode should have been stored. + actualAction := client.Actions()[0].(kubetesting.CreateActionImpl) + require.Equal(t, "create", actualAction.GetVerb()) + require.Equal(t, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, actualAction.GetResource()) + actualSecret := actualAction.GetObject().(*corev1.Secret) + require.True(t, strings.HasPrefix(actualSecret.Name, "pinniped-storage-authcode-")) + require.Empty(t, actualSecret.Namespace) // because the secrets client is already scoped to a namespace + storedRequestFromAuthcode, storedSessionFromAuthcode := validateAuthcodeStorage( t, oauthStore, @@ -481,6 +497,14 @@ func TestCallbackEndpoint(t *testing.T) { test.wantDownstreamRequestedScopes, ) + // One PKCE should have been stored. + actualAction = client.Actions()[1].(kubetesting.CreateActionImpl) + require.Equal(t, "create", actualAction.GetVerb()) + require.Equal(t, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, actualAction.GetResource()) + actualSecret = actualAction.GetObject().(*corev1.Secret) + require.True(t, strings.HasPrefix(actualSecret.Name, "pinniped-storage-pkce-")) + require.Empty(t, actualSecret.Namespace) // because the secrets client is already scoped to a namespace + validatePKCEStorage( t, oauthStore, @@ -491,15 +515,24 @@ func TestCallbackEndpoint(t *testing.T) { test.wantDownstreamPKCEChallengeMethod, ) - validateIDSessionStorage( - t, - oauthStore, - capturedAuthCode, // IDSession store key is full authcode - storedRequestFromAuthcode, - storedSessionFromAuthcode, - test.wantGrantedOpenidScope, - test.wantDownstreamNonce, - ) + // One IDSession should have been stored, if the downstream actually requested the "openid" scope + if test.wantGrantedOpenidScope { + actualAction = client.Actions()[2].(kubetesting.CreateActionImpl) + require.Equal(t, "create", actualAction.GetVerb()) + require.Equal(t, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, actualAction.GetResource()) + actualSecret = actualAction.GetObject().(*corev1.Secret) + require.True(t, strings.HasPrefix(actualSecret.Name, "pinniped-storage-idsession-")) + require.Empty(t, actualSecret.Namespace) // because the secrets client is already scoped to a namespace + + validateIDSessionStorage( + t, + oauthStore, + capturedAuthCode, // IDSession store key is full authcode + storedRequestFromAuthcode, + storedSessionFromAuthcode, + test.wantDownstreamNonce, + ) + } } }) } @@ -674,7 +707,7 @@ func shallowCopyAndModifyQuery(query url.Values, modifications map[string]string func validateAuthcodeStorage( t *testing.T, - oauthStore *storage.MemoryStore, + oauthStore *oidc.KubeStorage, storeKey string, wantGrantedOpenidScope bool, wantDownstreamIDTokenSubject string, @@ -683,9 +716,6 @@ func validateAuthcodeStorage( ) (*fosite.Request, *openid.DefaultSession) { t.Helper() - // One authcode should have been stored. - require.Len(t, oauthStore.AuthorizeCodes, 1) - // Get the authcode session back from storage so we can require that it was stored correctly. storedAuthorizeRequestFromAuthcode, err := oauthStore.GetAuthorizeCodeSession(context.Background(), storeKey, nil) require.NoError(t, err) @@ -725,7 +755,7 @@ func validateAuthcodeStorage( require.Equal(t, wantDownstreamIDTokenSubject, actualClaims.Subject) if wantDownstreamIDTokenGroups != nil { require.Len(t, actualClaims.Extra, 1) - require.Equal(t, wantDownstreamIDTokenGroups, actualClaims.Extra["groups"]) + require.ElementsMatch(t, wantDownstreamIDTokenGroups, actualClaims.Extra["groups"]) } else { require.Empty(t, actualClaims.Extra) require.NotContains(t, actualClaims.Extra, "groups") @@ -760,7 +790,7 @@ func validateAuthcodeStorage( func validatePKCEStorage( t *testing.T, - oauthStore *storage.MemoryStore, + oauthStore *oidc.KubeStorage, storeKey string, storedRequestFromAuthcode *fosite.Request, storedSessionFromAuthcode *openid.DefaultSession, @@ -768,8 +798,6 @@ func validatePKCEStorage( ) { t.Helper() - // One PKCE should have been stored. - require.Len(t, oauthStore.PKCES, 1) storedAuthorizeRequestFromPKCE, err := oauthStore.GetPKCERequestSession(context.Background(), storeKey, nil) require.NoError(t, err) @@ -787,33 +815,26 @@ func validatePKCEStorage( func validateIDSessionStorage( t *testing.T, - oauthStore *storage.MemoryStore, + oauthStore *oidc.KubeStorage, storeKey string, storedRequestFromAuthcode *fosite.Request, storedSessionFromAuthcode *openid.DefaultSession, - wantGrantedOpenidScope bool, wantDownstreamNonce string, ) { t.Helper() - // One IDSession should have been stored, if the downstream actually requested the "openid" scope.. - if wantGrantedOpenidScope { - require.Len(t, oauthStore.IDSessions, 1) - storedAuthorizeRequestFromIDSession, err := oauthStore.GetOpenIDConnectSession(context.Background(), storeKey, nil) - require.NoError(t, err) + storedAuthorizeRequestFromIDSession, err := oauthStore.GetOpenIDConnectSession(context.Background(), storeKey, nil) + require.NoError(t, err) - // Check that storage returned the expected concrete data types. - storedRequestFromIDSession, storedSessionFromIDSession := castStoredAuthorizeRequest(t, storedAuthorizeRequestFromIDSession) + // Check that storage returned the expected concrete data types. + storedRequestFromIDSession, storedSessionFromIDSession := castStoredAuthorizeRequest(t, storedAuthorizeRequestFromIDSession) - // The stored IDSession request should be the same as the stored authcode request. - require.Equal(t, storedRequestFromAuthcode.ID, storedRequestFromIDSession.ID) - require.Equal(t, storedSessionFromAuthcode, storedSessionFromIDSession) + // The stored IDSession request should be the same as the stored authcode request. + require.Equal(t, storedRequestFromAuthcode.ID, storedRequestFromIDSession.ID) + require.Equal(t, storedSessionFromAuthcode, storedSessionFromIDSession) - // The stored IDSession request should also contain the nonce that the downstream sent us. - require.Equal(t, wantDownstreamNonce, storedRequestFromIDSession.Form.Get("nonce")) - } else { - require.Len(t, oauthStore.IDSessions, 0) - } + // The stored IDSession request should also contain the nonce that the downstream sent us. + require.Equal(t, wantDownstreamNonce, storedRequestFromIDSession.Form.Get("nonce")) } func castStoredAuthorizeRequest(t *testing.T, storedAuthorizeRequest fosite.Requester) (*fosite.Request, *openid.DefaultSession) { diff --git a/internal/oidc/kube_storage.go b/internal/oidc/kube_storage.go new file mode 100644 index 00000000..664e382d --- /dev/null +++ b/internal/oidc/kube_storage.go @@ -0,0 +1,110 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package oidc + +import ( + "context" + "time" + + "github.com/ory/fosite" + "github.com/ory/fosite/handler/oauth2" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + + "go.pinniped.dev/internal/constable" + "go.pinniped.dev/internal/fositestorage/authorizationcode" +) + +const errKubeStorageNotImplemented = constable.Error("KubeStorage does not implement this method. It should not have been called.") + +type KubeStorage struct { + authorizationCodeStorage oauth2.AuthorizeCodeStorage +} + +func NewKubeStorage(secrets corev1client.SecretInterface) *KubeStorage { + return &KubeStorage{authorizationCodeStorage: authorizationcode.New(secrets)} +} + +func (KubeStorage) RevokeRefreshToken(_ context.Context, _ string) error { + return errKubeStorageNotImplemented +} + +func (KubeStorage) RevokeAccessToken(_ context.Context, _ string) error { + return errKubeStorageNotImplemented +} + +func (KubeStorage) CreateRefreshTokenSession(_ context.Context, _ string, _ fosite.Requester) (err error) { + return nil +} + +func (KubeStorage) GetRefreshTokenSession(_ context.Context, _ string, _ fosite.Session) (request fosite.Requester, err error) { + return nil, errKubeStorageNotImplemented +} + +func (KubeStorage) DeleteRefreshTokenSession(_ context.Context, _ string) (err error) { + return errKubeStorageNotImplemented +} + +func (KubeStorage) CreateAccessTokenSession(_ context.Context, _ string, _ fosite.Requester) (err error) { + return nil +} + +func (KubeStorage) GetAccessTokenSession(_ context.Context, _ string, _ fosite.Session) (request fosite.Requester, err error) { + return nil, errKubeStorageNotImplemented +} + +func (KubeStorage) DeleteAccessTokenSession(_ context.Context, _ string) (err error) { + return errKubeStorageNotImplemented +} + +func (KubeStorage) CreateOpenIDConnectSession(_ context.Context, _ string, _ fosite.Requester) error { + return nil +} + +func (KubeStorage) GetOpenIDConnectSession(_ context.Context, _ string, _ fosite.Requester) (fosite.Requester, error) { + return nil, errKubeStorageNotImplemented +} + +func (KubeStorage) DeleteOpenIDConnectSession(_ context.Context, _ string) error { + return errKubeStorageNotImplemented +} + +func (KubeStorage) GetPKCERequestSession(_ context.Context, _ string, _ fosite.Session) (fosite.Requester, error) { + return nil, errKubeStorageNotImplemented +} + +func (KubeStorage) CreatePKCERequestSession(_ context.Context, _ string, _ fosite.Requester) error { + return nil +} + +func (KubeStorage) DeletePKCERequestSession(_ context.Context, _ string) error { + return errKubeStorageNotImplemented +} + +func (k KubeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, r fosite.Requester) (err error) { + return k.authorizationCodeStorage.CreateAuthorizeCodeSession(ctx, signature, r) +} + +func (k KubeStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, s fosite.Session) (request fosite.Requester, err error) { + return k.authorizationCodeStorage.GetAuthorizeCodeSession(ctx, signature, s) +} + +func (k KubeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) (err error) { + return k.authorizationCodeStorage.InvalidateAuthorizeCodeSession(ctx, signature) +} + +func (KubeStorage) GetClient(_ context.Context, id string) (fosite.Client, error) { + client := PinnipedCLIOIDCClient() + if client.ID == id { + return client, nil + } + return nil, fosite.ErrNotFound +} + +func (KubeStorage) ClientAssertionJWTValid(_ context.Context, _ string) error { + return errKubeStorageNotImplemented +} + +func (KubeStorage) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error { + return errKubeStorageNotImplemented +} diff --git a/internal/oidc/nullstorage.go b/internal/oidc/nullstorage.go index 3767f889..3dcd7a06 100644 --- a/internal/oidc/nullstorage.go +++ b/internal/oidc/nullstorage.go @@ -12,16 +12,16 @@ import ( "go.pinniped.dev/internal/constable" ) -const errNotImplemented = constable.Error("NullStorage does not implement this method. It should not have been called.") +const errNullStorageNotImplemented = constable.Error("NullStorage does not implement this method. It should not have been called.") type NullStorage struct{} func (NullStorage) RevokeRefreshToken(_ context.Context, _ string) error { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) RevokeAccessToken(_ context.Context, _ string) error { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) CreateRefreshTokenSession(_ context.Context, _ string, _ fosite.Requester) (err error) { @@ -29,11 +29,11 @@ func (NullStorage) CreateRefreshTokenSession(_ context.Context, _ string, _ fosi } func (NullStorage) GetRefreshTokenSession(_ context.Context, _ string, _ fosite.Session) (request fosite.Requester, err error) { - return nil, errNotImplemented + return nil, errNullStorageNotImplemented } func (NullStorage) DeleteRefreshTokenSession(_ context.Context, _ string) (err error) { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) CreateAccessTokenSession(_ context.Context, _ string, _ fosite.Requester) (err error) { @@ -41,11 +41,11 @@ func (NullStorage) CreateAccessTokenSession(_ context.Context, _ string, _ fosit } func (NullStorage) GetAccessTokenSession(_ context.Context, _ string, _ fosite.Session) (request fosite.Requester, err error) { - return nil, errNotImplemented + return nil, errNullStorageNotImplemented } func (NullStorage) DeleteAccessTokenSession(_ context.Context, _ string) (err error) { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) CreateOpenIDConnectSession(_ context.Context, _ string, _ fosite.Requester) error { @@ -53,15 +53,15 @@ func (NullStorage) CreateOpenIDConnectSession(_ context.Context, _ string, _ fos } func (NullStorage) GetOpenIDConnectSession(_ context.Context, _ string, _ fosite.Requester) (fosite.Requester, error) { - return nil, errNotImplemented + return nil, errNullStorageNotImplemented } func (NullStorage) DeleteOpenIDConnectSession(_ context.Context, _ string) error { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) GetPKCERequestSession(_ context.Context, _ string, _ fosite.Session) (fosite.Requester, error) { - return nil, errNotImplemented + return nil, errNullStorageNotImplemented } func (NullStorage) CreatePKCERequestSession(_ context.Context, _ string, _ fosite.Requester) error { @@ -69,7 +69,7 @@ func (NullStorage) CreatePKCERequestSession(_ context.Context, _ string, _ fosit } func (NullStorage) DeletePKCERequestSession(_ context.Context, _ string) error { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) CreateAuthorizeCodeSession(_ context.Context, _ string, _ fosite.Requester) (err error) { @@ -77,11 +77,11 @@ func (NullStorage) CreateAuthorizeCodeSession(_ context.Context, _ string, _ fos } func (NullStorage) GetAuthorizeCodeSession(_ context.Context, _ string, _ fosite.Session) (request fosite.Requester, err error) { - return nil, errNotImplemented + return nil, errNullStorageNotImplemented } func (NullStorage) InvalidateAuthorizeCodeSession(_ context.Context, _ string) (err error) { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) GetClient(_ context.Context, id string) (fosite.Client, error) { @@ -93,9 +93,9 @@ func (NullStorage) GetClient(_ context.Context, id string) (fosite.Client, error } func (NullStorage) ClientAssertionJWTValid(_ context.Context, _ string) error { - return errNotImplemented + return errNullStorageNotImplemented } func (NullStorage) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error { - return errNotImplemented + return errNullStorageNotImplemented } diff --git a/test/integration/storage_test.go b/test/integration/storage_test.go index e2f3bdf2..501099fe 100644 --- a/test/integration/storage_test.go +++ b/test/integration/storage_test.go @@ -17,7 +17,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "go.pinniped.dev/internal/fosite/authorizationcode" + "go.pinniped.dev/internal/fositestorage/authorizationcode" "go.pinniped.dev/test/library" ) @@ -29,7 +29,7 @@ func TestAuthorizeCodeStorage(t *testing.T) { // randomly generated HMAC authorization code (see below) code = "TQ72B8YjdEOZyxridYbTLE-pzoK4hpdkZxym5j4EmSc.TKRTgQG41IBQ16FDKTthRdhXfLlNaErcMd9Fy47uXAw" // name of the secret that will be created in Kube - name = "pinniped-storage-authorization-codes-jssfhaibxdkiaugxufbsso3bixmfo7fzjvuevxbr35c4xdxolqga" + name = "pinniped-storage-authcode-jssfhaibxdkiaugxufbsso3bixmfo7fzjvuevxbr35c4xdxolqga" ) hmac := compose.NewOAuth2HMACStrategy(&compose.Config{}, []byte("super-secret-32-byte-for-testing"), nil)