Static validation for OIDC clients

The following validation is enforced:

1. Names must start with client.oauth.pinniped.dev-
2. Redirect URIs must start with https://
   or http://127.0.0.1
   or http://::1
3. All spec lists must not have duplicates

Added an integration test to assert all static validations.

Signed-off-by: Monis Khan <mok@vmware.com>
This commit is contained in:
Monis Khan 2022-06-13 20:06:47 -04:00
parent c77bee67c1
commit 59d67322d3
No known key found for this signature in database
GPG Key ID: 52C90ADA01B269B8
39 changed files with 602 additions and 55 deletions

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -49,3 +49,15 @@ metadata:
name: #@ pinnipedDevAPIGroupWithPrefix("oidcclients.config.supervisor") name: #@ pinnipedDevAPIGroupWithPrefix("oidcclients.config.supervisor")
spec: spec:
group: #@ pinnipedDevAPIGroupWithPrefix("config.supervisor") group: #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
versions:
#@overlay/match by=overlay.all, expects="1+"
- schema:
openAPIV3Schema:
#@overlay/match by=overlay.subset({"metadata":{"type":"object"}}), expects=1
properties:
metadata:
#@overlay/match missing_ok=True
properties:
name:
pattern: ^client\.oauth\.pinniped\.dev-
type: string

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -578,7 +578,7 @@ OIDCClientSpec is a struct that describes an OIDC Client.
[cols="25a,75a", options="header"] [cols="25a,75a", options="header"]
|=== |===
| Field | Description | Field | Description
| *`allowedRedirectURIs`* __string array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be https, unless it is a loopback. | *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client. | *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience. Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client. | *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -61,15 +61,20 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedRedirectURIs: allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be https, unless it client. Any other uris will be rejected. Must be a URI with the
is a loopback. https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items: items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
allowedScopes: allowedScopes:
description: "allowedScopes is a list of the allowed scopes param description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client. values that should be accepted during OIDC flows with this client.
@ -102,6 +107,7 @@ spec:
type: string type: string
minItems: 1 minItems: 1
type: array type: array
x-kubernetes-list-type: set
required: required:
- allowedGrantTypes - allowedGrantTypes
- allowedRedirectURIs - allowedRedirectURIs

View File

@ -7,6 +7,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange" // +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string type GrantType string
@ -17,9 +20,11 @@ type Scope string
type OIDCClientSpec struct { type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this // allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected. // client. Any other uris will be rejected.
// Must be https, unless it is a loopback. // Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []string `json:"allowedRedirectURIs"` AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this // allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client. // client.
@ -32,6 +37,7 @@ type OIDCClientSpec struct {
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, // - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user. // which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience. // This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"` AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
@ -51,6 +57,7 @@ type OIDCClientSpec struct {
// - groups: The client is allowed to request that ID tokens contain the user's group membership, // - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor. // if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups. // Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"` AllowedScopes []Scope `json:"allowedScopes"`
} }

View File

@ -217,7 +217,7 @@ func (in *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in *out = *in
if in.AllowedRedirectURIs != nil { if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]string, len(*in)) *out = make([]RedirectURI, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedGrantTypes != nil { if in.AllowedGrantTypes != nil {

View File

@ -229,6 +229,9 @@ func FositeOauth2Helper(
// Use the fosite default to make it more likely that off the shelf OIDC clients can work with the supervisor. // Use the fosite default to make it more likely that off the shelf OIDC clients can work with the supervisor.
MinParameterEntropy: fosite.MinParameterEntropy, MinParameterEntropy: fosite.MinParameterEntropy,
// do not allow custom scheme redirects, only https and http (on loopback)
RedirectSecureChecker: fosite.IsRedirectURISecureStrict,
} }
provider := compose.Compose( provider := compose.Compose(

View File

@ -0,0 +1,408 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration
import (
"context"
"fmt"
"sort"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
supervisorconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
"go.pinniped.dev/test/testlib"
)
func TestOIDCClientStaticValidation_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)
groupFix := strings.NewReplacer(".supervisor.pinniped.dev", ".supervisor."+env.APIGroupSuffix)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)
namespaceClient := testlib.NewKubernetesClientset(t).CoreV1().Namespaces()
ns, err := namespaceClient.Create(ctx, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-oidc-client-",
},
}, metav1.CreateOptions{})
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, namespaceClient.Delete(ctx, ns.Name, metav1.DeleteOptions{}))
})
oidcClients := testlib.NewSupervisorClientset(t).ConfigV1alpha1().OIDCClients(ns.Name)
tests := []struct {
name string
client *supervisorconfigv1alpha1.OIDCClient
fixWant func(t *testing.T, err error, want string) string
wantErr string
}{
{
name: "bad name",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "panda",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"https://a",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "panda" is invalid: metadata.name: Invalid value: "panda": metadata.name in body should match '^client\.oauth\.pinniped\.dev-'`,
},
{
name: "bad name but close",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client0oauth1pinniped2dev-regex",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"https://a",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client0oauth1pinniped2dev-regex" is invalid: metadata.name: Invalid value: "client0oauth1pinniped2dev-regex": metadata.name in body should match '^client\.oauth\.pinniped\.dev-'`,
},
{
name: "bad generate name",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "snorlax-",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
fixWant: func(t *testing.T, err error, want string) string {
require.Error(t, err)
gotErr := err.Error()
errPrefix := groupFix.Replace(`OIDCClient.config.supervisor.pinniped.dev "snorlax-`)
require.True(t, strings.HasPrefix(gotErr, errPrefix))
gotErr = strings.TrimPrefix(gotErr, errPrefix)
end := strings.Index(gotErr, `"`)
require.Equal(t, end, 5)
gotErr = gotErr[:end]
return strings.Replace(want, "RAND", gotErr, 2)
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "snorlax-RAND" is invalid: metadata.name: Invalid value: "snorlax-RAND": metadata.name in body should match '^client\.oauth\.pinniped\.dev-'`,
},
{
name: "bad redirect uri",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-hello",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
"oob",
"https://a",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-hello" is invalid: spec.allowedRedirectURIs[1]: Invalid value: "oob": spec.allowedRedirectURIs[1] in body should match '^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/'`,
},
{
name: "bad grant type",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-sky",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
"authorization_code",
"bird",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-sky" is invalid: spec.allowedGrantTypes[2]: Unsupported value: "bird": supported values: "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"`,
},
{
name: "bad scope",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-blue",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"*",
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-blue" is invalid: spec.allowedScopes[0]: Unsupported value: "*": supported values: "openid", "offline_access", "username", "groups", "pinniped:request-audience"`,
},
{
name: "empty unset all",
client: &supervisorconfigv1alpha1.OIDCClient{},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "" is invalid: [metadata.name: Required value: name or generateName is required, spec.allowedGrantTypes: Required value, spec.allowedRedirectURIs: Required value, spec.allowedScopes: Required value]`,
},
{
name: "empty uris",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-green-1",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-green-1" is invalid: spec.allowedRedirectURIs: Invalid value: 0: spec.allowedRedirectURIs in body should have at least 1 items`,
},
{
name: "empty grants",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-green-2",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-green-2" is invalid: spec.allowedGrantTypes: Invalid value: 0: spec.allowedGrantTypes in body should have at least 1 items`,
},
{
name: "empty scopes",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-green-3",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-green-3" is invalid: spec.allowedScopes: Invalid value: 0: spec.allowedScopes in body should have at least 1 items`,
},
{
name: "duplicate uris",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-red-1",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-red-1" is invalid: spec.allowedRedirectURIs[1]: Duplicate value: "http://127.0.0.1/callback"`,
},
{
name: "duplicate grants",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-red-2",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-red-2" is invalid: spec.allowedGrantTypes[1]: Duplicate value: "refresh_token"`,
},
{
name: "duplicate scopes",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-red-3",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"http://127.0.0.1/callback",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"refresh_token",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"username",
"username",
},
},
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-red-3" is invalid: spec.allowedScopes[1]: Duplicate value: "username"`,
},
{
name: "bad everything",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "zone",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"of",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"the",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"enders",
},
},
},
fixWant: func(t *testing.T, err error, want string) string {
// sort the error causes and use that to rebuild a sorted error message
statusErr := &errors.StatusError{}
require.ErrorAs(t, err, &statusErr)
require.Len(t, statusErr.ErrStatus.Details.Causes, 4)
out := make([]string, 0, len(statusErr.ErrStatus.Details.Causes))
for _, cause := range statusErr.ErrStatus.Details.Causes {
cause := cause
out = append(out, fmt.Sprintf("%s: %s", cause.Field, cause.Message))
}
sort.Strings(out)
errPrefix := groupFix.Replace(`OIDCClient.config.supervisor.pinniped.dev "zone" is invalid: [`)
require.True(t, strings.HasPrefix(err.Error(), errPrefix))
require.Equal(t, err.Error(), statusErr.ErrStatus.Message)
statusErr.ErrStatus.Message = errPrefix + strings.Join(out, ", ") + "]"
return want // leave the wanted error unchanged
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "zone" is invalid: [metadata.name: Invalid value: "zone": metadata.name in body should match '^client\.oauth\.pinniped\.dev-', spec.allowedGrantTypes[0]: Unsupported value: "the": supported values: "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange", spec.allowedRedirectURIs[0]: Invalid value: "of": spec.allowedRedirectURIs[0] in body should match '^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/', spec.allowedScopes[0]: Unsupported value: "enders": supported values: "openid", "offline_access", "username", "groups", "pinniped:request-audience"]`,
},
{
name: "everything valid",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-lava",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"https://example.com",
"http://127.0.0.1/yoyo",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:token-exchange",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"openid",
"offline_access",
"username",
"groups",
"pinniped:request-audience",
},
},
},
wantErr: "",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
client, err := oidcClients.Create(ctx, tt.client, metav1.CreateOptions{})
want := tt.wantErr
if len(want) == 0 {
require.NoError(t, err)
// unset server generated fields
client.Namespace = ""
client.UID = ""
client.ResourceVersion = ""
client.ManagedFields = nil
client.CreationTimestamp = metav1.Time{}
client.Generation = 0
require.Equal(t, tt.client, client)
return
}
if tt.fixWant != nil {
want = tt.fixWant(t, err, want)
}
want = groupFix.Replace(want)
require.EqualError(t, err, want)
})
}
}