From a7a2caa6069ce73b1781dd57e4b846874dd1c357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20B=C5=82aszczyk?= Date: Wed, 8 Dec 2021 09:25:34 +0100 Subject: [PATCH] feat: support all variations of responseTypes (#104) --- Dockerfile-kubebuilder | 17 +++++ Makefile | 4 +- api/v1alpha1/oauth2client_types.go | 2 +- api/v1alpha1/oauth2client_types_test.go | 37 +++++++--- .../crd/bases/hydra.ory.sh_oauth2clients.yaml | 71 +++++++------------ .../samples/hydra_v1alpha1_oauth2client.yaml | 4 ++ 6 files changed, 74 insertions(+), 61 deletions(-) create mode 100644 Dockerfile-kubebuilder diff --git a/Dockerfile-kubebuilder b/Dockerfile-kubebuilder new file mode 100644 index 0000000..0805886 --- /dev/null +++ b/Dockerfile-kubebuilder @@ -0,0 +1,17 @@ +FROM golang:1.16 as builder +WORKDIR /go/src/app +RUN os=$(go env GOOS) &&\ + arch=$(go env GOARCH) &&\ + curl -sL https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.2/kubebuilder_2.3.2_${os}_${arch}.tar.gz | tar -xz -C /tmp/ &&\ + mv /tmp/kubebuilder_2.3.2_${os}_${arch} /usr/local/kubebuilder &&\ + export PATH=$PATH:/usr/local/kubebuilder/bin +COPY . . +RUN make test &&\ + make manager + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:latest +COPY --from=builder /go/src/app/manager . +USER 1000 +ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index ec59abf..3f0627f 100644 --- a/Makefile +++ b/Makefile @@ -34,12 +34,12 @@ test: generate fmt vet manifests # Start KIND pseudo-cluster .PHONY: kind-start kind-start: - GO111MODULE=on go get "sigs.k8s.io/kind@v0.11.1" && kind create cluster + kind create cluster # Stop KIND pseudo-cluster .PHONY: kind-stop kind-stop: - GO111MODULE=on go get "sigs.k8s.io/kind@v0.11.1" && kind delete cluster + kind delete cluster # Deploy on KIND # Ensures the controller image is built, deploys the image to KIND cluster along with necessary configuration diff --git a/api/v1alpha1/oauth2client_types.go b/api/v1alpha1/oauth2client_types.go index d06671f..8832620 100644 --- a/api/v1alpha1/oauth2client_types.go +++ b/api/v1alpha1/oauth2client_types.go @@ -129,7 +129,7 @@ type OAuth2ClientSpec struct { // GrantType represents an OAuth 2.0 grant type type GrantType string -// +kubebuilder:validation:Enum=id_token;code;token +// +kubebuilder:validation:Enum=id_token;code;token;code token;code id_token;id_token token;code id_token token // ResponseType represents an OAuth 2.0 response type strings type ResponseType string diff --git a/api/v1alpha1/oauth2client_types_test.go b/api/v1alpha1/oauth2client_types_test.go index b8a36cc..dd827b7 100644 --- a/api/v1alpha1/oauth2client_types_test.go +++ b/api/v1alpha1/oauth2client_types_test.go @@ -101,19 +101,19 @@ func TestCreateAPI(t *testing.T) { t.Run("by failing if the requested object doesn't meet CRD requirements", func(t *testing.T) { for desc, modifyClient := range map[string]func(){ - "invalid grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} }, - "invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid"} }, - "invalid scope": func() { created.Spec.Scope = "" }, - "missing secret name": func() { created.Spec.SecretName = "" }, - "invalid redirect URI": func() { created.Spec.RedirectURIs = []RedirectURI{"invalid"} }, - "invalid logout redirect URI": func() { created.Spec.PostLogoutRedirectURIs = []RedirectURI{"invalid"} }, - "invalid hydra url": func() { created.Spec.HydraAdmin.URL = "invalid" }, - "invalid hydra port high": func() { created.Spec.HydraAdmin.Port = 65536 }, - "invalid hydra endpoint": func() { created.Spec.HydraAdmin.Endpoint = "invalid" }, - "invalid hydra forwarded proto": func() { created.Spec.HydraAdmin.Endpoint = "invalid" }, + "invalid grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} }, + "invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid", "code"} }, + "invalid composite response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid code", "code id_token"} }, + "invalid scope": func() { created.Spec.Scope = "" }, + "missing secret name": func() { created.Spec.SecretName = "" }, + "invalid redirect URI": func() { created.Spec.RedirectURIs = []RedirectURI{"invalid"} }, + "invalid logout redirect URI": func() { created.Spec.PostLogoutRedirectURIs = []RedirectURI{"invalid"} }, + "invalid hydra url": func() { created.Spec.HydraAdmin.URL = "invalid" }, + "invalid hydra port high": func() { created.Spec.HydraAdmin.Port = 65536 }, + "invalid hydra endpoint": func() { created.Spec.HydraAdmin.Endpoint = "invalid" }, + "invalid hydra forwarded proto": func() { created.Spec.HydraAdmin.Endpoint = "invalid" }, } { t.Run(fmt.Sprintf("case=%s", desc), func(t *testing.T) { - resetTestClient() modifyClient() createErr = k8sClient.Create(context.TODO(), created) @@ -121,6 +121,21 @@ func TestCreateAPI(t *testing.T) { }) } }) + + t.Run("by creating an object if it passes validation", func(t *testing.T) { + for desc, modifyClient := range map[string]func(){ + "single response type": func() { created.Spec.ResponseTypes = []ResponseType{"token", "id_token", "code"} }, + "double response type": func() { created.Spec.ResponseTypes = []ResponseType{"id_token token", "code id_token", "code token"} }, + "triple response type": func() { created.Spec.ResponseTypes = []ResponseType{"code id_token token"} }, + } { + t.Run(fmt.Sprintf("case=%s", desc), func(t *testing.T) { + resetTestClient() + modifyClient() + require.NoError(t, k8sClient.Create(context.TODO(), created)) + require.NoError(t, k8sClient.Delete(context.TODO(), created)) + }) + } + }) }) } diff --git a/config/crd/bases/hydra.ory.sh_oauth2clients.yaml b/config/crd/bases/hydra.ory.sh_oauth2clients.yaml index 316fd28..6e90f69 100644 --- a/config/crd/bases/hydra.ory.sh_oauth2clients.yaml +++ b/config/crd/bases/hydra.ory.sh_oauth2clients.yaml @@ -22,14 +22,10 @@ spec: description: OAuth2Client is the Schema for the oauth2clients API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -44,18 +40,15 @@ spec: type: string type: array audience: - description: Audience is a whitelist defining the audiences this client - is allowed to request tokens for + description: Audience is a whitelist defining the audiences this client is allowed to request tokens for items: type: string type: array clientName: - description: ClientName is the human-readable string name of the client - to be presented to the end-user during authorization. + description: ClientName is the human-readable string name of the client to be presented to the end-user during authorization. type: string grantTypes: - description: GrantTypes is an array of grant types the client is allowed - to use. + description: GrantTypes is an array of grant types the client is allowed to use. items: description: GrantType represents an OAuth 2.0 grant type enum: @@ -68,31 +61,22 @@ spec: minItems: 1 type: array hydraAdmin: - description: HydraAdmin is the optional configuration to use for managing - this client + description: HydraAdmin is the optional configuration to use for managing this client properties: endpoint: - description: Endpoint is the endpoint for the hydra instance on - which to set up the client. This value will override the value - provided to `--endpoint` (defaults to `"/clients"` in the application) + description: Endpoint is the endpoint for the hydra instance on which to set up the client. This value will override the value provided to `--endpoint` (defaults to `"/clients"` in the application) pattern: (^$|^/.*) type: string forwardedProto: - description: ForwardedProto overrides the `--forwarded-proto` - flag. The value "off" will force this to be off even if `--forwarded-proto` - is specified + description: ForwardedProto overrides the `--forwarded-proto` flag. The value "off" will force this to be off even if `--forwarded-proto` is specified pattern: (^$|https?|off) type: string port: - description: Port is the port for the hydra instance on which - to set up the client. This value will override the value provided - to `--hydra-port` + description: Port is the port for the hydra instance on which to set up the client. This value will override the value provided to `--hydra-port` maximum: 65535 type: integer url: - description: URL is the URL for the hydra instance on which to - set up the client. This value will override the value provided - to `--hydra-url` + description: URL is the URL for the hydra instance on which to set up the client. This value will override the value provided to `--hydra-url` maxLength: 64 pattern: (^$|^https?://.*) type: string @@ -103,44 +87,41 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true postLogoutRedirectUris: - description: PostLogoutRedirectURIs is an array of the post logout - redirect URIs allowed for the application + description: PostLogoutRedirectURIs is an array of the post logout redirect URIs allowed for the application items: description: RedirectURI represents a redirect URI for the client pattern: \w+:/?/?[^\s]+ type: string type: array redirectUris: - description: RedirectURIs is an array of the redirect URIs allowed - for the application + description: RedirectURIs is an array of the redirect URIs allowed for the application items: description: RedirectURI represents a redirect URI for the client pattern: \w+:/?/?[^\s]+ type: string type: array responseTypes: - description: ResponseTypes is an array of the OAuth 2.0 response type - strings that the client can use at the authorization endpoint. + description: ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint. items: - description: ResponseType represents an OAuth 2.0 response type - strings + description: ResponseType represents an OAuth 2.0 response type strings enum: - id_token - code - token + - code token + - code id_token + - id_token token + - code id_token token type: string maxItems: 3 minItems: 1 type: array scope: - description: Scope is a string containing a space-separated list of - scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) - that the client can use when requesting access tokens. + description: Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. pattern: ([a-zA-Z0-9\.\*]+\s?)+ type: string secretName: - description: SecretName points to the K8s secret that contains this - client's ID and password + description: SecretName points to the K8s secret that contains this client's ID and password maxLength: 253 minLength: 1 pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' @@ -157,8 +138,7 @@ spec: - client_secret_post - private_key_jwt - none - description: Indication which authentication method shoud be used - for the token endpoint + description: Indication which authentication method shoud be used for the token endpoint type: string required: - grantTypes @@ -169,17 +149,14 @@ spec: description: OAuth2ClientStatus defines the observed state of OAuth2Client properties: observedGeneration: - description: ObservedGeneration represents the most recent generation - observed by the daemon set controller. + description: ObservedGeneration represents the most recent generation observed by the daemon set controller. format: int64 type: integer reconciliationError: - description: ReconciliationError represents an error that occurred - during the reconciliation process + description: ReconciliationError represents an error that occurred during the reconciliation process properties: description: - description: Description is the description of the reconciliation - error + description: Description is the description of the reconciliation error type: string statusCode: description: Code is the status code of the reconciliation error diff --git a/config/samples/hydra_v1alpha1_oauth2client.yaml b/config/samples/hydra_v1alpha1_oauth2client.yaml index 2c67a26..d3e8878 100644 --- a/config/samples/hydra_v1alpha1_oauth2client.yaml +++ b/config/samples/hydra_v1alpha1_oauth2client.yaml @@ -13,6 +13,10 @@ spec: - id_token - code - token + - code token + - code id_token + - id_token token + - code id_token token scope: "read write" secretName: my-secret-123 # these are optional