Skip to content

Commit f854a3d

Browse files
adding auth config missing fields
Signed-off-by: Shaza Aldawamneh <[email protected]>
1 parent 33ccad5 commit f854a3d

File tree

28 files changed

+9433
-89
lines changed

28 files changed

+9433
-89
lines changed

config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml

Lines changed: 541 additions & 0 deletions
Large diffs are not rendered by default.

config/v1/types_authentication.go

Lines changed: 152 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
55
// +genclient
66
// +genclient:nonNamespaced
77
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
8-
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.spec.oidcProviders) || self.spec.oidcProviders.all(p, !has(p.oidcClients) || p.oidcClients.all(specC, self.status.oidcClients.exists(statusC, statusC.componentNamespace == specC.componentNamespace && statusC.componentName == specC.componentName) || (has(oldSelf.spec.oidcProviders) && oldSelf.spec.oidcProviders.exists(oldP, oldP.name == p.name && has(oldP.oidcClients) && oldP.oidcClients.exists(oldC, oldC.componentNamespace == specC.componentNamespace && oldC.componentName == specC.componentName)))))",message="all oidcClients in the oidcProviders must match their componentName and componentNamespace to either a previously configured oidcClient or they must exist in the status.oidcClients"
8+
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings;ExternalOIDCWithUpstreamParity,rule="!has(self.spec.oidcProviders) || self.spec.oidcProviders.all(p, !has(p.oidcClients) || p.oidcClients.all(specC, self.status.oidcClients.exists(statusC, statusC.componentNamespace == specC.componentNamespace && statusC.componentName == specC.componentName) || (has(oldSelf.spec.oidcProviders) && oldSelf.spec.oidcProviders.exists(oldP, oldP.name == p.name && has(oldP.oidcClients) && oldP.oidcClients.exists(oldC, oldC.componentNamespace == specC.componentNamespace && oldC.componentName == specC.componentName)))))",message="all oidcClients in the oidcProviders must match their componentName and componentNamespace to either a previously configured oidcClient or they must exist in the status.oidcClients"
99

1010
// Authentication specifies cluster-wide settings for authentication (like OAuth and
1111
// webhook token authenticators). The canonical name of an instance is `cluster`.
@@ -91,6 +91,7 @@ type AuthenticationSpec struct {
9191
// +kubebuilder:validation:MaxItems=1
9292
// +openshift:enable:FeatureGate=ExternalOIDC
9393
// +openshift:enable:FeatureGate=ExternalOIDCWithUIDAndExtraClaimMappings
94+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
9495
// +optional
9596
OIDCProviders []OIDCProvider `json:"oidcProviders,omitempty"`
9697
}
@@ -243,11 +244,30 @@ type OIDCProvider struct {
243244
// +listType=atomic
244245
// +optional
245246
ClaimValidationRules []TokenClaimValidationRule `json:"claimValidationRules,omitempty"`
247+
248+
// UserValidationRules defines the set of rules used to validate claims in a user's token.
249+
// Each rule is evaluated independently to determine whether the token subject is considered valid.
250+
// Rules can either require specific claims and values to be present,
251+
// or define CEL expressions that must evaluate to true for the token to be accepted.
252+
// If the expression in a rule evaluates to false, the token is rejected.
253+
// At least one rule must evaluate to true for the token to be considered valid.
254+
// A maximum of 64 rules can be specified. This field is optional.
255+
//
256+
// See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax.
257+
// +listType=atomic
258+
// +kubebuilder:validation:MaxItems=64
259+
// +kubebuilder:validation:XValidation:rule="self.size() == 0 || <feature-gate-enabled-check>",message="user validation rules are not supported when StructuredAuthenticationConfiguration feature gate is disabled"
260+
// +kubebuilder:validation:XValidation:rule="self.all(x, x.expression.size() > 0)",message="each expression must be non-empty"
261+
// +kubebuilder:validation:XValidation:rule="self.map(x, x.expression).countDistinct() == self.size()",message="expressions must be unique"
262+
// +optional
263+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
264+
UserValidationRules []TokenUserValidationRule `json:"userValidationRules,omitempty"`
246265
}
247266

248267
// +kubebuilder:validation:MinLength=1
249268
type TokenAudience string
250269

270+
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="self.?discoveryURL.orValue(\"\").size() > 0 ? (self.issuerURL.size() == 0 || self.discoveryURL.find('^.+[^/]') != self.issuerURL.find('^.+[^/]')) : true",message="discoveryURL must be different from issuerURL"
251271
type TokenIssuer struct {
252272
// issuerURL is a required field that configures the URL used to issue tokens
253273
// by the identity provider.
@@ -291,8 +311,50 @@ type TokenIssuer struct {
291311
//
292312
// +optional
293313
CertificateAuthority ConfigMapNameReference `json:"issuerCertificateAuthority"`
314+
// discoveryURL is an optional field that, if specified, overrides the default discovery endpoint
315+
// used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `issuerURL`
316+
// as "{url}/.well-known/openid-configuration".
317+
//
318+
// The discoveryURL must:
319+
// - Be a valid absolute URL.
320+
// - Use the HTTPS scheme.
321+
// - Not contain query parameters, user info, or fragments.
322+
// - Be different from the value of `url` (ignoring trailing slashes)
323+
//
324+
// +optional
325+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
326+
// +kubebuilder:validation:XValidation:rule="self.size() > 0 ? isURL(self) : true",message="discoveryURL must be a valid URL"
327+
// +kubebuilder:validation:XValidation:rule="self.size() > 0 ? (isURL(self) && url(self).getScheme() == 'https') : true",message="discoveryURL must be a valid https URL"
328+
// +kubebuilder:validation:XValidation:rule="self.size() > 0 ? url(self).getQuery().size() == 0 : true",message="discoveryURL must not contain query parameters"
329+
// +kubebuilder:validation:XValidation:rule="self.matches('^[^#]*$')",message="discoveryURL must not contain fragments"
330+
// +kubebuilder:validation:XValidation:rule="!(self.matches('^https:\\/\\/.+:.+@{1}.+\\/.*$'))",message="discoveryURL must not contain user info"
331+
// +kubebuilder:validation:MinLength=1
332+
// +kubebuilder:validation:MaxLength=2048
333+
DiscoveryURL *string `json:"discoveryURL,omitempty"`
334+
335+
// AudienceMatchPolicy specifies how token audiences are matched.
336+
// Allowed values are `MatchAny`.
337+
// When set to `MatchAny`, the token is accepted if any of its audiences match any of the configured audiences.
338+
// When omitted, the system applies a default policy. Currently, the default is `MatchAny`.
339+
//
340+
// +optional
341+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
342+
AudienceMatchPolicy *AudienceMatchPolicy `json:"audienceMatchPolicy,omitempty"`
294343
}
295344

345+
// AudienceMatchPolicyType is a set of valid values for Issuer.AudienceMatchPolicy.
346+
// When omitted, the system applies the default policy, which currently behaves as `MatchAny`.
347+
// Valid values are:
348+
// - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences.
349+
//
350+
// +kubebuilder:validation:Enum=MatchAny
351+
type AudienceMatchPolicy string
352+
353+
// Valid types for AudienceMatchPolicyType
354+
const (
355+
AudienceMatchPolicyMatchAny AudienceMatchPolicy = "MatchAny"
356+
)
357+
296358
type TokenClaimMappings struct {
297359
// username is a required field that configures how the username of a cluster identity
298360
// should be constructed from the claims in a JWT token issued by the identity provider.
@@ -717,45 +779,65 @@ type PrefixedClaimMapping struct {
717779
Prefix string `json:"prefix"`
718780
}
719781

720-
// TokenValidationRuleType represents the different
721-
// claim validation rule types that can be configured.
722-
// +enum
782+
// TokenValidationRuleType defines the type of token validation rule.
783+
//
784+
// +kubebuilder:validation:Type=string
785+
// +openshift:validation:FeatureGateAwareEnum:featureGate="",enum="RequiredClaim";StableValue
786+
// +openshift:validation:FeatureGateAwareEnum:featureGate=ExternalOIDCWithUpstreamParity,enum="RequiredClaim;Expression";StableValue;TechPreviewOnlyValue
787+
// +required
723788
type TokenValidationRuleType string
724789

725790
const (
726-
TokenValidationRuleTypeRequiredClaim = "RequiredClaim"
791+
// TokenValidationRuleRequiredClaim indicates that the token must contain a specific claim.
792+
// Used as a value for TokenValidationRuleType.
793+
TokenValidationRuleRequiredClaim = "RequiredClaim"
794+
// TokenValidationRuleExpression indicates that the token validation is defined via a CEL expression.
795+
// Used as a value for TokenValidationRuleType.
796+
TokenValidationRuleExpression = "Expression"
727797
)
728798

799+
// TokenClaimValidationRule represents a validation rule based on token claims.
800+
// If type is RequiredClaim, requiredClaim must be set.
801+
// If type is Expression, expressionRule must be set.
802+
//
803+
// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'RequiredClaim' ? has(self.requiredClaim) : !has(self.requiredClaim)",message="requiredClaim must be set when type is 'RequiredClaim', and forbidden otherwise"
804+
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.type) && self.type == 'Expression' ? has(self.expressionRule) : !has(self.expressionRule)",message="expressionRule must be set when type is 'Expression', and forbidden otherwise"
729805
type TokenClaimValidationRule struct {
730806
// type is an optional field that configures the type of the validation rule.
731807
//
732-
// Allowed values are 'RequiredClaim' and omitted (not provided or an empty string).
808+
// Allowed values are "RequiredClaim" and "Expression".
733809
//
734-
// When set to 'RequiredClaim', the Kubernetes API server
735-
// will be configured to validate that the incoming JWT
736-
// contains the required claim and that its value matches
737-
// the required value.
810+
// When set to 'RequiredClaim', the Kubernetes API server will be configured
811+
// to validate that the incoming JWT contains the required claim and that its
812+
// value matches the required value.
738813
//
739-
// Defaults to 'RequiredClaim'.
740-
//
741-
// +kubebuilder:validation:Enum={"RequiredClaim"}
742-
// +kubebuilder:default="RequiredClaim"
814+
// When set to 'Expression', the Kubernetes API server will be configured
815+
// to validate the incoming JWT against the configured CEL expression.
743816
Type TokenValidationRuleType `json:"type"`
744817

745-
// requiredClaim is an optional field that configures the required claim
746-
// and value that the Kubernetes API server will use to validate if an incoming
747-
// JWT is valid for this identity provider.
748-
//
818+
// requiredClaim allows configuring a required claim name and its expected value.
819+
// When type is RequiredClaim, this field is used by the Kubernetes API server
820+
// to validate if an incoming JWT is valid for this identity provider.
821+
749822
// +optional
750823
RequiredClaim *TokenRequiredClaim `json:"requiredClaim,omitempty"`
824+
825+
// ExpressionRule configures a CEL expression that will be used
826+
// by the Kubernetes API server to validate if an incoming JWT
827+
// is valid for this identity provider. The CEL expression must
828+
// return a boolean value where 'true' signals a valid state.
829+
// ExpressionRule must be set when 'type' is 'Expression', and
830+
// is forbidden otherwise.
831+
//
832+
// +optional
833+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
834+
ExpressionRule *TokenExpressionRule `json:"expressionRule,omitempty"`
751835
}
752836

753837
type TokenRequiredClaim struct {
754-
// claim is a required field that configures the name of the required claim.
755838
// When taken from the JWT claims, claim must be a string value.
756839
//
757840
// claim must not be an empty string ("").
758-
//
759841
// +kubebuilder:validation:MinLength=1
760842
// +required
761843
Claim string `json:"claim"`
@@ -771,3 +853,53 @@ type TokenRequiredClaim struct {
771853
// +required
772854
RequiredValue string `json:"requiredValue"`
773855
}
856+
857+
type TokenExpressionRule struct {
858+
// expression is a CEL expression evaluated against token claims.
859+
// The expression must be a non-empty string and no longer than 1024 characters.
860+
// The expression must return a boolean value where 'true' signals a valid token and 'false' an invalid one.
861+
// This field is required.
862+
//
863+
// +kubebuilder:validation:MinLength=1
864+
// +kubebuilder:validation:MaxLength=1024
865+
// +required
866+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
867+
Expression string `json:"expression,omitempty"`
868+
869+
// Message allows configuring a human-readable message that is logged by the Kubernetes API server
870+
// when a token fails validation based on the CEL expression defined in 'Expression'.
871+
// This field is optional. If provided, the message must be at least 1 character long
872+
// and cannot exceed 256 characters. This message is logged and not returned to the caller.
873+
874+
//
875+
// +optional
876+
// +kubebuilder:validation:MinLength=1
877+
// +kubebuilder:validation:MaxLength=256
878+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
879+
Message string `json:"message,omitempty"`
880+
}
881+
882+
// TokenUserValidationRule provides a CEL-based rule used to validate a token subject.
883+
// Each rule contains a CEL expression that is evaluated against the token’s claims.
884+
type TokenUserValidationRule struct {
885+
// expression is a CEL expression that must evaluate
886+
// to true for the token to be accepted. The expression is evaluated against the token's
887+
// user information (e.g., username, groups).
888+
// This field must be non-empty and may not exceed 1024 characters.
889+
//
890+
// +required
891+
// +kubebuilder:validation:MinLength=1
892+
// +kubebuilder:validation:MaxLength=1024
893+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
894+
Expression string `json:"expression,omitempty"`
895+
// Message allows configuring a human-readable message that is logged by the Kubernetes API server
896+
// when a token fails validation based on the CEL expression defined in 'Expression'.
897+
// This field is optional. If provided, the message must be at least 1 character long
898+
// and cannot exceed 256 characters. This message is logged and not returned to the caller.
899+
900+
// +optional
901+
// +kubebuilder:validation:MinLength=1
902+
// +kubebuilder:validation:MaxLength=256
903+
// +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity
904+
Message string `json:"message,omitempty"`
905+
}

0 commit comments

Comments
 (0)