diff --git a/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl b/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl index 1bc7399d..36d86de4 100644 --- a/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl +++ b/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/deploy/supervisor/config.supervisor.pinniped.dev_oidcclients.yaml b/deploy/supervisor/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/deploy/supervisor/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/deploy/supervisor/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index 2b29fc45..62ea1f8e 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.17/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.17/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.17/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.17/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.17/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.17/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.17/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.17/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index e2fb5b80..1d705d41 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.18/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.18/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.18/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.18/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.18/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.18/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.18/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.18/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index 337689da..dee1f150 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.19/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.19/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.19/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.19/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.19/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.19/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.19/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.19/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.20/README.adoc b/generated/1.20/README.adoc index 493e4ba2..e70a070d 100644 --- a/generated/1.20/README.adoc +++ b/generated/1.20/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.20/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.20/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.20/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.20/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.20/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.20/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.20/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.20/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.21/README.adoc b/generated/1.21/README.adoc index 59be6db3..3d106f96 100644 --- a/generated/1.21/README.adoc +++ b/generated/1.21/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.21/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.21/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.21/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.21/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.22/README.adoc b/generated/1.22/README.adoc index 7f4ace33..36a03dd1 100644 --- a/generated/1.22/README.adoc +++ b/generated/1.22/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.22/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.22/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.22/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.22/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.23/README.adoc b/generated/1.23/README.adoc index ad7d96a6..ca8875dc 100644 --- a/generated/1.23/README.adoc +++ b/generated/1.23/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.23/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.23/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.23/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.23/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index 9a7ab440..73328c0e 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -724,7 +724,7 @@ OIDCClient describes the configuration of an OIDC client. [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-oidcclientspec"] ==== OIDCClientSpec -OIDCClientSpec is a struct that describes an OIDC Client. +OIDCClientSpec is a struct that describes an OIDCClient. .Appears In: **** @@ -755,8 +755,9 @@ OIDCClientStatus is a struct that describes the actual state of an OIDCClient. [cols="25a,75a", options="header"] |=== | Field | Description -| *`phase`* __OIDCClientPhase__ | Phase summarizes the overall status of the OIDCClient. -| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an OIDCClient's current state. +| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state. +| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. |=== diff --git a/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/generated/1.24/crds/config.supervisor.pinniped.dev_oidcclients.yaml b/generated/1.24/crds/config.supervisor.pinniped.dev_oidcclients.yaml index b5569275..c61e9c45 100644 --- a/generated/1.24/crds/config.supervisor.pinniped.dev_oidcclients.yaml +++ b/generated/1.24/crds/config.supervisor.pinniped.dev_oidcclients.yaml @@ -117,8 +117,8 @@ spec: description: Status of the OIDC client. properties: conditions: - description: Represents the observations of an OIDCClient's current - state. + description: conditions represent the observations of an OIDCClient's + current state. items: description: Condition status of a resource (mirrored from the metav1.Condition type added in Kubernetes 1.19). In a future API version we can @@ -185,12 +185,18 @@ spec: x-kubernetes-list-type: map phase: default: Pending - description: Phase summarizes the overall status of the OIDCClient. + description: phase summarizes the overall status of the OIDCClient. enum: - Pending - Ready - Error type: string + totalClientSecrets: + description: totalClientSecrets is the current number of client secrets + that are detected for this OIDCClient. + type: integer + required: + - totalClientSecrets type: object required: - spec diff --git a/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go index 1bc7399d..36d86de4 100644 --- a/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -27,7 +27,7 @@ type GrantType string // +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience" type Scope string -// OIDCClientSpec is a struct that describes an OIDC Client. +// OIDCClientSpec is a struct that describes an OIDCClient. type OIDCClientSpec struct { // 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. @@ -75,17 +75,20 @@ type OIDCClientSpec struct { // OIDCClientStatus is a struct that describes the actual state of an OIDCClient. type OIDCClientStatus struct { - // Phase summarizes the overall status of the OIDCClient. + // phase summarizes the overall status of the OIDCClient. // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Pending;Ready;Error Phase OIDCClientPhase `json:"phase,omitempty"` - // Represents the observations of an OIDCClient's current state. + // conditions represent the observations of an OIDCClient's current state. // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // totalClientSecrets is the current number of client secrets that are detected for this OIDCClient. + TotalClientSecrets int `json:"totalClientSecrets"` } // OIDCClient describes the configuration of an OIDC client. diff --git a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go index 600f7420..34d82941 100644 --- a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go +++ b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go @@ -6,7 +6,10 @@ package oidcclientwatcher import ( "context" "fmt" + "strings" + "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/crypto/bcrypt" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -29,16 +32,17 @@ const ( allowedGrantTypesValid = "AllowedGrantTypesValid" allowedScopesValid = "AllowedScopesValid" - reasonSuccess = "Success" - reasonMissingRequiredValue = "MissingRequiredValue" - reasonNoClientSecretFound = "NoClientSecretFound" + reasonSuccess = "Success" + reasonMissingRequiredValue = "MissingRequiredValue" + reasonNoClientSecretFound = "NoClientSecretFound" + reasonInvalidClientSecretFound = "InvalidClientSecretFound" authorizationCodeGrantTypeName = "authorization_code" refreshTokenGrantTypeName = "refresh_token" tokenExchangeGrantTypeName = "urn:ietf:params:oauth:grant-type:token-exchange" //nolint:gosec // this is not a credential - openidScopeName = "openid" - offlineAccessScopeName = "offline_access" + openidScopeName = oidc.ScopeOpenID + offlineAccessScopeName = oidc.ScopeOfflineAccess requestAudienceScopeName = "pinniped:request-audience" usernameScopeName = "username" groupsScopeName = "groups" @@ -46,7 +50,10 @@ const ( allowedGrantTypesFieldName = "allowedGrantTypes" allowedScopesFieldName = "allowedScopes" - secretTypeToObserve = "storage.pinniped.dev/oidc-client-secret" //nolint:gosec // this is not a credential + secretTypeToObserve = "storage.pinniped.dev/oidc-client-secret" //nolint:gosec // this is not a credential + oidcClientPrefixToObserve = "client.oauth.pinniped.dev-" //nolint:gosec // this is not a credential + + minimumRequiredBcryptCost = 15 ) type oidcClientWatcherController struct { @@ -81,7 +88,9 @@ func NewOIDCClientWatcherController( // We want to be notified when anything happens to an OIDCClient. withInformer( oidcClientInformer, - pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), + pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool { + return strings.HasPrefix(obj.GetName(), oidcClientPrefixToObserve) + }), controllerlib.InformerOption{}, ), ) @@ -101,6 +110,11 @@ func (c *oidcClientWatcherController) Sync(ctx controllerlib.Context) error { storage := oidcclientsecretstorage.New(nil, nil) for _, oidcClient := range oidcClients { + // Skip the OIDCClients that we are not trying to observe. + if !strings.HasPrefix(oidcClient.Name, oidcClientPrefixToObserve) { + continue + } + correspondingSecretName := storage.GetName(oidcClient.UID) secret, err := c.secretInformer.Lister().Secrets(oidcClient.Namespace).Get(correspondingSecretName) @@ -119,9 +133,9 @@ func (c *oidcClientWatcherController) Sync(ctx controllerlib.Context) error { secret = nil } - conditions := validateOIDCClient(oidcClient, secret) + conditions, totalClientSecrets := validateOIDCClient(oidcClient, secret) - if err := c.updateStatus(ctx.Context, oidcClient, conditions); err != nil { + if err := c.updateStatus(ctx.Context, oidcClient, conditions, totalClientSecrets); err != nil { return fmt.Errorf("cannot update OIDCClient '%s/%s': %w", oidcClient.Namespace, oidcClient.Name, err) } @@ -138,100 +152,94 @@ func (c *oidcClientWatcherController) Sync(ctx controllerlib.Context) error { // validateOIDCClient validates the OIDCClient and its corresponding client secret storage Secret. // When the corresponding client secret storage Secret was not found, pass nil to this function to -// get the validation error for that case. -func validateOIDCClient(oidcClient *v1alpha1.OIDCClient, secret *v1.Secret) []*v1alpha1.Condition { - c := validateSecret(secret, []*v1alpha1.Condition{}) +// get the validation error for that case. It returns a slice of conditions along with the number +// of client secrets found. +func validateOIDCClient(oidcClient *v1alpha1.OIDCClient, secret *v1.Secret) ([]*v1alpha1.Condition, int) { + c, totalClientSecrets := validateSecret(secret, make([]*v1alpha1.Condition, 0, 3)) c = validateAllowedGrantTypes(oidcClient, c) c = validateAllowedScopes(oidcClient, c) - return c + return c, totalClientSecrets } // validateAllowedScopes checks if allowedScopes is valid on the OIDCClient. func validateAllowedScopes(oidcClient *v1alpha1.OIDCClient, conditions []*v1alpha1.Condition) []*v1alpha1.Condition { - switch { - case !allowedScopesContains(oidcClient, openidScopeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedScopesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must always be included in %q", openidScopeName, allowedScopesFieldName), - }) - case allowedGrantTypesContains(oidcClient, refreshTokenGrantTypeName) && !allowedScopesContains(oidcClient, offlineAccessScopeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedScopesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must be included in %q when %q is included in %q", - offlineAccessScopeName, allowedScopesFieldName, refreshTokenGrantTypeName, allowedGrantTypesFieldName), - }) - case allowedScopesContains(oidcClient, requestAudienceScopeName) && - (!allowedScopesContains(oidcClient, usernameScopeName) || !allowedScopesContains(oidcClient, groupsScopeName)): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedScopesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q and %q must be included in %q when %q is included in %q", - usernameScopeName, groupsScopeName, allowedScopesFieldName, requestAudienceScopeName, allowedScopesFieldName), - }) - case allowedGrantTypesContains(oidcClient, tokenExchangeGrantTypeName) && !allowedScopesContains(oidcClient, requestAudienceScopeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedScopesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must be included in %q when %q is included in %q", - requestAudienceScopeName, allowedScopesFieldName, tokenExchangeGrantTypeName, allowedGrantTypesFieldName), - }) - default: + m := make([]string, 0, 4) + + if !allowedScopesContains(oidcClient, openidScopeName) { + m = append(m, fmt.Sprintf("%q must always be included in %q", openidScopeName, allowedScopesFieldName)) + } + if allowedGrantTypesContains(oidcClient, refreshTokenGrantTypeName) && !allowedScopesContains(oidcClient, offlineAccessScopeName) { + m = append(m, fmt.Sprintf("%q must be included in %q when %q is included in %q", + offlineAccessScopeName, allowedScopesFieldName, refreshTokenGrantTypeName, allowedGrantTypesFieldName)) + } + if allowedScopesContains(oidcClient, requestAudienceScopeName) && + (!allowedScopesContains(oidcClient, usernameScopeName) || !allowedScopesContains(oidcClient, groupsScopeName)) { + m = append(m, fmt.Sprintf("%q and %q must be included in %q when %q is included in %q", + usernameScopeName, groupsScopeName, allowedScopesFieldName, requestAudienceScopeName, allowedScopesFieldName)) + } + if allowedGrantTypesContains(oidcClient, tokenExchangeGrantTypeName) && !allowedScopesContains(oidcClient, requestAudienceScopeName) { + m = append(m, fmt.Sprintf("%q must be included in %q when %q is included in %q", + requestAudienceScopeName, allowedScopesFieldName, tokenExchangeGrantTypeName, allowedGrantTypesFieldName)) + } + + if len(m) == 0 { conditions = append(conditions, &v1alpha1.Condition{ Type: allowedScopesValid, Status: v1alpha1.ConditionTrue, Reason: reasonSuccess, Message: fmt.Sprintf("%q is valid", allowedScopesFieldName), }) + } else { + conditions = append(conditions, &v1alpha1.Condition{ + Type: allowedScopesValid, + Status: v1alpha1.ConditionFalse, + Reason: reasonMissingRequiredValue, + Message: strings.Join(m, "; "), + }) } + return conditions } // validateAllowedGrantTypes checks if allowedGrantTypes is valid on the OIDCClient. func validateAllowedGrantTypes(oidcClient *v1alpha1.OIDCClient, conditions []*v1alpha1.Condition) []*v1alpha1.Condition { - switch { - case !allowedGrantTypesContains(oidcClient, authorizationCodeGrantTypeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedGrantTypesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must always be included in %q", - authorizationCodeGrantTypeName, allowedGrantTypesFieldName), - }) - case allowedScopesContains(oidcClient, offlineAccessScopeName) && !allowedGrantTypesContains(oidcClient, refreshTokenGrantTypeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedGrantTypesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must be included in %q when %q is included in %q", - refreshTokenGrantTypeName, allowedGrantTypesFieldName, offlineAccessScopeName, allowedScopesFieldName), - }) - case allowedScopesContains(oidcClient, requestAudienceScopeName) && !allowedGrantTypesContains(oidcClient, tokenExchangeGrantTypeName): - conditions = append(conditions, &v1alpha1.Condition{ - Type: allowedGrantTypesValid, - Status: v1alpha1.ConditionFalse, - Reason: reasonMissingRequiredValue, - Message: fmt.Sprintf("%q must be included in %q when %q is included in %q", - tokenExchangeGrantTypeName, allowedGrantTypesFieldName, requestAudienceScopeName, allowedScopesFieldName), - }) - default: + m := make([]string, 0, 3) + + if !allowedGrantTypesContains(oidcClient, authorizationCodeGrantTypeName) { + m = append(m, fmt.Sprintf("%q must always be included in %q", + authorizationCodeGrantTypeName, allowedGrantTypesFieldName)) + } + if allowedScopesContains(oidcClient, offlineAccessScopeName) && !allowedGrantTypesContains(oidcClient, refreshTokenGrantTypeName) { + m = append(m, fmt.Sprintf("%q must be included in %q when %q is included in %q", + refreshTokenGrantTypeName, allowedGrantTypesFieldName, offlineAccessScopeName, allowedScopesFieldName)) + } + if allowedScopesContains(oidcClient, requestAudienceScopeName) && !allowedGrantTypesContains(oidcClient, tokenExchangeGrantTypeName) { + m = append(m, fmt.Sprintf("%q must be included in %q when %q is included in %q", + tokenExchangeGrantTypeName, allowedGrantTypesFieldName, requestAudienceScopeName, allowedScopesFieldName)) + } + + if len(m) == 0 { conditions = append(conditions, &v1alpha1.Condition{ Type: allowedGrantTypesValid, Status: v1alpha1.ConditionTrue, Reason: reasonSuccess, Message: fmt.Sprintf("%q is valid", allowedGrantTypesFieldName), }) + } else { + conditions = append(conditions, &v1alpha1.Condition{ + Type: allowedGrantTypesValid, + Status: v1alpha1.ConditionFalse, + Reason: reasonMissingRequiredValue, + Message: strings.Join(m, "; "), + }) } + return conditions } // validateSecret checks if the client secret storage Secret is valid and contains at least one client secret. -func validateSecret(secret *v1.Secret, conditions []*v1alpha1.Condition) []*v1alpha1.Condition { +// It returns the updated conditions slice along with the number of client secrets found. +func validateSecret(secret *v1.Secret, conditions []*v1alpha1.Condition) ([]*v1alpha1.Condition, int) { if secret == nil { // Invalid: no storage Secret found. conditions = append(conditions, &v1alpha1.Condition{ @@ -240,7 +248,7 @@ func validateSecret(secret *v1.Secret, conditions []*v1alpha1.Condition) []*v1al Reason: reasonNoClientSecretFound, Message: "no client secret found (no Secret storage found)", }) - return conditions + return conditions, 0 } storedClientSecret, err := oidcclientsecretstorage.ReadFromSecret(secret) @@ -252,7 +260,7 @@ func validateSecret(secret *v1.Secret, conditions []*v1alpha1.Condition) []*v1al Reason: reasonNoClientSecretFound, Message: fmt.Sprintf("error reading client secret storage: %s", err.Error()), }) - return conditions + return conditions, 0 } // Successfully read the stored client secrets, so check if there are any stored in the list. @@ -265,16 +273,42 @@ func validateSecret(secret *v1.Secret, conditions []*v1alpha1.Condition) []*v1al Reason: reasonNoClientSecretFound, Message: "no client secret found (empty list in storage)", }) - } else { - // Valid: has at least one client secret stored for this OIDC client. + return conditions, 0 + } + + // Check each hashed password's format and bcrypt cost. + bcryptErrs := make([]string, 0, storedClientSecretsCount) + for i, p := range storedClientSecret.SecretHashes { + cost, err := bcrypt.Cost([]byte(p)) + if err != nil { + bcryptErrs = append(bcryptErrs, fmt.Sprintf( + "hashed client secret at index %d: %s", + i, err.Error())) + } else if cost < minimumRequiredBcryptCost { + bcryptErrs = append(bcryptErrs, fmt.Sprintf( + "hashed client secret at index %d: bcrypt cost %d is below the required minimum of %d", + i, cost, minimumRequiredBcryptCost)) + } + } + if len(bcryptErrs) > 0 { + // Invalid: some stored client secrets were not valid. conditions = append(conditions, &v1alpha1.Condition{ Type: clientSecretExists, - Status: v1alpha1.ConditionTrue, - Reason: reasonSuccess, - Message: fmt.Sprintf("%d client secret(s) found", storedClientSecretsCount), + Status: v1alpha1.ConditionFalse, + Reason: reasonInvalidClientSecretFound, + Message: strings.Join(bcryptErrs, "; "), }) + return conditions, storedClientSecretsCount } - return conditions + + // Valid: has at least one client secret stored for this OIDC client, and all stored client secrets are valid. + conditions = append(conditions, &v1alpha1.Condition{ + Type: clientSecretExists, + Status: v1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: fmt.Sprintf("%d client secret(s) found", storedClientSecretsCount), + }) + return conditions, storedClientSecretsCount } func allowedGrantTypesContains(haystack *v1alpha1.OIDCClient, needle string) bool { @@ -295,7 +329,12 @@ func allowedScopesContains(haystack *v1alpha1.OIDCClient, needle string) bool { return false } -func (c *oidcClientWatcherController) updateStatus(ctx context.Context, upstream *v1alpha1.OIDCClient, conditions []*v1alpha1.Condition) error { +func (c *oidcClientWatcherController) updateStatus( + ctx context.Context, + upstream *v1alpha1.OIDCClient, + conditions []*v1alpha1.Condition, + totalClientSecrets int, +) error { updated := upstream.DeepCopy() hadErrorCondition := conditionsutil.MergeConfigConditions(conditions, upstream.Generation, &updated.Status.Conditions, plog.New()) @@ -305,6 +344,8 @@ func (c *oidcClientWatcherController) updateStatus(ctx context.Context, upstream updated.Status.Phase = v1alpha1.PhaseError } + updated.Status.TotalClientSecrets = totalClientSecrets + if equality.Semantic.DeepEqual(upstream, updated) { return nil } diff --git a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher_test.go b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher_test.go index 683c92ab..92a0d358 100644 --- a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher_test.go @@ -101,12 +101,32 @@ func TestOIDCClientWatcherControllerFilterOIDCClient(t *testing.T) { wantDelete bool }{ { - name: "anything goes", - oidcClient: configv1alpha1.OIDCClient{}, + name: "name has client.oauth.pinniped.dev- prefix", + oidcClient: configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Name: "client.oauth.pinniped.dev-foo"}, + }, wantAdd: true, wantUpdate: true, wantDelete: true, }, + { + name: "name does not have client.oauth.pinniped.dev- prefix", + oidcClient: configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Name: "something.oauth.pinniped.dev-foo"}, + }, + wantAdd: false, + wantUpdate: false, + wantDelete: false, + }, + { + name: "other names without any particular pinniped.dev prefixes", + oidcClient: configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Name: "something"}, + }, + wantAdd: false, + wantUpdate: false, + wantDelete: false, + }, } for _, test := range tests { tt := test @@ -143,15 +163,18 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { t.Parallel() const ( - testName = "test-name" + testName = "client.oauth.pinniped.dev-test-name" testNamespace = "test-namespace" testUID = "test-uid-123" //nolint:gosec // this is not a credential - testBcryptSecret1 = "$2y$15$Kh7cRj0ScSD5QelE3ZNSl.nF04JDv7zb3SgGN.tSfLIX.4kt3UX7m" // bcrypt of "password1" - + testBcryptSecret1 = "$2y$15$Kh7cRj0ScSD5QelE3ZNSl.nF04JDv7zb3SgGN.tSfLIX.4kt3UX7m" // bcrypt of "password1" at cost 15 //nolint:gosec // this is not a credential - testBcryptSecret2 = "$2y$15$Kh7cRj0ScSD5QelE3ZNSl.nF04JDv7zb3SgGN.tSfLIX.4kt3UX7m" // bcrypt of "password2" + testBcryptSecret2 = "$2y$15$Kh7cRj0ScSD5QelE3ZNSl.nF04JDv7zb3SgGN.tSfLIX.4kt3UX7m" // bcrypt of "password2" at cost 15 + //nolint:gosec // this is not a credential + testInvalidBcryptSecretCostTooLow = "$2y$14$njwk1cItiRy6cb6u9aiJLuhtJG83zM9111t.xU6MxvnqqYbkXxzwy" // bcrypt of "password1" at cost 14 + //nolint:gosec // this is not a credential + testInvalidBcryptSecretInvalidFormat = "$2y$14$njwk1cItiRy6cb6u9aiJLuhtJG83zM9111t.xU6MxvnqqYbkXxz" // not enough characters in hash value ) now := metav1.NewTime(time.Now().UTC()) @@ -190,7 +213,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { } } - sadClientSecretsCondition := func(time metav1.Time, observedGeneration int64, message string) configv1alpha1.Condition { + sadNoClientSecretsCondition := func(time metav1.Time, observedGeneration int64, message string) configv1alpha1.Condition { return configv1alpha1.Condition{ Type: "ClientSecretExists", Status: "False", @@ -201,6 +224,17 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { } } + sadInvalidClientSecretsCondition := func(time metav1.Time, observedGeneration int64, message string) configv1alpha1.Condition { + return configv1alpha1.Condition{ + Type: "ClientSecretExists", + Status: "False", + LastTransitionTime: time, + Reason: "InvalidClientSecretFound", + Message: message, + ObservedGeneration: observedGeneration, + } + } + happyAllowedScopesCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition { return configv1alpha1.Condition{ Type: "AllowedScopesValid", @@ -245,6 +279,12 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { "pinniped-storage-version": []byte("1"), } + secretStringDataWithSomeInvalidClientSecrets := map[string][]byte{ + "pinniped-storage-data": []byte(`{"version":"1","hashes":["` + + testBcryptSecret1 + `","` + testInvalidBcryptSecretCostTooLow + `","` + testInvalidBcryptSecretInvalidFormat + `"]}`), + "pinniped-storage-version": []byte("1"), + } + secretStringDataWithWrongVersion := map[string][]byte{ "pinniped-storage-data": []byte(`{"version":"wrong-version","hashes":[]}`), "pinniped-storage-version": []byte("1"), @@ -275,27 +315,48 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { wantAPIActions: 0, // no updates }, { - name: "successfully validate minimal OIDCClient and one client secret stored", + name: "OIDCClient with wrong prefix is ignored", inputObjects: []runtime.Object{&configv1alpha1.OIDCClient{ - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, - Spec: configv1alpha1.OIDCClientSpec{ - AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code"}, - AllowedScopes: []configv1alpha1.Scope{"openid"}, - }, + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "wrong-prefix-name", Generation: 1234, UID: testUID}, }}, - inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, - wantAPIActions: 1, // one update + wantAPIActions: 0, // no updates wantResultingOIDCClients: []configv1alpha1.OIDCClient{{ - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, - Status: configv1alpha1.OIDCClientStatus{ - Phase: "Ready", - Conditions: []configv1alpha1.Condition{ - happyAllowedGrantTypesCondition(now, 1234), - happyAllowedScopesCondition(now, 1234), - happyClientSecretsCondition(1, now, 1234), + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "wrong-prefix-name", Generation: 1234, UID: testUID}, + }}, + }, + { + name: "successfully validate minimal OIDCClient and one client secret stored (while ignoring client with wrong prefix)", + inputObjects: []runtime.Object{ + &configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "wrong-prefix-name", Generation: 1234, UID: testUID}, + }, + &configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: configv1alpha1.OIDCClientSpec{ + AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code"}, + AllowedScopes: []configv1alpha1.Scope{"openid"}, }, }, - }}, + }, + inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, + wantAPIActions: 1, // one update + wantResultingOIDCClients: []configv1alpha1.OIDCClient{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "wrong-prefix-name", Generation: 1234, UID: testUID}, + }, + { + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: configv1alpha1.OIDCClientStatus{ + Phase: "Ready", + Conditions: []configv1alpha1.Condition{ + happyAllowedGrantTypesCondition(now, 1234), + happyAllowedScopesCondition(now, 1234), + happyClientSecretsCondition(1, now, 1234), + }, + TotalClientSecrets: 1, + }, + }, + }, }, { name: "successfully validate minimal OIDCClient and two client secrets stored", @@ -317,6 +378,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(2, now, 1234), }, + TotalClientSecrets: 2, }, }}, }, @@ -335,6 +397,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(earlier, 1234), happyClientSecretsCondition(1, earlier, 1234), }, + TotalClientSecrets: 1, }, }}, inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, @@ -348,6 +411,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(earlier, 1234), happyClientSecretsCondition(1, earlier, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -365,7 +429,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { Conditions: []configv1alpha1.Condition{ sadAllowedGrantTypesCondition(now, 1234, `"authorization_code" must always be included in "allowedGrantTypes"`), sadAllowedScopesCondition(now, 1234, `"openid" must always be included in "allowedScopes"`), - sadClientSecretsCondition(now, 1234, "no client secret found (no Secret storage found)"), + sadNoClientSecretsCondition(now, 1234, "no client secret found (no Secret storage found)"), }, }, }}, @@ -388,7 +452,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { Conditions: []configv1alpha1.Condition{ happyAllowedGrantTypesCondition(now, 1234), happyAllowedScopesCondition(now, 1234), - sadClientSecretsCondition(now, 1234, "error reading client secret storage: OIDC client secret storage data has wrong version: OIDC client secret storage has version wrong-version instead of 1"), + sadNoClientSecretsCondition(now, 1234, "error reading client secret storage: OIDC client secret storage data has wrong version: OIDC client secret storage has version wrong-version instead of 1"), }, }, }}, @@ -411,8 +475,35 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { Conditions: []configv1alpha1.Condition{ happyAllowedGrantTypesCondition(now, 1234), happyAllowedScopesCondition(now, 1234), - sadClientSecretsCondition(now, 1234, "no client secret found (empty list in storage)"), + sadNoClientSecretsCondition(now, 1234, "no client secret found (empty list in storage)"), }, + TotalClientSecrets: 0, + }, + }}, + }, + { + name: "client secret storage exists but some of the client secrets are invalid bcrypt hashes", + inputObjects: []runtime.Object{&configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: configv1alpha1.OIDCClientSpec{ + AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code"}, + AllowedScopes: []configv1alpha1.Scope{"openid"}, + }, + }}, + inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithSomeInvalidClientSecrets)}, + wantAPIActions: 1, // one update + wantResultingOIDCClients: []configv1alpha1.OIDCClient{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: configv1alpha1.OIDCClientStatus{ + Phase: "Error", + Conditions: []configv1alpha1.Condition{ + happyAllowedGrantTypesCondition(now, 1234), + happyAllowedScopesCondition(now, 1234), + sadInvalidClientSecretsCondition(now, 1234, + "hashed client secret at index 1: bcrypt cost 14 is below the required minimum of 15; "+ + "hashed client secret at index 2: crypto/bcrypt: hashedSecret too short to be a bcrypted password"), + }, + TotalClientSecrets: 3, }, }}, }, @@ -420,14 +511,14 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { name: "can operate on multiple at a time, e.g. one is valid one another is missing required minimum settings", inputObjects: []runtime.Object{ &configv1alpha1.OIDCClient{ - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test1", Generation: 1234, UID: "uid1"}, + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "client.oauth.pinniped.dev-test1", Generation: 1234, UID: "uid1"}, Spec: configv1alpha1.OIDCClientSpec{ AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code"}, AllowedScopes: []configv1alpha1.Scope{"openid"}, }, }, &configv1alpha1.OIDCClient{ - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test2", Generation: 4567, UID: "uid2"}, + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "client.oauth.pinniped.dev-test2", Generation: 4567, UID: "uid2"}, Spec: configv1alpha1.OIDCClientSpec{}, }, }, @@ -435,7 +526,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { wantAPIActions: 2, // one update for each OIDCClient wantResultingOIDCClients: []configv1alpha1.OIDCClient{ { - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test1", Generation: 1234, UID: "uid1"}, + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "client.oauth.pinniped.dev-test1", Generation: 1234, UID: "uid1"}, Status: configv1alpha1.OIDCClientStatus{ Phase: "Ready", Conditions: []configv1alpha1.Condition{ @@ -443,17 +534,19 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }, { - ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test2", Generation: 4567, UID: "uid2"}, + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "client.oauth.pinniped.dev-test2", Generation: 4567, UID: "uid2"}, Status: configv1alpha1.OIDCClientStatus{ Phase: "Error", Conditions: []configv1alpha1.Condition{ sadAllowedGrantTypesCondition(now, 4567, `"authorization_code" must always be included in "allowedGrantTypes"`), sadAllowedScopesCondition(now, 4567, `"openid" must always be included in "allowedScopes"`), - sadClientSecretsCondition(now, 4567, "no client secret found (no Secret storage found)"), + sadNoClientSecretsCondition(now, 4567, "no client secret found (no Secret storage found)"), }, + TotalClientSecrets: 0, }, }, }, @@ -474,6 +567,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(earlier, 1234, `"openid" must always be included in "allowedScopes"`), happyClientSecretsCondition(1, earlier, 1234), }, + TotalClientSecrets: 1, }, }}, inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, @@ -488,6 +582,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 4567), happyClientSecretsCondition(1, earlier, 4567), // was already validated earlier }, + TotalClientSecrets: 1, }, }}, }, @@ -511,6 +606,64 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, + }, + }}, + }, + { + name: "multiple errors on allowedScopes and allowedGrantTypes", + inputObjects: []runtime.Object{&configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: configv1alpha1.OIDCClientSpec{ + AllowedGrantTypes: []configv1alpha1.GrantType{"refresh_token"}, + AllowedScopes: []configv1alpha1.Scope{"pinniped:request-audience"}, + }, + }}, + wantAPIActions: 1, // one update + inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, + wantResultingOIDCClients: []configv1alpha1.OIDCClient{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: configv1alpha1.OIDCClientStatus{ + Phase: "Error", + Conditions: []configv1alpha1.Condition{ + sadAllowedGrantTypesCondition(now, 1234, + `"authorization_code" must always be included in "allowedGrantTypes"; `+ + `"urn:ietf:params:oauth:grant-type:token-exchange" must be included in "allowedGrantTypes" when "pinniped:request-audience" is included in "allowedScopes"`), + sadAllowedScopesCondition(now, 1234, + `"openid" must always be included in "allowedScopes"; `+ + `"offline_access" must be included in "allowedScopes" when "refresh_token" is included in "allowedGrantTypes"; `+ + `"username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes"`), + happyClientSecretsCondition(1, now, 1234), + }, + TotalClientSecrets: 1, + }, + }}, + }, + { + name: "another combination of multiple errors on allowedScopes and allowedGrantTypes", + inputObjects: []runtime.Object{&configv1alpha1.OIDCClient{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: configv1alpha1.OIDCClientSpec{ + AllowedGrantTypes: []configv1alpha1.GrantType{"urn:ietf:params:oauth:grant-type:token-exchange"}, + AllowedScopes: []configv1alpha1.Scope{"offline_access"}, + }, + }}, + wantAPIActions: 1, // one update + inputSecrets: []runtime.Object{storageSecretForUIDWithData(testUID, secretStringDataWithOneClientSecret)}, + wantResultingOIDCClients: []configv1alpha1.OIDCClient{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: configv1alpha1.OIDCClientStatus{ + Phase: "Error", + Conditions: []configv1alpha1.Condition{ + sadAllowedGrantTypesCondition(now, 1234, + `"authorization_code" must always be included in "allowedGrantTypes"; `+ + `"refresh_token" must be included in "allowedGrantTypes" when "offline_access" is included in "allowedScopes"`), + sadAllowedScopesCondition(now, 1234, + `"openid" must always be included in "allowedScopes"; `+ + `"pinniped:request-audience" must be included in "allowedScopes" when "urn:ietf:params:oauth:grant-type:token-exchange" is included in "allowedGrantTypes"`), + happyClientSecretsCondition(1, now, 1234), + }, + TotalClientSecrets: 1, }, }}, }, @@ -534,6 +687,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -557,6 +711,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(now, 1234, `"offline_access" must be included in "allowedScopes" when "refresh_token" is included in "allowedGrantTypes"`), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -580,6 +735,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(now, 1234, `"username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes"`), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -603,6 +759,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(now, 1234, `"username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes"`), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -626,6 +783,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(now, 1234, `"username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes"`), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -649,6 +807,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { sadAllowedScopesCondition(now, 1234, `"pinniped:request-audience" must be included in "allowedScopes" when "urn:ietf:params:oauth:grant-type:token-exchange" is included in "allowedGrantTypes"`), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -672,6 +831,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -695,6 +855,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -718,6 +879,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -741,6 +903,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -764,6 +927,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -787,6 +951,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -810,6 +975,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, @@ -833,6 +999,7 @@ func TestOIDCClientWatcherControllerSync(t *testing.T) { happyAllowedScopesCondition(now, 1234), happyClientSecretsCondition(1, now, 1234), }, + TotalClientSecrets: 1, }, }}, }, diff --git a/test/integration/supervisor_oidc_client_test.go b/test/integration/supervisor_oidc_client_test.go index adb43403..cf059fd3 100644 --- a/test/integration/supervisor_oidc_client_test.go +++ b/test/integration/supervisor_oidc_client_test.go @@ -506,7 +506,7 @@ func TestOIDCClientControllerValidations_Parallel(t *testing.T) { Type: "AllowedScopesValid", Status: "False", Reason: "MissingRequiredValue", - Message: `"openid" must always be included in "allowedScopes"`, + Message: `"openid" must always be included in "allowedScopes"; "offline_access" must be included in "allowedScopes" when "refresh_token" is included in "allowedGrantTypes"`, }, { Type: "ClientSecretExists",