From 9a632fd8db448015fa409fd027d5d2e19645071c Mon Sep 17 00:00:00 2001 From: KevFan Date: Mon, 30 Sep 2024 18:33:52 +0100 Subject: [PATCH 1/5] feat: introduce v1beta3 Signed-off-by: KevFan --- api/v1beta1/zz_generated.deepcopy.go | 4 +- api/v1beta2/zz_generated.deepcopy.go | 4 +- api/v1beta3/auth_config_conversion.go | 4 + api/v1beta3/auth_config_types.go | 883 +++++++ api/v1beta3/auth_config_webhook.go | 11 + api/v1beta3/groupversion_info.go | 36 + api/v1beta3/zz_generated.deepcopy.go | 1402 ++++++++++ .../authorino.kuadrant.io_authconfigs.yaml | 2302 +++++++++++++++++ install/manifests.yaml | 2302 +++++++++++++++++ main.go | 13 +- 10 files changed, 6952 insertions(+), 9 deletions(-) create mode 100644 api/v1beta3/auth_config_conversion.go create mode 100644 api/v1beta3/auth_config_types.go create mode 100644 api/v1beta3/auth_config_webhook.go create mode 100644 api/v1beta3/groupversion_info.go create mode 100644 api/v1beta3/zz_generated.deepcopy.go diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index c70bb9df..3fe74ccf 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* Copyright 2020 Red Hat, Inc. @@ -123,7 +122,8 @@ func (in *AuthConfigSpec) DeepCopyInto(out *AuthConfigSpec) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = make(JSONPatternExpressions, len(*in)) copy(*out, *in) } diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 3647917e..29171143 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* Copyright 2020 Red Hat, Inc. @@ -137,7 +136,8 @@ func (in *AuthConfigSpec) DeepCopyInto(out *AuthConfigSpec) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = make(PatternExpressions, len(*in)) copy(*out, *in) } diff --git a/api/v1beta3/auth_config_conversion.go b/api/v1beta3/auth_config_conversion.go new file mode 100644 index 00000000..418e7a5c --- /dev/null +++ b/api/v1beta3/auth_config_conversion.go @@ -0,0 +1,4 @@ +package v1beta3 + +// Hub marks this version as a conversion hub. +func (a *AuthConfig) Hub() {} diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go new file mode 100644 index 00000000..0c3be7ed --- /dev/null +++ b/api/v1beta3/auth_config_types.go @@ -0,0 +1,883 @@ +/* +Copyright 2020 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + k8score "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sruntime "k8s.io/apimachinery/pkg/runtime" +) + +const ( + // The following constants are used to identify the different methods of authentication. + UnknownAuthenticationMethod AuthenticationMethod = iota + ApiKeyAuthentication + JwtAuthentication + OAuth2TokenIntrospectionAuthentication + KubernetesTokenReviewAuthentication + X509ClientCertificateAuthentication + PlainIdentityAuthentication + AnonymousAccessAuthentication + + // The following constants are used to identify the different methods of metadata fetching. + UnknownMetadataMethod MetadataMethod = iota + HttpMetadata + UserInfoMetadata + UmaResourceMetadata + + // The following constants are used to identify the different methods of authorization. + UnknownAuthorizationMethod AuthorizationMethod = iota + PatternMatchingAuthorization + OpaAuthorization + KubernetesSubjectAccessReviewAuthorization + SpiceDBAuthorization + + // The following constants are used to identify the different methods of auth response. + UnknownAuthResponseMethod AuthResponseMethod = iota + PlainAuthResponse + JsonAuthResponse + WristbandAuthResponse + + // The following constants are used to identify the different methods of callback functions. + UnknownCallbackMethod CallbackMethod = iota + HttpCallback + + // The following constants are used to identify the different types of credentials. + UnknownCredentialsType CredentialsType = iota + AuthorizationHeaderCredentials + CustomHeaderCredentials + QueryStringCredentials + CookieCredentials + + // Status conditions + StatusConditionAvailable StatusConditionType = "Available" + StatusConditionReady StatusConditionType = "Ready" + + // Status reasons + StatusReasonReconciling string = "Reconciling" + StatusReasonReconciled string = "Reconciled" + StatusReasonInvalidResource string = "Invalid" + StatusReasonHostsLinked string = "HostsLinked" + StatusReasonHostsNotLinked string = "HostsNotLinked" + StatusReasonCachingError string = "CachingError" + StatusReasonUnknown string = "Unknown" + + EvaluatorDefaultCacheTTL = 60 +) + +type AuthenticationMethod int8 +type MetadataMethod int8 +type AuthorizationMethod int8 +type AuthResponseMethod int8 +type CallbackMethod int8 +type CredentialsType int8 + +type StatusConditionType string + +// AuthConfig is the schema for Authorino's AuthConfig API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.summary.ready`,description="Ready for all hosts" +// +kubebuilder:printcolumn:name="Hosts",type=string,JSONPath=`.status.summary.numHostsReady`,description="Number of hosts ready" +// +kubebuilder:printcolumn:name="Authentication",type=integer,JSONPath=`.status.summary.numIdentitySources`,description="Number of trusted identity sources",priority=2 +// +kubebuilder:printcolumn:name="Metadata",type=integer,JSONPath=`.status.summary.numMetadataSources`,description="Number of external metadata sources",priority=2 +// +kubebuilder:printcolumn:name="Authorization",type=integer,JSONPath=`.status.summary.numAuthorizationPolicies`,description="Number of authorization policies",priority=2 +// +kubebuilder:printcolumn:name="Response",type=integer,JSONPath=`.status.summary.numResponseItems`,description="Number of items added to the authorization response",priority=2 +// +kubebuilder:printcolumn:name="Wristband",type=boolean,JSONPath=`.status.summary.festivalWristbandEnabled`,description="Whether issuing Festival Wristbands",priority=2 +type AuthConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AuthConfigSpec `json:"spec,omitempty"` + Status AuthConfigStatus `json:"status,omitempty"` +} + +// Specifies the desired state of the AuthConfig resource, i.e. the authencation/authorization scheme to be applied to protect the matching service hosts. +type AuthConfigSpec struct { + // The list of public host names of the services protected by this authentication/authorization scheme. + // Authorino uses the requested host to lookup for the corresponding authentication/authorization configs to enforce. + Hosts []string `json:"hosts"` + + // Named sets of patterns that can be referred in `when` conditions and in pattern-matching authorization policy rules. + // +optional + NamedPatterns map[string]PatternExpressions `json:"patterns,omitempty"` + + // Overall conditions for the AuthConfig to be enforced. + // If omitted, the AuthConfig will be enforced at all requests. + // If present, all conditions must match for the AuthConfig to be enforced; otherwise, Authorino skips the AuthConfig and returns to the auth request with status OK. + // +optional + Conditions []PatternExpressionOrRef `json:"when,omitempty"` + + // Authentication configs. + // At least one config MUST evaluate to a valid identity object for the auth request to be successful. + // +optional + Authentication map[string]AuthenticationSpec `json:"authentication,omitempty"` + + // Metadata sources. + // Authorino fetches auth metadata as JSON from sources specified in this config. + // +optional + Metadata map[string]MetadataSpec `json:"metadata,omitempty"` + + // Authorization policies. + // All policies MUST evaluate to "allowed = true" for the auth request be successful. + // +optional + Authorization map[string]AuthorizationSpec `json:"authorization,omitempty"` + + // Response items. + // Authorino builds custom responses to the client of the auth request. + // +optional + Response *ResponseSpec `json:"response,omitempty"` + + // Callback functions. + // Authorino sends callbacks at the end of the auth pipeline to the endpoints specified in this config. + // +optional + Callbacks map[string]CallbackSpec `json:"callbacks,omitempty"` +} + +type PatternExpressions []PatternExpression + +type PatternExpression struct { + // Path selector to fetch content from the authorization JSON (e.g. 'request.method'). + // Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. + // Authorino custom JSON path modifiers are also supported. + Selector string `json:"selector,omitempty"` + // The binary operator to be applied to the content fetched from the authorization JSON, for comparison with "value". + // Possible values are: "eq" (equal to), "neq" (not equal to), "incl" (includes; for arrays), "excl" (excludes; for arrays), "matches" (regex) + Operator PatternExpressionOperator `json:"operator,omitempty"` + // The value of reference for the comparison with the content fetched from the authorization JSON. + // If used with the "matches" operator, the value must compile to a valid Golang regex. + Value string `json:"value,omitempty"` +} + +// +kubebuilder:validation:Enum:=eq;neq;incl;excl;matches +type PatternExpressionOperator string + +type PatternExpressionOrRef struct { + PatternExpression `json:",omitempty"` + PatternRef `json:",omitempty"` + + // A list of pattern expressions to be evaluated as a logical AND. + All []UnstructuredPatternExpressionOrRef `json:"all,omitempty"` + // A list of pattern expressions to be evaluated as a logical OR. + Any []UnstructuredPatternExpressionOrRef `json:"any,omitempty"` +} + +type UnstructuredPatternExpressionOrRef struct { + // +kubebuilder:pruning:PreserveUnknownFields + PatternExpressionOrRef `json:",omitempty"` +} + +type PatternRef struct { + // Reference to a named set of pattern expressions + Name string `json:"patternRef,omitempty"` +} + +type NamedValuesOrSelectors map[string]ValueOrSelector + +type ValueOrSelector struct { + // Static value + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + Value k8sruntime.RawExtension `json:"value,omitempty"` + + // Simple path selector to fetch content from the authorization JSON (e.g. 'request.method') or a string template with variables that resolve to patterns (e.g. "Hello, {auth.identity.name}!"). + // Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. + // The following Authorino custom modifiers are supported: @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, @base64:encode|decode and @strip. + Selector string `json:"selector,omitempty"` +} + +type CommonEvaluatorSpec struct { + // Priority group of the config. + // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + // +optional + // +kubebuilder:default:=0 + Priority int `json:"priority,omitempty"` + + // Whether this config should generate individual observability metrics + // +optional + // +kubebuilder:default:=false + Metrics bool `json:"metrics,omitempty"` + + // Conditions for Authorino to enforce this config. + // If omitted, the config will be enforced for all requests. + // If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. + // +optional + Conditions []PatternExpressionOrRef `json:"when,omitempty"` + + // Caching options for the resolved object returned when applying this config. + // Omit it to avoid caching objects for this config. + // +optional + Cache *EvaluatorCaching `json:"cache,omitempty"` +} + +type EvaluatorCaching struct { + // Key used to store the entry in the cache. + // The resolved key must be unique within the scope of this particular config. + Key ValueOrSelector `json:"key"` + + // Duration (in seconds) of the external data in the cache before pulled again from the source. + // +optional + // +kubebuilder:default:=60 + TTL int `json:"ttl,omitempty"` +} + +type AuthenticationSpec struct { + CommonEvaluatorSpec `json:",omitempty"` + + // Defines where credentials are required to be passed in the request for authentication based on this config. + // If omitted, it defaults to credentials passed in the HTTP Authorization header and the "Bearer" prefix prepended to the secret credential value. + // +optional + Credentials Credentials `json:"credentials,omitempty"` + + // Overrides the resolved identity object by setting the additional properties (claims) specified in this config, + // before appending the object to the authorization JSON. + // It requires the resolved identity object to always be a JSON object. + // Do not use this option with identity objects of other JSON types (array, string, etc). + // +optional + Overrides ExtendedProperties `json:"overrides,omitempty"` + + // Set default property values (claims) for the resolved identity object, that are set before appending the object to + // the authorization JSON. If the property is already present in the resolved identity object, the default value is ignored. + // It requires the resolved identity object to always be a JSON object. + // Do not use this option with identity objects of other JSON types (array, string, etc). + // +optional + Defaults ExtendedProperties `json:"defaults,omitempty"` + + AuthenticationMethodSpec `json:""` +} + +func (s *AuthenticationSpec) GetMethod() AuthenticationMethod { + if s.ApiKey != nil { + return ApiKeyAuthentication + } else if s.Jwt != nil { + return JwtAuthentication + } else if s.OAuth2TokenIntrospection != nil { + return OAuth2TokenIntrospectionAuthentication + } else if s.X509ClientCertificate != nil { + return X509ClientCertificateAuthentication + } else if s.KubernetesTokenReview != nil { + return KubernetesTokenReviewAuthentication + } else if s.Plain != nil { + return PlainIdentityAuthentication + } else if s.AnonymousAccess != nil { + return AnonymousAccessAuthentication + } + return UnknownAuthenticationMethod +} + +type Credentials struct { + AuthorizationHeader *Prefixed `json:"authorizationHeader,omitempty"` + CustomHeader *CustomHeader `json:"customHeader,omitempty"` + QueryString *Named `json:"queryString,omitempty"` + Cookie *Named `json:"cookie,omitempty"` +} + +func (c *Credentials) GetType() CredentialsType { + if c.AuthorizationHeader != nil { + return AuthorizationHeaderCredentials + } else if c.CustomHeader != nil { + return CustomHeaderCredentials + } else if c.QueryString != nil { + return QueryStringCredentials + } else if c.Cookie != nil { + return CookieCredentials + } + return UnknownCredentialsType +} + +type Named struct { + Name string `json:"name"` +} + +type Prefixed struct { + Prefix string `json:"prefix,omitempty"` +} + +type CustomHeader struct { + Named `json:""` +} + +type ExtendedProperties NamedValuesOrSelectors + +type AuthenticationMethodSpec struct { + // Authentication based on API keys stored in Kubernetes secrets. + ApiKey *ApiKeyAuthenticationSpec `json:"apiKey,omitempty"` + // Authentication based on JWT tokens. + Jwt *JwtAuthenticationSpec `json:"jwt,omitempty"` + // Authentication by OAuth2 token introspection. + OAuth2TokenIntrospection *OAuth2TokenIntrospectionSpec `json:"oauth2Introspection,omitempty"` + // Authentication by Kubernetes token review. + KubernetesTokenReview *KubernetesTokenReviewSpec `json:"kubernetesTokenReview,omitempty"` + // Authentication based on client X.509 certificates. + // The certificates presented by the clients must be signed by a trusted CA whose certificates are stored in Kubernetes secrets. + X509ClientCertificate *X509ClientCertificateAuthenticationSpec `json:"x509,omitempty"` + // Identity object extracted from the context. + // Use this method when authentication is performed beforehand by a proxy and the resulting object passed to Authorino as JSON in the auth request. + Plain *PlainIdentitySpec `json:"plain,omitempty"` + // Anonymous access. + AnonymousAccess *AnonymousAccessSpec `json:"anonymous,omitempty"` +} + +// Settings to select the API key Kubernetes secrets. +type ApiKeyAuthenticationSpec struct { + // Label selector used by Authorino to match secrets from the cluster storing valid credentials to authenticate to this service + Selector *metav1.LabelSelector `json:"selector"` + + // Whether Authorino should look for API key secrets in all namespaces or only in the same namespace as the AuthConfig. + // Enabling this option in namespaced Authorino instances has no effect. + // +optional + // +kubebuilder:default:=false + AllNamespaces bool `json:"allNamespaces,omitempty"` +} + +// Settings to fetch the JSON Web Key Set (JWKS) for the JWT authentication. +type JwtAuthenticationSpec struct { + // URL of the issuer of the JWT. + // If `jwksUrl` is omitted, Authorino will append the path to the OpenID Connect Well-Known Discovery endpoint + // (i.e. "/.well-known/openid-configuration") to this URL, to discover the OIDC configuration where to obtain + // the "jkws_uri" claim from. + // The value must coincide with the value of the "iss" (issuer) claim of the discovered OpenID Connect configuration. + // +optional + IssuerUrl string `json:"issuerUrl"` + + // Decides how long to wait before refreshing the JWKS (in seconds). + // If omitted, Authorino will never refresh the JWKS. + // +optional + TTL int `json:"ttl,omitempty"` +} + +// Settings to perform the OAuth2 token introspection request. +type OAuth2TokenIntrospectionSpec struct { + // The full URL of the token introspection endpoint. + Url string `json:"endpoint"` + + // The token type hint for the token introspection. + // If omitted, it defaults to "access_token". + // +optional + TokenTypeHint string `json:"tokenTypeHint,omitempty"` + + // Reference to a Kubernetes secret in the same namespace, that stores client credentials to the OAuth2 server. + Credentials *k8score.LocalObjectReference `json:"credentialsRef"` +} + +// Parameters of the Kubernetes TokenReview request +type KubernetesTokenReviewSpec struct { + // The list of audiences (scopes) that must be claimed in a Kubernetes authentication token supplied in the request, and reviewed by Authorino. + // If omitted, Authorino will review tokens expecting the host name of the requested protected service amongst the audiences. + // +optional + Audiences []string `json:"audiences,omitempty"` +} + +// Settings to authenticate clients by X.509 certificates. +type X509ClientCertificateAuthenticationSpec struct { + // Label selector used by Authorino to match secrets from the cluster storing trusted CA certificates to validate + // clients trying to authenticate to this service + Selector *metav1.LabelSelector `json:"selector"` + + // Whether Authorino should look for TLS secrets in all namespaces or only in the same namespace as the AuthConfig. + // Enabling this option in namespaced Authorino instances has no effect. + // +optional + // +kubebuilder:default:=false + AllNamespaces bool `json:"allNamespaces,omitempty"` +} + +// Settings to extract the identity object from the context. +type PlainIdentitySpec struct { + // Simple path selector to fetch content from the authorization JSON (e.g. 'request.method') or a string template with variables that resolve to patterns (e.g. "Hello, {auth.identity.name}!"). + // Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. + // The following Authorino custom modifiers are supported: @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, @base64:encode|decode and @strip. + Selector string `json:"selector"` +} + +type AnonymousAccessSpec struct{} + +type MetadataSpec struct { + CommonEvaluatorSpec `json:""` + MetadataMethodSpec `json:""` +} + +func (s *MetadataSpec) GetMethod() MetadataMethod { + if s.Http != nil { + return HttpMetadata + } else if s.UserInfo != nil { + return UserInfoMetadata + } else if s.Uma != nil { + return UmaResourceMetadata + } + return UnknownMetadataMethod +} + +type MetadataMethodSpec struct { + // External source of auth metadata via HTTP request + Http *HttpEndpointSpec `json:"http,omitempty"` + // OpendID Connect UserInfo linked to an OIDC authentication config specified in this same AuthConfig. + UserInfo *UserInfoMetadataSpec `json:"userInfo,omitempty"` + // User-Managed Access (UMA) source of resource data. + Uma *UmaMetadataSpec `json:"uma,omitempty"` +} + +// Settings of the external HTTP request +type HttpEndpointSpec struct { + // Endpoint URL of the HTTP service. + // The value can include variable placeholders in the format "{selector}", where "selector" is any pattern supported + // by https://pkg.go.dev/github.com/tidwall/gjson and selects value from the authorization JSON. + // E.g. https://ext-auth-server.io/metadata?p={request.path} + Url string `json:"url"` + + // HTTP verb used in the request to the service. Accepted values: GET (default), POST. + // When the request method is POST, the authorization JSON is passed in the body of the request. + // +optional + // +kubebuilder:default:=GET + Method *HttpMethod `json:"method,omitempty"` + + // Raw body of the HTTP request. + // Supersedes 'bodyParameters'; use either one or the other. + // Use it with method=POST; for GET requests, set parameters as query string in the 'endpoint' (placeholders can be used). + // +optional + Body *ValueOrSelector `json:"body,omitempty"` + + // Custom parameters to encode in the body of the HTTP request. + // Superseded by 'body'; use either one or the other. + // Use it with method=POST; for GET requests, set parameters as query string in the 'endpoint' (placeholders can be used). + // +optional + Parameters NamedValuesOrSelectors `json:"bodyParameters,omitempty"` + + // Content-Type of the request body. Shapes how 'bodyParameters' are encoded. + // Use it with method=POST; for GET requests, Content-Type is automatically set to 'text/plain'. + // +optional + // +kubebuilder:default:=application/x-www-form-urlencoded + ContentType HttpContentType `json:"contentType,omitempty"` + + // Custom headers in the HTTP request. + // +optional + Headers NamedValuesOrSelectors `json:"headers,omitempty"` + + // Reference to a Secret key whose value will be passed by Authorino in the request. + // The HTTP service can use the shared secret to authenticate the origin of the request. + // Ignored if used together with oauth2. + // +optional + SharedSecret *SecretKeyReference `json:"sharedSecretRef,omitempty"` + + // Authentication with the HTTP service by OAuth2 Client Credentials grant. + // +optional + OAuth2 *OAuth2ClientAuthentication `json:"oauth2,omitempty"` + + // Defines where client credentials will be passed in the request to the service. + // If omitted, it defaults to client credentials passed in the HTTP Authorization header and the "Bearer" prefix expected prepended to the secret value. + // +optional + Credentials Credentials `json:"credentials,omitempty"` +} + +// +kubebuilder:validation:Enum:=GET;POST;PUT;PATCH;DELETE;HEAD;OPTIONS;CONNECT;TRACE +type HttpMethod string + +// +kubebuilder:validation:Enum:=application/x-www-form-urlencoded;application/json +type HttpContentType string + +// Reference to a Kubernetes secret +type SecretKeyReference struct { + // The name of the secret in the Authorino's namespace to select from. + Name string `json:"name"` + + // The key of the secret to select from. Must be a valid secret key. + Key string `json:"key"` +} + +// Settings for OAuth2 client authentication with the external service +type OAuth2ClientAuthentication struct { + // Token endpoint URL of the OAuth2 resource server. + TokenUrl string `json:"tokenUrl"` + // OAuth2 Client ID. + ClientId string `json:"clientId"` + // Reference to a Kuberentes Secret key that stores that OAuth2 Client Secret. + ClientSecret SecretKeyReference `json:"clientSecretRef"` + // Optional scopes for the client credentials grant, if supported by he OAuth2 server. + Scopes []string `json:"scopes,omitempty"` + // Optional extra parameters for the requests to the token URL. + ExtraParams map[string]string `json:"extraParams,omitempty"` + // Caches and reuses the token until expired. + // Set it to false to force fetch the token at every authorization request regardless of expiration. + // +kubebuilder:default:=true + Cache *bool `json:"cache,omitempty"` +} + +// Settings of the OpendID Connect UserInfo linked to an OIDC-enabled JWT authentication config of this same AuthConfig. +type UserInfoMetadataSpec struct { + // The name of an OIDC-enabled JWT authentication config whose OpenID Connect configuration discovered includes the OIDC "userinfo_endpoint" claim. + IdentitySource string `json:"identitySource"` +} + +// Settings of the User-Managed Access (UMA) source of resource data. +type UmaMetadataSpec struct { + // The endpoint of the UMA server. + // The value must coincide with the "issuer" claim of the UMA config discovered from the well-known uma configuration endpoint. + Endpoint string `json:"endpoint"` + + // Reference to a Kubernetes secret in the same namespace, that stores client credentials to the resource registration API of the UMA server. + Credentials *k8score.LocalObjectReference `json:"credentialsRef"` +} + +type AuthorizationSpec struct { + CommonEvaluatorSpec `json:""` + AuthorizationMethodSpec `json:""` +} + +func (s *AuthorizationSpec) GetMethod() AuthorizationMethod { + if s.PatternMatching != nil { + return PatternMatchingAuthorization + } else if s.Opa != nil { + return OpaAuthorization + } else if s.KubernetesSubjectAccessReview != nil { + return KubernetesSubjectAccessReviewAuthorization + } else if s.SpiceDB != nil { + return SpiceDBAuthorization + } + return UnknownAuthorizationMethod +} + +type AuthorizationMethodSpec struct { + // Pattern-matching authorization rules. + PatternMatching *PatternMatchingAuthorizationSpec `json:"patternMatching,omitempty"` + // Open Policy Agent (OPA) Rego policy. + Opa *OpaAuthorizationSpec `json:"opa,omitempty"` + // Authorization by Kubernetes SubjectAccessReview + KubernetesSubjectAccessReview *KubernetesSubjectAccessReviewAuthorizationSpec `json:"kubernetesSubjectAccessReview,omitempty"` + // Authorization decision delegated to external Authzed/SpiceDB server. + SpiceDB *SpiceDBAuthorizationSpec `json:"spicedb,omitempty"` +} + +type PatternMatchingAuthorizationSpec struct { + Patterns []PatternExpressionOrRef `json:"patterns"` +} + +// Settings of the Open Policy Agent (OPA) authorization. +type OpaAuthorizationSpec struct { + // Authorization policy as a Rego language document. + // The Rego document must include the "allow" condition, set by Authorino to "false" by default (i.e. requests are unauthorized unless changed). + // The Rego document must NOT include the "package" declaration in line 1. + Rego string `json:"rego,omitempty"` + + // Settings for fetching the OPA policy from an external registry. + // Use it alternatively to 'rego'. + // For the configurations of the HTTP request, the following options are not implemented: 'method', 'body', 'bodyParameters', + // 'contentType', 'headers', 'oauth2'. Use it only with: 'url', 'sharedSecret', 'credentials'. + External *ExternalOpaPolicy `json:"externalPolicy,omitempty"` + + // Returns the value of all Rego rules in the virtual document. Values can be read in subsequent evaluators/phases of the Auth Pipeline. + // Otherwise, only the default `allow` rule will be exposed. + // Returning all Rego rules can affect performance of OPA policies during reconciliation (policy precompile) and at runtime. + // +kubebuilder:default:=false + AllValues bool `json:"allValues,omitempty"` +} + +// ExternalOpaPolicy sets the configs for fetching OPA policies from an external source. +type ExternalOpaPolicy struct { + *HttpEndpointSpec `json:""` + + // Duration (in seconds) of the external data in the cache before pulled again from the source. + TTL int `json:"ttl,omitempty"` +} + +// Parameters of the Kubernetes SubjectAccessReview request. +type KubernetesSubjectAccessReviewAuthorizationSpec struct { + // User to check for authorization in the Kubernetes RBAC. + // Omit it to check for group authorization only. + User *ValueOrSelector `json:"user,omitempty"` + + // Groups the user must be a member of or, if `user` is omitted, the groups to check for authorization in the Kubernetes RBAC. + Groups []string `json:"groups,omitempty"` + + // Use resourceAttributes to check permissions on Kubernetes resources. + // If omitted, it performs a non-resource SubjectAccessReview, with verb and path inferred from the request. + // +optional + ResourceAttributes *KubernetesSubjectAccessReviewResourceAttributesSpec `json:"resourceAttributes,omitempty"` +} + +type KubernetesSubjectAccessReviewResourceAttributesSpec struct { + // API group of the resource. + // Use '*' for all API groups. + Group ValueOrSelector `json:"group,omitempty"` + // Resource kind + // Use '*' for all resource kinds. + Resource ValueOrSelector `json:"resource,omitempty"` + // Subresource kind + SubResource ValueOrSelector `json:"subresource,omitempty"` + // Resource name + // Omit it to check for authorization on all resources of the specified kind. + Name ValueOrSelector `json:"name,omitempty"` + // Namespace where the user must have permissions on the resource. + Namespace ValueOrSelector `json:"namespace,omitempty"` + // Verb to check for authorization on the resource. + // Use '*' for all verbs. + Verb ValueOrSelector `json:"verb,omitempty"` +} + +// Settings of the check request to the external SpiceDB server. +type SpiceDBAuthorizationSpec struct { + // Hostname and port number to the GRPC interface of the SpiceDB server (e.g. spicedb:50051). + Endpoint string `json:"endpoint"` + + // Insecure HTTP connection (i.e. disables TLS verification) + Insecure bool `json:"insecure,omitempty"` + + // Reference to a Secret key whose value will be used by Authorino to authenticate with the Authzed service. + SharedSecret *SecretKeyReference `json:"sharedSecretRef,omitempty"` + + // The subject that will be checked for the permission or relation. + Subject *SpiceDBObject `json:"subject,omitempty"` + + // The resource on which to check the permission or relation. + Resource *SpiceDBObject `json:"resource,omitempty"` + + // The name of the permission (or relation) on which to execute the check. + Permission ValueOrSelector `json:"permission,omitempty"` +} + +type SpiceDBObject struct { + Name ValueOrSelector `json:"name,omitempty"` + Kind ValueOrSelector `json:"kind,omitempty"` +} + +// Settings of the custom auth response. +type ResponseSpec struct { + // Customizations on the denial status attributes when the request is unauthenticated. + // For integration of Authorino via proxy, the proxy must honour the response status attributes specified in this config. + // Default: 401 Unauthorized + // +optional + Unauthenticated *DenyWithSpec `json:"unauthenticated,omitempty"` + + // Customizations on the denial status attributes when the request is unauthorized. + // For integration of Authorino via proxy, the proxy must honour the response status attributes specified in this config. + // Default: 403 Forbidden + // +optional + Unauthorized *DenyWithSpec `json:"unauthorized,omitempty"` + + // Response items to be included in the auth response when the request is authenticated and authorized. + // For integration of Authorino via proxy, the proxy must use these settings to propagate dynamic metadata and/or inject data in the request. + // +optional + Success WrappedSuccessResponseSpec `json:"success,omitempty"` +} + +// +kubebuilder:validation:Minimum:=300 +// +kubebuilder:validation:Maximum:=599 +type DenyWithCode int64 + +// Setting of the custom denial response. +type DenyWithSpec struct { + // HTTP status code to override the default denial status code. + Code DenyWithCode `json:"code,omitempty"` + + // HTTP message to override the default denial message. + Message *ValueOrSelector `json:"message,omitempty"` + + // HTTP response headers to override the default denial headers. + Headers NamedValuesOrSelectors `json:"headers,omitempty"` + + // HTTP response body to override the default denial body. + Body *ValueOrSelector `json:"body,omitempty"` +} + +// Settings of the custom success response. +type WrappedSuccessResponseSpec struct { + // Custom success response items wrapped as HTTP headers. + // For integration of Authorino via proxy, the proxy must use these settings to inject data in the request. + Headers map[string]HeaderSuccessResponseSpec `json:"headers,omitempty"` + + // Custom success response items wrapped as HTTP headers. + // For integration of Authorino via proxy, the proxy must use these settings to propagate dynamic metadata. + // See https://www.envoyproxy.io/docs/envoy/latest/configuration/advanced/well_known_dynamic_metadata + DynamicMetadata map[string]SuccessResponseSpec `json:"dynamicMetadata,omitempty"` +} + +type HeaderSuccessResponseSpec struct { + SuccessResponseSpec `json:",omitempty"` +} + +// Settings of the success custom response item. +type SuccessResponseSpec struct { + CommonEvaluatorSpec `json:""` + AuthResponseMethodSpec `json:""` + + // The key used to add the custom response item (name of the HTTP header or root property of the Dynamic Metadata object). + // If omitted, it will be set to the name of the response config. + Key string `json:"key,omitempty"` +} + +func (s *SuccessResponseSpec) GetMethod() AuthResponseMethod { + if s.Plain != nil { + return PlainAuthResponse + } else if s.Json != nil { + return JsonAuthResponse + } else if s.Wristband != nil { + return WristbandAuthResponse + } + return UnknownAuthResponseMethod +} + +// Settings of the custom success response item. +type AuthResponseMethodSpec struct { + // Plain text content + Plain *PlainAuthResponseSpec `json:"plain,omitempty"` + // JSON object + // Specify it as the list of properties of the object, whose values can combine static values and values selected from the authorization JSON. + Json *JsonAuthResponseSpec `json:"json,omitempty"` + // Authorino Festival Wristband token + Wristband *WristbandAuthResponseSpec `json:"wristband,omitempty"` +} + +// Static value or selector to set the plain custom response item. +type PlainAuthResponseSpec ValueOrSelector + +// List of properties of the JSON object to set the custom response item +// The values can be static or selected from the authorization JSON. +type JsonAuthResponseSpec struct { + Properties NamedValuesOrSelectors `json:"properties"` +} + +// Settings of the Festival Wristband token custom response item. +type WristbandAuthResponseSpec struct { + // The endpoint to the Authorino service that issues the wristband (format: ://:/, where = /://:/, + where = /://:/, + where = /://:/, + where = /://:/, + where = / Date: Tue, 1 Oct 2024 11:15:19 +0100 Subject: [PATCH 2/5] refactor: remove v1beta1 & webhook Signed-off-by: KevFan --- .github/workflows/e2e-test.yaml | 2 +- PROJECT | 4 +- api/v1beta1/auth_config_conversion.go | 1079 ------ api/v1beta1/auth_config_conversion_test.go | 1025 ------ api/v1beta1/auth_config_types.go | 827 ----- api/v1beta1/groupversion_info.go | 36 - api/v1beta1/zz_generated.deepcopy.go | 1252 ------- api/v1beta2/auth_config_conversion.go | 4 - api/v1beta2/auth_config_webhook.go | 11 - api/v1beta3/auth_config_conversion.go | 4 - api/v1beta3/auth_config_types.go | 1 - api/v1beta3/auth_config_webhook.go | 11 - go.mod | 2 +- .../authorino.kuadrant.io_authconfigs.yaml | 2172 +------------ install/crd/kustomization.yaml | 4 +- install/crd/patches/oneof_in_authconfigs.yaml | 200 -- .../crd/patches/webhook_in_authconfigs.yaml | 2 +- install/manifests.yaml | 2885 ++--------------- main.go | 8 - tests/v1beta1/authconfig-invalid.yaml | 30 - tests/v1beta1/authconfig.yaml | 306 -- tests/v1beta3/authconfig-invalid.yaml | 30 + tests/v1beta3/authconfig.yaml | 308 ++ 23 files changed, 581 insertions(+), 9622 deletions(-) delete mode 100644 api/v1beta1/auth_config_conversion.go delete mode 100644 api/v1beta1/auth_config_conversion_test.go delete mode 100644 api/v1beta1/auth_config_types.go delete mode 100644 api/v1beta1/groupversion_info.go delete mode 100644 api/v1beta1/zz_generated.deepcopy.go delete mode 100644 api/v1beta2/auth_config_conversion.go delete mode 100644 api/v1beta2/auth_config_webhook.go delete mode 100644 api/v1beta3/auth_config_conversion.go delete mode 100644 api/v1beta3/auth_config_webhook.go delete mode 100644 tests/v1beta1/authconfig-invalid.yaml delete mode 100644 tests/v1beta1/authconfig.yaml create mode 100644 tests/v1beta3/authconfig-invalid.yaml create mode 100644 tests/v1beta3/authconfig.yaml diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index 50597460..706b502b 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -15,7 +15,7 @@ jobs: matrix: go-version: [1.21.x] platform: [ubuntu-latest] - authconfig_version: [v1beta1, v1beta2] + authconfig_version: [v1beta2, v1beta3] runs-on: ${{ matrix.platform }} defaults: run: diff --git a/PROJECT b/PROJECT index 1fd76fe1..f495821d 100644 --- a/PROJECT +++ b/PROJECT @@ -5,10 +5,10 @@ repo: github.com/kuadrant/authorino/ resources: - group: config kind: AuthConfig - version: v1beta1 + version: v1beta2 - group: config kind: AuthConfig - version: v1beta2 + version: v1beta3 version: 3-alpha plugins: go.sdk.operatorframework.io/v2-alpha: {} diff --git a/api/v1beta1/auth_config_conversion.go b/api/v1beta1/auth_config_conversion.go deleted file mode 100644 index f60ec485..00000000 --- a/api/v1beta1/auth_config_conversion.go +++ /dev/null @@ -1,1079 +0,0 @@ -package v1beta1 - -import ( - "encoding/json" - - "github.com/kuadrant/authorino/api/v1beta2" - "github.com/kuadrant/authorino/pkg/utils" - "github.com/tidwall/gjson" - k8sruntime "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/conversion" -) - -func (src *AuthConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v1beta2.AuthConfig) - - logger := ctrl.Log.WithName("webhook").WithName("authconfig").WithName("converto").WithValues("src", src) - logger.V(1).Info("starting converting resource") - - // metadata - dst.ObjectMeta = src.ObjectMeta - - // hosts - dst.Spec.Hosts = src.Spec.Hosts - - // named patterns - if src.Spec.Patterns != nil { - dst.Spec.NamedPatterns = make(map[string]v1beta2.PatternExpressions, len(src.Spec.Patterns)) - for name, patterns := range src.Spec.Patterns { - dst.Spec.NamedPatterns[name] = utils.Map(patterns, convertPatternExpressionTo) - } - } - - // conditions - dst.Spec.Conditions = utils.Map(src.Spec.Conditions, convertPatternExpressionOrRefTo) - - // authentication - if src.Spec.Identity != nil { - dst.Spec.Authentication = make(map[string]v1beta2.AuthenticationSpec, len(src.Spec.Identity)) - for _, identity := range src.Spec.Identity { - name, authentication := convertAuthenticationTo(identity) - dst.Spec.Authentication[name] = authentication - } - } - - // metadata - if src.Spec.Metadata != nil { - dst.Spec.Metadata = make(map[string]v1beta2.MetadataSpec, len(src.Spec.Metadata)) - for _, metadataSrc := range src.Spec.Metadata { - name, metadata := convertMetadataTo(metadataSrc) - dst.Spec.Metadata[name] = metadata - } - } - - // authorization - if src.Spec.Authorization != nil { - dst.Spec.Authorization = make(map[string]v1beta2.AuthorizationSpec, len(src.Spec.Authorization)) - for _, authorizationSrc := range src.Spec.Authorization { - name, authorization := convertAuthorizationTo(authorizationSrc) - dst.Spec.Authorization[name] = authorization - } - } - - // response - denyWith := src.Spec.DenyWith - - if denyWith != nil || len(src.Spec.Response) > 0 { - dst.Spec.Response = &v1beta2.ResponseSpec{} - } - - if denyWith != nil && denyWith.Unauthenticated != nil { - dst.Spec.Response.Unauthenticated = convertDenyWithSpecTo(denyWith.Unauthenticated) - } - - if denyWith != nil && denyWith.Unauthorized != nil { - dst.Spec.Response.Unauthorized = convertDenyWithSpecTo(denyWith.Unauthorized) - } - - for _, responseSrc := range src.Spec.Response { - if responseSrc.Wrapper != "httpHeader" && responseSrc.Wrapper != "" { - continue - } - if dst.Spec.Response.Success.Headers == nil { - dst.Spec.Response.Success.Headers = make(map[string]v1beta2.HeaderSuccessResponseSpec) - } - name, response := convertSuccessResponseTo(responseSrc) - dst.Spec.Response.Success.Headers[name] = v1beta2.HeaderSuccessResponseSpec{ - SuccessResponseSpec: response, - } - } - - for _, responseSrc := range src.Spec.Response { - if responseSrc.Wrapper != "envoyDynamicMetadata" { - continue - } - if dst.Spec.Response.Success.DynamicMetadata == nil { - dst.Spec.Response.Success.DynamicMetadata = make(map[string]v1beta2.SuccessResponseSpec) - } - name, response := convertSuccessResponseTo(responseSrc) - dst.Spec.Response.Success.DynamicMetadata[name] = response - } - - // callbacks - if src.Spec.Callbacks != nil { - dst.Spec.Callbacks = make(map[string]v1beta2.CallbackSpec, len(src.Spec.Callbacks)) - for _, callbackSrc := range src.Spec.Callbacks { - name, callback := convertCallbackTo(callbackSrc) - dst.Spec.Callbacks[name] = callback - } - } - - // status - dst.Status = convertStatusTo(src.Status) - - logger.V(1).Info("finished converting resource", "dst", dst) - - return nil -} - -func (dst *AuthConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v1beta2.AuthConfig) - - logger := ctrl.Log.WithName("webhook").WithName("authconfig").WithName("converfrom").WithValues("src", src) - logger.V(1).Info("starting converting resource") - - // metadata - dst.ObjectMeta = src.ObjectMeta - - // hosts - dst.Spec.Hosts = src.Spec.Hosts - - // named patterns - if src.Spec.NamedPatterns != nil { - dst.Spec.Patterns = make(map[string]JSONPatternExpressions, len(src.Spec.NamedPatterns)) - for name, patterns := range src.Spec.NamedPatterns { - dst.Spec.Patterns[name] = utils.Map(patterns, convertPatternExpressionFrom) - } - } - - // conditions - dst.Spec.Conditions = utils.Map(src.Spec.Conditions, convertPatternExpressionOrRefFrom) - - // identity - for name, authentication := range src.Spec.Authentication { - identity := convertAuthenticationFrom(name, authentication) - dst.Spec.Identity = append(dst.Spec.Identity, identity) - } - - // metadata - for name, metadataSrc := range src.Spec.Metadata { - metadata := convertMetadataFrom(name, metadataSrc) - dst.Spec.Metadata = append(dst.Spec.Metadata, metadata) - } - - // authorization - for name, authorizationSrc := range src.Spec.Authorization { - authorization := convertAuthorizationFrom(name, authorizationSrc) - dst.Spec.Authorization = append(dst.Spec.Authorization, authorization) - } - - // response - if src.Spec.Response != nil { - for name, responseSrc := range src.Spec.Response.Success.Headers { - response := convertSuccessResponseFrom(name, responseSrc.SuccessResponseSpec, "httpHeader") - dst.Spec.Response = append(dst.Spec.Response, response) - } - - for name, responseSrc := range src.Spec.Response.Success.DynamicMetadata { - response := convertSuccessResponseFrom(name, responseSrc, "envoyDynamicMetadata") - dst.Spec.Response = append(dst.Spec.Response, response) - } - - // denyWith - if src.Spec.Response.Unauthenticated != nil || src.Spec.Response.Unauthorized != nil { - dst.Spec.DenyWith = &DenyWith{} - } - - if denyWithSrc := src.Spec.Response.Unauthenticated; denyWithSrc != nil { - dst.Spec.DenyWith.Unauthenticated = convertDenyWithSpecFrom(denyWithSrc) - } - - if denyWithSrc := src.Spec.Response.Unauthorized; denyWithSrc != nil { - dst.Spec.DenyWith.Unauthorized = convertDenyWithSpecFrom(denyWithSrc) - } - } - - // callbacks - for name, callbackSrc := range src.Spec.Callbacks { - callback := convertCallbackFrom(name, callbackSrc) - dst.Spec.Callbacks = append(dst.Spec.Callbacks, callback) - } - - // status - dst.Status = convertStatusFrom(src.Status) - - logger.V(1).Info("finished converting resource", "dst", dst) - - return nil -} - -func convertPatternExpressionTo(src JSONPatternExpression) v1beta2.PatternExpression { - return v1beta2.PatternExpression{ - Selector: src.Selector, - Operator: v1beta2.PatternExpressionOperator(src.Operator), - Value: src.Value, - } -} - -func convertPatternExpressionFrom(src v1beta2.PatternExpression) JSONPatternExpression { - return JSONPatternExpression{ - Selector: src.Selector, - Operator: JSONPatternOperator(src.Operator), - Value: src.Value, - } -} - -func convertPatternExpressionOrRefTo(src JSONPattern) v1beta2.PatternExpressionOrRef { - pattern := v1beta2.PatternExpressionOrRef{ - PatternExpression: convertPatternExpressionTo(src.JSONPatternExpression), - PatternRef: v1beta2.PatternRef{ - Name: src.JSONPatternRef.JSONPatternName, - }, - } - if len(src.All) > 0 { - pattern.All = make([]v1beta2.UnstructuredPatternExpressionOrRef, len(src.All)) - for i, p := range src.All { - pattern.All[i] = v1beta2.UnstructuredPatternExpressionOrRef{PatternExpressionOrRef: convertPatternExpressionOrRefTo(p.JSONPattern)} - } - } - if len(src.Any) > 0 { - pattern.Any = make([]v1beta2.UnstructuredPatternExpressionOrRef, len(src.Any)) - for i, p := range src.Any { - pattern.Any[i] = v1beta2.UnstructuredPatternExpressionOrRef{PatternExpressionOrRef: convertPatternExpressionOrRefTo(p.JSONPattern)} - } - } - return pattern -} - -func convertPatternExpressionOrRefFrom(src v1beta2.PatternExpressionOrRef) JSONPattern { - pattern := JSONPattern{ - JSONPatternExpression: convertPatternExpressionFrom(src.PatternExpression), - JSONPatternRef: JSONPatternRef{ - JSONPatternName: src.PatternRef.Name, - }, - } - if len(src.All) > 0 { - pattern.All = make([]UnstructuredJSONPattern, len(src.All)) - for i, p := range src.All { - pattern.All[i] = UnstructuredJSONPattern{JSONPattern: convertPatternExpressionOrRefFrom(p.PatternExpressionOrRef)} - } - } - if len(src.Any) > 0 { - pattern.Any = make([]UnstructuredJSONPattern, len(src.Any)) - for i, p := range src.Any { - pattern.Any[i] = UnstructuredJSONPattern{JSONPattern: convertPatternExpressionOrRefFrom(p.PatternExpressionOrRef)} - } - } - return pattern -} - -func convertAuthenticationTo(src *Identity) (string, v1beta2.AuthenticationSpec) { - authentication := v1beta2.AuthenticationSpec{ - CommonEvaluatorSpec: v1beta2.CommonEvaluatorSpec{ - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefTo), - Cache: convertEvaluatorCachingTo(src.Cache), - }, - Credentials: convertCredentialsTo(src.Credentials), - } - - var overrides []JsonProperty - for _, extendedProperty := range src.ExtendedProperties { - if !extendedProperty.Overwrite { - continue - } - overrides = append(overrides, extendedProperty.JsonProperty) - } - if len(overrides) > 0 { - authentication.Overrides = v1beta2.ExtendedProperties(convertNamedValuesOrSelectorsTo(overrides)) - } - - var defaults []JsonProperty - for _, extendedProperty := range src.ExtendedProperties { - if extendedProperty.Overwrite { - continue - } - defaults = append(defaults, extendedProperty.JsonProperty) - } - if len(defaults) > 0 { - authentication.Defaults = v1beta2.ExtendedProperties(convertNamedValuesOrSelectorsTo(defaults)) - } - - switch src.GetType() { - case IdentityApiKey: - selector := *src.APIKey.Selector - authentication.ApiKey = &v1beta2.ApiKeyAuthenticationSpec{ - Selector: &selector, - AllNamespaces: src.APIKey.AllNamespaces, - } - case IdentityOidc: - authentication.Jwt = &v1beta2.JwtAuthenticationSpec{ - IssuerUrl: src.Oidc.Endpoint, - TTL: src.Oidc.TTL, - } - case IdentityOAuth2: - credentials := *src.OAuth2.Credentials - authentication.OAuth2TokenIntrospection = &v1beta2.OAuth2TokenIntrospectionSpec{ - Url: src.OAuth2.TokenIntrospectionUrl, - TokenTypeHint: src.OAuth2.TokenTypeHint, - Credentials: &credentials, - } - case IdentityKubernetesAuth: - authentication.KubernetesTokenReview = &v1beta2.KubernetesTokenReviewSpec{ - Audiences: src.KubernetesAuth.Audiences, - } - case IdentityMTLS: - selector := *src.MTLS.Selector - authentication.X509ClientCertificate = &v1beta2.X509ClientCertificateAuthenticationSpec{ - Selector: &selector, - AllNamespaces: src.MTLS.AllNamespaces, - } - case IdentityPlain: - authentication.Plain = &v1beta2.PlainIdentitySpec{ - Selector: src.Plain.AuthJSON, - } - case IdentityAnonymous: - authentication.AnonymousAccess = &v1beta2.AnonymousAccessSpec{} - } - - return src.Name, authentication -} - -func convertAuthenticationFrom(name string, src v1beta2.AuthenticationSpec) *Identity { - extendedProperties := utils.Map(convertNamedValuesOrSelectorsFrom(v1beta2.NamedValuesOrSelectors(src.Overrides)), func(jsonProperty JsonProperty) ExtendedProperty { - return ExtendedProperty{ - JsonProperty: jsonProperty, - Overwrite: true, - } - }) - extendedProperties = append(extendedProperties, utils.Map(convertNamedValuesOrSelectorsFrom(v1beta2.NamedValuesOrSelectors(src.Defaults)), func(jsonProperty JsonProperty) ExtendedProperty { - return ExtendedProperty{ - JsonProperty: jsonProperty, - Overwrite: false, - } - })...) - - identity := &Identity{ - Name: name, - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefFrom), - Cache: convertEvaluatorCachingFrom(src.Cache), - Credentials: convertCredentialsFrom(src.Credentials), - ExtendedProperties: extendedProperties, - } - - switch src.GetMethod() { - case v1beta2.ApiKeyAuthentication: - selector := *src.ApiKey.Selector - identity.APIKey = &Identity_APIKey{ - Selector: &selector, - AllNamespaces: src.ApiKey.AllNamespaces, - } - case v1beta2.JwtAuthentication: - identity.Oidc = &Identity_OidcConfig{ - Endpoint: src.Jwt.IssuerUrl, - TTL: src.Jwt.TTL, - } - case v1beta2.OAuth2TokenIntrospectionAuthentication: - credentials := *src.OAuth2TokenIntrospection.Credentials - identity.OAuth2 = &Identity_OAuth2Config{ - TokenIntrospectionUrl: src.OAuth2TokenIntrospection.Url, - TokenTypeHint: src.OAuth2TokenIntrospection.TokenTypeHint, - Credentials: &credentials, - } - case v1beta2.KubernetesTokenReviewAuthentication: - identity.KubernetesAuth = &Identity_KubernetesAuth{ - Audiences: src.KubernetesTokenReview.Audiences, - } - case v1beta2.X509ClientCertificateAuthentication: - selector := *src.X509ClientCertificate.Selector - identity.MTLS = &Identity_MTLS{ - Selector: &selector, - AllNamespaces: src.X509ClientCertificate.AllNamespaces, - } - case v1beta2.PlainIdentityAuthentication: - selector := Identity_Plain(ValueFrom{ - AuthJSON: src.Plain.Selector, - }) - identity.Plain = &selector - case v1beta2.AnonymousAccessAuthentication: - identity.Anonymous = &Identity_Anonymous{} - } - - return identity -} - -func convertEvaluatorCachingTo(src *EvaluatorCaching) *v1beta2.EvaluatorCaching { - if src == nil { - return nil - } - return &v1beta2.EvaluatorCaching{ - Key: convertValueOrSelectorTo(src.Key), - TTL: src.TTL, - } -} - -func convertEvaluatorCachingFrom(src *v1beta2.EvaluatorCaching) *EvaluatorCaching { - if src == nil { - return nil - } - return &EvaluatorCaching{ - Key: convertValueOrSelectorFrom(src.Key), - TTL: src.TTL, - } -} - -func convertValueOrSelectorTo(src StaticOrDynamicValue) v1beta2.ValueOrSelector { - value := k8sruntime.RawExtension{} - if src.ValueFrom.AuthJSON == "" { - jsonString, err := json.Marshal(src.Value) - if err == nil { - value.Raw = jsonString - } - } - return v1beta2.ValueOrSelector{ - Value: value, - Selector: src.ValueFrom.AuthJSON, - } -} - -func convertValueOrSelectorFrom(src v1beta2.ValueOrSelector) StaticOrDynamicValue { - return StaticOrDynamicValue{ - Value: gjson.ParseBytes(src.Value.Raw).String(), - ValueFrom: convertSelectorFrom(src), - } -} - -func convertCredentialsTo(src Credentials) v1beta2.Credentials { - credentials := v1beta2.Credentials{} - switch src.In { - case "authorization_header": - credentials.AuthorizationHeader = &v1beta2.Prefixed{ - Prefix: src.KeySelector, - } - case "custom_header": - credentials.CustomHeader = &v1beta2.CustomHeader{ - Named: v1beta2.Named{Name: src.KeySelector}, - } - case "query": - credentials.QueryString = &v1beta2.Named{ - Name: src.KeySelector, - } - case "cookie": - credentials.Cookie = &v1beta2.Named{ - Name: src.KeySelector, - } - } - return credentials -} - -func convertCredentialsFrom(src v1beta2.Credentials) Credentials { - var in, key string - switch src.GetType() { - case v1beta2.AuthorizationHeaderCredentials: - in = "authorization_header" - key = src.AuthorizationHeader.Prefix - case v1beta2.CustomHeaderCredentials: - in = "custom_header" - key = src.CustomHeader.Name - case v1beta2.QueryStringCredentials: - in = "query" - key = src.QueryString.Name - case v1beta2.CookieCredentials: - in = "cookie" - key = src.Cookie.Name - } - return Credentials{ - In: Credentials_In(in), - KeySelector: key, - } -} - -func convertNamedValuesOrSelectorsTo(src []JsonProperty) v1beta2.NamedValuesOrSelectors { - if src == nil { - return nil - } - namedValuesOrSelectors := v1beta2.NamedValuesOrSelectors{} - for _, jsonProperty := range src { - value := k8sruntime.RawExtension{} - if jsonProperty.ValueFrom.AuthJSON == "" { - value.Raw = jsonProperty.Value.Raw - } - namedValuesOrSelectors[jsonProperty.Name] = v1beta2.ValueOrSelector{ - Value: value, - Selector: jsonProperty.ValueFrom.AuthJSON, - } - } - return namedValuesOrSelectors -} - -func convertNamedValuesOrSelectorsFrom(src v1beta2.NamedValuesOrSelectors) []JsonProperty { - if src == nil { - return nil - } - jsonProperties := make([]JsonProperty, 0, len(src)) - for name, valueOrSelector := range src { - jsonProperties = append(jsonProperties, JsonProperty{ - Name: name, - Value: valueOrSelector.Value, - ValueFrom: convertSelectorFrom(valueOrSelector), - }) - } - return jsonProperties -} - -func convertSelectorFrom(src v1beta2.ValueOrSelector) ValueFrom { - return ValueFrom{ - AuthJSON: src.Selector, - } -} - -func convertMetadataTo(src *Metadata) (string, v1beta2.MetadataSpec) { - metadata := v1beta2.MetadataSpec{ - CommonEvaluatorSpec: v1beta2.CommonEvaluatorSpec{ - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefTo), - Cache: convertEvaluatorCachingTo(src.Cache), - }, - } - - switch src.GetType() { - case MetadataGenericHTTP: - metadata.Http = convertHttpEndpointSpecTo(src.GenericHTTP) - case MetadataUserinfo: - metadata.UserInfo = &v1beta2.UserInfoMetadataSpec{ - IdentitySource: src.UserInfo.IdentitySource, - } - case MetadataUma: - credentials := *src.UMA.Credentials - metadata.Uma = &v1beta2.UmaMetadataSpec{ - Endpoint: src.UMA.Endpoint, - Credentials: &credentials, - } - } - - return src.Name, metadata -} - -func convertMetadataFrom(name string, src v1beta2.MetadataSpec) *Metadata { - metadata := &Metadata{ - Name: name, - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefFrom), - Cache: convertEvaluatorCachingFrom(src.Cache), - } - - switch src.GetMethod() { - case v1beta2.HttpMetadata: - metadata.GenericHTTP = convertHttpEndpointSpecFrom(src.Http) - case v1beta2.UserInfoMetadata: - metadata.UserInfo = &Metadata_UserInfo{ - IdentitySource: src.UserInfo.IdentitySource, - } - case v1beta2.UmaResourceMetadata: - credentials := *src.Uma.Credentials - metadata.UMA = &Metadata_UMA{ - Endpoint: src.Uma.Endpoint, - Credentials: &credentials, - } - } - - return metadata -} - -func convertHttpEndpointSpecTo(src *Metadata_GenericHTTP) *v1beta2.HttpEndpointSpec { - if src == nil { - return nil - } - return &v1beta2.HttpEndpointSpec{ - Url: src.Endpoint, - Method: convertMethodTo(src.Method), - Body: convertPtrValueOrSelectorTo(src.Body), - Parameters: convertNamedValuesOrSelectorsTo(src.Parameters), - ContentType: convertContentTypeTo(src.ContentType), - Headers: convertNamedValuesOrSelectorsTo(src.Headers), - SharedSecret: convertSecretKeyReferenceTo(src.SharedSecret), - OAuth2: convertOAuth2ClientAuthenticationTo(src.OAuth2), - Credentials: convertCredentialsTo(src.Credentials), - } -} - -func convertHttpEndpointSpecFrom(src *v1beta2.HttpEndpointSpec) *Metadata_GenericHTTP { - if src == nil { - return nil - } - return &Metadata_GenericHTTP{ - Endpoint: src.Url, - Method: convertMethodFrom(src.Method), - Body: convertPtrValueOrSelectorFrom(src.Body), - Parameters: convertNamedValuesOrSelectorsFrom(src.Parameters), - ContentType: convertContentTypeFrom(src.ContentType), - Headers: convertNamedValuesOrSelectorsFrom(src.Headers), - SharedSecret: convertSecretKeyReferenceFrom(src.SharedSecret), - OAuth2: convertOAuth2ClientAuthenticationFrom(src.OAuth2), - Credentials: convertCredentialsFrom(src.Credentials), - } -} - -func convertMethodTo(src *GenericHTTP_Method) *v1beta2.HttpMethod { - if src == nil { - return nil - } - method := v1beta2.HttpMethod(*src) - return &method -} - -func convertMethodFrom(src *v1beta2.HttpMethod) *GenericHTTP_Method { - if src == nil { - return nil - } - method := GenericHTTP_Method(*src) - return &method -} - -func convertPtrValueOrSelectorTo(src *StaticOrDynamicValue) *v1beta2.ValueOrSelector { - if src == nil { - return nil - } - v := convertValueOrSelectorTo(*src) - return &v -} - -func convertPtrValueOrSelectorFrom(src *v1beta2.ValueOrSelector) *StaticOrDynamicValue { - if src == nil { - return nil - } - v := convertValueOrSelectorFrom(*src) - return &v -} - -func convertContentTypeTo(src Metadata_GenericHTTP_ContentType) v1beta2.HttpContentType { - return v1beta2.HttpContentType(src) -} - -func convertContentTypeFrom(src v1beta2.HttpContentType) Metadata_GenericHTTP_ContentType { - return Metadata_GenericHTTP_ContentType(src) -} - -func convertSecretKeyReferenceTo(src *SecretKeyReference) *v1beta2.SecretKeyReference { - if src == nil { - return nil - } - return &v1beta2.SecretKeyReference{ - Name: src.Name, - Key: src.Key, - } -} - -func convertSecretKeyReferenceFrom(src *v1beta2.SecretKeyReference) *SecretKeyReference { - if src == nil { - return nil - } - return &SecretKeyReference{ - Name: src.Name, - Key: src.Key, - } -} - -func convertOAuth2ClientAuthenticationTo(src *OAuth2ClientAuthentication) *v1beta2.OAuth2ClientAuthentication { - if src == nil { - return nil - } - o := &v1beta2.OAuth2ClientAuthentication{ - TokenUrl: src.TokenUrl, - ClientId: src.ClientId, - ClientSecret: *convertSecretKeyReferenceTo(&src.ClientSecret), - Scopes: src.Scopes, - ExtraParams: src.ExtraParams, - } - if src.Cache != nil { - cache := *src.Cache - o.Cache = &cache - } - return o -} - -func convertOAuth2ClientAuthenticationFrom(src *v1beta2.OAuth2ClientAuthentication) *OAuth2ClientAuthentication { - if src == nil { - return nil - } - o := &OAuth2ClientAuthentication{ - TokenUrl: src.TokenUrl, - ClientId: src.ClientId, - ClientSecret: *convertSecretKeyReferenceFrom(&src.ClientSecret), - Scopes: src.Scopes, - ExtraParams: src.ExtraParams, - } - if src.Cache != nil { - cache := *src.Cache - o.Cache = &cache - } - return o -} - -func convertAuthorizationTo(src *Authorization) (string, v1beta2.AuthorizationSpec) { - authorization := v1beta2.AuthorizationSpec{ - CommonEvaluatorSpec: v1beta2.CommonEvaluatorSpec{ - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefTo), - Cache: convertEvaluatorCachingTo(src.Cache), - }, - } - - switch src.GetType() { - case AuthorizationJSONPatternMatching: - authorization.PatternMatching = &v1beta2.PatternMatchingAuthorizationSpec{ - Patterns: utils.Map(src.JSON.Rules, convertPatternExpressionOrRefTo), - } - case AuthorizationOPA: - authorization.Opa = &v1beta2.OpaAuthorizationSpec{ - Rego: src.OPA.InlineRego, - External: convertOpaExternalRegistryTo(src.OPA.ExternalRegistry), - AllValues: src.OPA.AllValues, - } - case AuthorizationKubernetesAuthz: - authorization.KubernetesSubjectAccessReview = &v1beta2.KubernetesSubjectAccessReviewAuthorizationSpec{ - User: convertPtrValueOrSelectorTo(&src.KubernetesAuthz.User), - Groups: src.KubernetesAuthz.Groups, - ResourceAttributes: convertKubernetesSubjectAccessReviewResourceAttributesTo(src.KubernetesAuthz.ResourceAttributes), - } - case AuthorizationAuthzed: - authorization.SpiceDB = &v1beta2.SpiceDBAuthorizationSpec{ - Endpoint: src.Authzed.Endpoint, - Insecure: src.Authzed.Insecure, - SharedSecret: convertSecretKeyReferenceTo(src.Authzed.SharedSecret), - Subject: spiceDBObjectTo(src.Authzed.Subject), - Resource: spiceDBObjectTo(src.Authzed.Resource), - Permission: convertValueOrSelectorTo(src.Authzed.Permission), - } - } - - return src.Name, authorization -} - -func convertAuthorizationFrom(name string, src v1beta2.AuthorizationSpec) *Authorization { - authorization := &Authorization{ - Name: name, - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefFrom), - Cache: convertEvaluatorCachingFrom(src.Cache), - } - - switch src.GetMethod() { - case v1beta2.PatternMatchingAuthorization: - authorization.JSON = &Authorization_JSONPatternMatching{ - Rules: utils.Map(src.PatternMatching.Patterns, convertPatternExpressionOrRefFrom), - } - case v1beta2.OpaAuthorization: - authorization.OPA = &Authorization_OPA{ - InlineRego: src.Opa.Rego, - ExternalRegistry: convertOpaExternalRegistryFrom(src.Opa.External), - AllValues: src.Opa.AllValues, - } - case v1beta2.KubernetesSubjectAccessReviewAuthorization: - authorization.KubernetesAuthz = &Authorization_KubernetesAuthz{ - Groups: src.KubernetesSubjectAccessReview.Groups, - ResourceAttributes: convertKubernetesSubjectAccessReviewResourceAttributesFrom(src.KubernetesSubjectAccessReview.ResourceAttributes), - } - if src.KubernetesSubjectAccessReview.User != nil { - authorization.KubernetesAuthz.User = convertValueOrSelectorFrom(*src.KubernetesSubjectAccessReview.User) - } - case v1beta2.SpiceDBAuthorization: - authorization.Authzed = &Authorization_Authzed{ - Endpoint: src.SpiceDB.Endpoint, - Insecure: src.SpiceDB.Insecure, - SharedSecret: convertSecretKeyReferenceFrom(src.SpiceDB.SharedSecret), - Subject: spiceDBObjectFrom(src.SpiceDB.Subject), - Resource: spiceDBObjectFrom(src.SpiceDB.Resource), - Permission: convertValueOrSelectorFrom(src.SpiceDB.Permission), - } - } - - return authorization -} - -func convertOpaExternalRegistryTo(src ExternalRegistry) *v1beta2.ExternalOpaPolicy { - if src.Endpoint == "" { - return nil - } - return &v1beta2.ExternalOpaPolicy{ - HttpEndpointSpec: &v1beta2.HttpEndpointSpec{ - Url: src.Endpoint, - SharedSecret: convertSecretKeyReferenceTo(src.SharedSecret), - Credentials: convertCredentialsTo(src.Credentials), - }, - TTL: src.TTL, - } -} - -func convertOpaExternalRegistryFrom(src *v1beta2.ExternalOpaPolicy) ExternalRegistry { - if src == nil { - return ExternalRegistry{} - } - return ExternalRegistry{ - Endpoint: src.Url, - SharedSecret: convertSecretKeyReferenceFrom(src.SharedSecret), - Credentials: convertCredentialsFrom(src.Credentials), - TTL: src.TTL, - } -} - -func convertKubernetesSubjectAccessReviewResourceAttributesTo(src *Authorization_KubernetesAuthz_ResourceAttributes) *v1beta2.KubernetesSubjectAccessReviewResourceAttributesSpec { - if src == nil { - return nil - } - return &v1beta2.KubernetesSubjectAccessReviewResourceAttributesSpec{ - Namespace: convertValueOrSelectorTo(src.Namespace), - Group: convertValueOrSelectorTo(src.Group), - Resource: convertValueOrSelectorTo(src.Resource), - Name: convertValueOrSelectorTo(src.Name), - SubResource: convertValueOrSelectorTo(src.SubResource), - Verb: convertValueOrSelectorTo(src.Verb), - } -} - -func convertKubernetesSubjectAccessReviewResourceAttributesFrom(src *v1beta2.KubernetesSubjectAccessReviewResourceAttributesSpec) *Authorization_KubernetesAuthz_ResourceAttributes { - if src == nil { - return nil - } - return &Authorization_KubernetesAuthz_ResourceAttributes{ - Namespace: convertValueOrSelectorFrom(src.Namespace), - Group: convertValueOrSelectorFrom(src.Group), - Resource: convertValueOrSelectorFrom(src.Resource), - Name: convertValueOrSelectorFrom(src.Name), - SubResource: convertValueOrSelectorFrom(src.SubResource), - Verb: convertValueOrSelectorFrom(src.Verb), - } -} - -func spiceDBObjectTo(src *AuthzedObject) *v1beta2.SpiceDBObject { - if src == nil { - return nil - } - return &v1beta2.SpiceDBObject{ - Kind: convertValueOrSelectorTo(src.Kind), - Name: convertValueOrSelectorTo(src.Name), - } -} - -func spiceDBObjectFrom(src *v1beta2.SpiceDBObject) *AuthzedObject { - if src == nil { - return nil - } - return &AuthzedObject{ - Kind: convertValueOrSelectorFrom(src.Kind), - Name: convertValueOrSelectorFrom(src.Name), - } -} - -func convertDenyWithSpecTo(src *DenyWithSpec) *v1beta2.DenyWithSpec { - if src == nil { - return nil - } - return &v1beta2.DenyWithSpec{ - Code: v1beta2.DenyWithCode(src.Code), - Headers: convertNamedValuesOrSelectorsTo(src.Headers), - Message: convertPtrValueOrSelectorTo(src.Message), - Body: convertPtrValueOrSelectorTo(src.Body), - } -} - -func convertDenyWithSpecFrom(src *v1beta2.DenyWithSpec) *DenyWithSpec { - if src == nil { - return nil - } - return &DenyWithSpec{ - Code: DenyWith_Code(src.Code), - Headers: convertNamedValuesOrSelectorsFrom(src.Headers), - Message: convertPtrValueOrSelectorFrom(src.Message), - Body: convertPtrValueOrSelectorFrom(src.Body), - } -} - -func convertSuccessResponseTo(src *Response) (string, v1beta2.SuccessResponseSpec) { - response := v1beta2.SuccessResponseSpec{ - CommonEvaluatorSpec: v1beta2.CommonEvaluatorSpec{ - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefTo), - Cache: convertEvaluatorCachingTo(src.Cache), - }, - Key: src.WrapperKey, - } - - switch src.GetType() { - case ResponsePlain: - selector := v1beta2.PlainAuthResponseSpec(convertValueOrSelectorTo(StaticOrDynamicValue(*src.Plain))) - response.Plain = &selector - case ResponseDynamicJSON: - response.Json = &v1beta2.JsonAuthResponseSpec{ - Properties: convertNamedValuesOrSelectorsTo(src.JSON.Properties), - } - case ResponseWristband: - response.Wristband = &v1beta2.WristbandAuthResponseSpec{ - Issuer: src.Wristband.Issuer, - CustomClaims: convertNamedValuesOrSelectorsTo(src.Wristband.CustomClaims), - } - if src.Wristband.TokenDuration != nil { - duration := *src.Wristband.TokenDuration - response.Wristband.TokenDuration = &duration - } - for _, keySrc := range src.Wristband.SigningKeyRefs { - if keySrc == nil { - continue - } - key := &v1beta2.WristbandSigningKeyRef{ - Name: keySrc.Name, - Algorithm: v1beta2.WristbandSigningKeyAlgorithm(keySrc.Algorithm), - } - response.Wristband.SigningKeyRefs = append(response.Wristband.SigningKeyRefs, key) - } - } - - return src.Name, response -} - -func convertSuccessResponseFrom(name string, src v1beta2.SuccessResponseSpec, wrapper string) *Response { - response := &Response{ - Name: name, - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefFrom), - Cache: convertEvaluatorCachingFrom(src.Cache), - Wrapper: Response_Wrapper(wrapper), - WrapperKey: src.Key, - } - - switch src.GetMethod() { - case v1beta2.PlainAuthResponse: - selector := Response_Plain(convertValueOrSelectorFrom(v1beta2.ValueOrSelector(*src.Plain))) - response.Plain = &selector - case v1beta2.JsonAuthResponse: - response.JSON = &Response_DynamicJSON{ - Properties: convertNamedValuesOrSelectorsFrom(src.Json.Properties), - } - case v1beta2.WristbandAuthResponse: - response.Wristband = &Response_Wristband{ - Issuer: src.Wristband.Issuer, - CustomClaims: convertNamedValuesOrSelectorsFrom(src.Wristband.CustomClaims), - } - if src.Wristband.TokenDuration != nil { - duration := *src.Wristband.TokenDuration - response.Wristband.TokenDuration = &duration - } - for _, keySrc := range src.Wristband.SigningKeyRefs { - if keySrc == nil { - continue - } - key := SigningKeyRef{ - Name: keySrc.Name, - Algorithm: SigningKeyAlgorithm(keySrc.Algorithm), - } - response.Wristband.SigningKeyRefs = append(response.Wristband.SigningKeyRefs, &key) - } - } - - return response -} - -func convertCallbackTo(src *Callback) (string, v1beta2.CallbackSpec) { - callback := v1beta2.CallbackSpec{ - CommonEvaluatorSpec: v1beta2.CommonEvaluatorSpec{ - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefTo), - }, - } - - switch src.GetType() { - case CallbackHTTP: - callback.Http = convertHttpEndpointSpecTo(src.HTTP) - } - - return src.Name, callback -} - -func convertCallbackFrom(name string, src v1beta2.CallbackSpec) *Callback { - callback := &Callback{ - Name: name, - Priority: src.Priority, - Metrics: src.Metrics, - Conditions: utils.Map(src.Conditions, convertPatternExpressionOrRefFrom), - } - - switch src.GetMethod() { - case v1beta2.HttpCallback: - callback.HTTP = convertHttpEndpointSpecFrom(src.Http) - } - - return callback -} - -func convertStatusTo(src AuthConfigStatus) v1beta2.AuthConfigStatus { - return v1beta2.AuthConfigStatus{ - Conditions: utils.Map(src.Conditions, func(conditionSrc Condition) v1beta2.AuthConfigStatusCondition { - condition := v1beta2.AuthConfigStatusCondition{ - Type: v1beta2.StatusConditionType(conditionSrc.Type), - Status: conditionSrc.Status, - LastTransitionTime: conditionSrc.LastTransitionTime, - Reason: conditionSrc.Reason, - Message: conditionSrc.Message, - } - if conditionSrc.LastUpdatedTime != nil { - time := *conditionSrc.LastUpdatedTime - condition.LastUpdatedTime = &time - } - return condition - }), - Summary: convertStatusSummaryTo(src.Summary), - } -} - -func convertStatusFrom(src v1beta2.AuthConfigStatus) AuthConfigStatus { - return AuthConfigStatus{ - Conditions: utils.Map(src.Conditions, func(conditionSrc v1beta2.AuthConfigStatusCondition) Condition { - condition := Condition{ - Type: ConditionType(conditionSrc.Type), - Status: conditionSrc.Status, - LastTransitionTime: conditionSrc.LastTransitionTime, - Reason: conditionSrc.Reason, - Message: conditionSrc.Message, - } - if conditionSrc.LastUpdatedTime != nil { - time := *conditionSrc.LastUpdatedTime - condition.LastUpdatedTime = &time - } - return condition - }), - Summary: convertStatusSummaryFrom(src.Summary), - } -} - -func convertStatusSummaryTo(src Summary) v1beta2.AuthConfigStatusSummary { - hostsReady := make([]string, len(src.HostsReady)) - copy(hostsReady, src.HostsReady) - - return v1beta2.AuthConfigStatusSummary{ - Ready: src.Ready, - HostsReady: hostsReady, - NumHostsReady: src.NumHostsReady, - NumIdentitySources: src.NumIdentitySources, - NumMetadataSources: src.NumMetadataSources, - NumAuthorizationPolicies: src.NumAuthorizationPolicies, - NumResponseItems: src.NumResponseItems, - FestivalWristbandEnabled: src.FestivalWristbandEnabled, - } -} - -func convertStatusSummaryFrom(src v1beta2.AuthConfigStatusSummary) Summary { - hostsReady := make([]string, len(src.HostsReady)) - copy(hostsReady, src.HostsReady) - - return Summary{ - Ready: src.Ready, - HostsReady: hostsReady, - NumHostsReady: src.NumHostsReady, - NumIdentitySources: src.NumIdentitySources, - NumMetadataSources: src.NumMetadataSources, - NumAuthorizationPolicies: src.NumAuthorizationPolicies, - NumResponseItems: src.NumResponseItems, - FestivalWristbandEnabled: src.FestivalWristbandEnabled, - } -} diff --git a/api/v1beta1/auth_config_conversion_test.go b/api/v1beta1/auth_config_conversion_test.go deleted file mode 100644 index f9c860a6..00000000 --- a/api/v1beta1/auth_config_conversion_test.go +++ /dev/null @@ -1,1025 +0,0 @@ -package v1beta1 - -import ( - "encoding/json" - "reflect" - "sort" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/kuadrant/authorino/api/v1beta2" -) - -func TestConvertTo(t *testing.T) { - converted := &v1beta2.AuthConfig{} - config := authConfig() - config.ConvertTo(converted) - - expected := hubAuthConfig() - if !reflect.DeepEqual(expected, converted) { - t.Error(cmp.Diff(expected, converted)) - } -} - -func TestConvertFrom(t *testing.T) { - converted := &AuthConfig{} - converted.ConvertFrom(hubAuthConfig()) - - sort.Slice(converted.Spec.Identity, func(i, j int) bool { - return converted.Spec.Identity[i].Name < converted.Spec.Identity[j].Name - }) - sort.Slice(converted.Spec.Metadata, func(i, j int) bool { - return converted.Spec.Metadata[i].Name < converted.Spec.Metadata[j].Name - }) - sort.Slice(converted.Spec.Authorization, func(i, j int) bool { - return converted.Spec.Authorization[i].Name < converted.Spec.Authorization[j].Name - }) - sort.Slice(converted.Spec.Response, func(i, j int) bool { - return converted.Spec.Response[i].Name < converted.Spec.Response[j].Name - }) - for idx := range converted.Spec.Response { - if converted.Spec.Response[idx].Wristband != nil { - sort.Slice(converted.Spec.Response[idx].Wristband.CustomClaims, func(i, j int) bool { - return converted.Spec.Response[idx].Wristband.CustomClaims[i].Name < converted.Spec.Response[idx].Wristband.CustomClaims[j].Name - }) - } - if converted.Spec.Response[idx].JSON != nil { - sort.Slice(converted.Spec.Response[idx].JSON.Properties, func(i, j int) bool { - return converted.Spec.Response[idx].JSON.Properties[i].Name < converted.Spec.Response[idx].JSON.Properties[j].Name - }) - } - } - sort.Slice(converted.Spec.Callbacks, func(i, j int) bool { - return converted.Spec.Callbacks[i].Name < converted.Spec.Callbacks[j].Name - }) - sort.Slice(converted.Spec.DenyWith.Unauthenticated.Headers, func(i, j int) bool { - return converted.Spec.DenyWith.Unauthenticated.Headers[i].Name < converted.Spec.DenyWith.Unauthenticated.Headers[j].Name - }) - sort.Slice(converted.Spec.DenyWith.Unauthorized.Headers, func(i, j int) bool { - return converted.Spec.DenyWith.Unauthorized.Headers[i].Name < converted.Spec.DenyWith.Unauthorized.Headers[j].Name - }) - - expected := authConfig() - if !reflect.DeepEqual(expected, converted) { - t.Error(cmp.Diff(expected, converted)) - } -} - -func hubAuthConfig() *v1beta2.AuthConfig { - authConfig := &v1beta2.AuthConfig{} - err := json.Unmarshal([]byte(` - { - "metadata": { - "name": "auth-config" - }, - "spec": { - "authentication": { - "anonymousAccess": { - "anonymous": {}, - "credentials": { - "authorizationHeader": {} - }, - "priority": 1 - }, - "apiKeyUsers": { - "apiKey": { - "selector": { - "matchLabels": { - "app": "talker-api", - "talker-api/credential-kind": "api-key" - } - } - }, - "credentials": { - "authorizationHeader": { - "prefix": "API-KEY" - } - }, - "overrides": { - "groups": { - "value": [ - "admin" - ] - } - } - }, - "fromEnvoy": { - "credentials": { - "authorizationHeader": {} - }, - "plain": { - "selector": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.jwt_authn|verified_jwt" - }, - "when": [ - { - "operator": "neq", - "selector": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.jwt_authn" - } - ] - }, - "k8sServiceAccountTokens": { - "credentials": { - "authorizationHeader": {} - }, - "kubernetesTokenReview": { - "audiences": [ - "talker-api.default.svc.cluster.local" - ] - } - }, - "mtlsUsers": { - "credentials": { - "authorizationHeader": {} - }, - "x509": { - "selector": { - "matchLabels": { - "app": "talker-api", - "talker-api/credential-kind": "ca-cert" - } - } - } - }, - "oauth2OpaqueTokens": { - "credentials": { - "authorizationHeader": {} - }, - "oauth2Introspection": { - "credentialsRef": { - "name": "oauth2-introspection-credentials" - }, - "endpoint": "https://accounts.company.com/oauth2/v1/introspect" - }, - "overrides": { - "jwtRBAC": { - "value": true - } - } - }, - "oidcServerUsers": { - "credentials": { - "authorizationHeader": {} - }, - "defaults": { - "username": { - "selector": "auth.identity.preferred_username" - } - }, - "jwt": { - "issuerUrl": "https://accounts.company.com", - "ttl": 3600 - }, - "overrides": { - "jwtRBAC": { - "value": true - } - } - } - }, - "authorization": { - "deny20percent": { - "opa": { - "rego": "allow { rand.intn(\"foo\", 100) < 80 }" - }, - "priority": 1 - }, - "externalOpaPolicy": { - "opa": { - "externalPolicy": { - "credentials": { - "authorizationHeader": {} - }, - "ttl": 3600, - "url": "https://raw.githubusercontent.com/repo/authorino-opa/main/allowed-methods.rego" - } - } - }, - "externalSpicedbPolicy": { - "spicedb": { - "endpoint": "spicedb.spicedb.svc.cluster.local:50051", - "insecure": true, - "permission": { - "selector": "context.request.http.method.@replace:{\"old\":\"GET\",\"new\":\"read\"}.@replace:{\"old\":\"POST\",\"new\":\"write\"}" - }, - "resource": { - "kind": { - "value": "blog/post" - }, - "name": { - "selector": "context.request.http.path.@extract:{\"sep\":\"/\",\"pos\":2}" - } - }, - "sharedSecretRef": { - "key": "grpc-preshared-key", - "name": "spicedb" - }, - "subject": { - "kind": { - "value": "blog/user" - }, - "name": { - "selector": "auth.identity.metadata.annotations.username" - } - } - } - }, - "inlineRego": { - "opa": { - "allValues": true, - "rego": "country = object.get(object.get(input.auth.metadata, \"geo-info\", {}), \"country_iso_code\", null)\nallow {\n allowed_countries := [\"ES\", \"FR\", \"IT\"]\n allowed_countries[_] == country\n}\n" - } - }, - "kubernetesRBAC": { - "kubernetesSubjectAccessReview": { - "user": { - "selector": "auth.identity.username" - } - }, - "when": [ - { - "patternRef": "admin-path" - }, - { - "operator": "eq", - "selector": "auth.identity.kubernetes-rbac", - "value": "true" - } - ] - }, - "simplePatternMatching": { - "patternMatching": { - "patterns": [ - { - "operator": "incl", - "selector": "auth.identity.roles", - "value": "admin" - } - ] - }, - "when": [ - { - "patternRef": "admin-path" - }, - { - "operator": "eq", - "selector": "auth.identity.jwtRBAC", - "value": "true" - } - ] - }, - "timestamp": { - "opa": { - "allValues": true, - "rego": "now = time.now_ns() / 1000000000\nallow = true\n" - }, - "priority": 20 - } - }, - "callbacks": { - "telemetry": { - "http": { - "body": { - "selector": "\\{\"requestId\":context.request.http.id,\"username\":\"{auth.identity.username}\",\"authorizationResult\":{auth.authorization}\\}\n" - }, - "contentType": "application/x-www-form-urlencoded", - "credentials": { - "authorizationHeader": {} - }, - "method": "POST", - "oauth2": { - "cache": true, - "clientId": "talker-api", - "clientSecretRef": { - "key": "client-secret", - "name": "talker-api-telemetry-credentials" - }, - "tokenUrl": "https://accounts.company.com/oauth2/v1/token" - }, - "url": "http://telemetry.server" - } - } - }, - "hosts": [ - "talker-api.127.0.0.1.nip.io", - "talker-api.default.svc.cluster.local" - ], - "metadata": { - "geoInfo": { - "cache": { - "key": { - "selector": "context.request.http.headers.x-forwarded-for.@extract:{\"sep\":\",\"}" - }, - "ttl": 3600 - }, - "http": { - "contentType": "application/x-www-form-urlencoded", - "credentials": { - "authorizationHeader": {} - }, - "headers": { - "Accept": { - "value": "application/json" - } - }, - "method": "GET", - "sharedSecretRef": { - "key": "shared-secret", - "name": "ip-location" - }, - "url": "http://ip-location.authorino.svc.cluster.local:3000/{context.request.http.headers.x-forwarded-for.@extract:{\"sep\":\",\"}}" - }, - "metrics": true - }, - "oidcUserInfo": { - "userInfo": { - "identitySource": "oidcServerUsers" - } - }, - "umaResourceInfo": { - "cache": { - "key": { - "selector": "context.request.http.path" - }, - "ttl": 60 - }, - "uma": { - "credentialsRef": { - "name": "talker-api-uma-credentials" - }, - "endpoint": "http://keycloak.authorino.svc.cluster.local:8080/realms/kuadrant" - }, - "when": [ - { - "patternRef": "resourcePath" - } - ] - } - }, - "patterns": { - "adminPath": [ - { - "operator": "matches", - "selector": "context.request.http.path", - "value": "^/admin(/.*)?$" - } - ], - "resourcePath": [ - { - "operator": "matches", - "selector": "context.request.http.path", - "value": "^/greetings/\\d+$" - } - ] - }, - "response": { - "success": { - "dynamicMetadata": { - "username": { - "key": "", - "plain": { - "selector": "auth.identity.username" - } - } - }, - "headers": { - "festival-wristband": { - "key": "x-wristband-token", - "wristband": { - "customClaims": { - "scope": { - "selector": "context.request.http.method.@case:lower" - }, - "uri": { - "selector": "context.request.http.path" - }, - "username": { - "selector": "auth.identity.username" - } - }, - "issuer": "https://authorino-authorino-oidc.authorino.svc.cluster.local:8083/authorino/e2e-test/festival-wristband", - "signingKeyRefs": [ - { - "algorithm": "ES256", - "name": "wristband-signing-key" - } - ], - "tokenDuration": 300 - } - }, - "x-auth-data": { - "json": { - "properties": { - "geo": { - "selector": "auth.metadata.geoInfo" - }, - "timestamp": { - "selector": "auth.authorization.timestamp" - }, - "username": { - "selector": "auth.identity.username" - } - } - }, - "key": "" - }, - "x-auth-service": { - "key": "", - "plain": { - "value": "Authorino" - } - } - } - }, - "unauthenticated": { - "message": { - "value": "Authentication failed" - } - }, - "unauthorized": { - "body": { - "value": "{\n \"kind\": \"Error\",\n \"id\": \"403\",\n \"href\": \"/forbidden\",\n \"code\": \"FORBIDDEN-403\",\n \"reason\": \"Forbidden\"\n}\n" - }, - "headers": { - "content-type": { - "value": "application/json" - }, - "random": { - "selector": "auth.authorization.deny20percent" - } - }, - "message": { - "value": "Access denied" - } - } - }, - "when": [ - { - "operator": "neq", - "selector": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.skipper_lua_filter|skip", - "value": "true" - } - ] - }, - "status": { - "summary": { - "ready": false, - "hostsReady": [], - "numHostsReady": "", - "numIdentitySources": 0, - "numMetadataSources": 0, - "numAuthorizationPolicies": 0, - "numResponseItems": 0, - "festivalWristbandEnabled": false - } - } - }`), &authConfig) - if err != nil { - panic(err) - } - return authConfig -} - -func authConfig() *AuthConfig { - authConfig := &AuthConfig{} - err := json.Unmarshal([]byte(` - { - "metadata": { - "name": "auth-config" - }, - "spec": { - "authorization": [ - { - "metrics": false, - "name": "deny20percent", - "opa": { - "allValues": false, - "inlineRego": "allow { rand.intn(\"foo\", 100) < 80 }" - }, - "priority": 1 - }, - { - "metrics": false, - "name": "externalOpaPolicy", - "opa": { - "allValues": false, - "externalRegistry": { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "endpoint": "https://raw.githubusercontent.com/repo/authorino-opa/main/allowed-methods.rego", - "ttl": 3600 - } - }, - "priority": 0 - }, - { - "authzed": { - "endpoint": "spicedb.spicedb.svc.cluster.local:50051", - "insecure": true, - "permission": { - "valueFrom": { - "authJSON": "context.request.http.method.@replace:{\"old\":\"GET\",\"new\":\"read\"}.@replace:{\"old\":\"POST\",\"new\":\"write\"}" - } - }, - "resource": { - "kind": { - "value": "blog/post", - "valueFrom": {} - }, - "name": { - "valueFrom": { - "authJSON": "context.request.http.path.@extract:{\"sep\":\"/\",\"pos\":2}" - } - } - }, - "sharedSecretRef": { - "key": "grpc-preshared-key", - "name": "spicedb" - }, - "subject": { - "kind": { - "value": "blog/user", - "valueFrom": {} - }, - "name": { - "valueFrom": { - "authJSON": "auth.identity.metadata.annotations.username" - } - } - } - }, - "metrics": false, - "name": "externalSpicedbPolicy", - "priority": 0 - }, - { - "metrics": false, - "name": "inlineRego", - "opa": { - "allValues": true, - "inlineRego": "country = object.get(object.get(input.auth.metadata, \"geo-info\", {}), \"country_iso_code\", null)\nallow {\n allowed_countries := [\"ES\", \"FR\", \"IT\"]\n allowed_countries[_] == country\n}\n" - }, - "priority": 0 - }, - { - "kubernetes": { - "user": { - "valueFrom": { - "authJSON": "auth.identity.username" - } - } - }, - "metrics": false, - "name": "kubernetesRBAC", - "priority": 0, - "when": [ - { - "patternRef": "admin-path" - }, - { - "operator": "eq", - "selector": "auth.identity.kubernetes-rbac", - "value": "true" - } - ] - }, - { - "json": { - "rules": [ - { - "operator": "incl", - "selector": "auth.identity.roles", - "value": "admin" - } - ] - }, - "metrics": false, - "name": "simplePatternMatching", - "priority": 0, - "when": [ - { - "patternRef": "admin-path" - }, - { - "operator": "eq", - "selector": "auth.identity.jwtRBAC", - "value": "true" - } - ] - }, - { - "metrics": false, - "name": "timestamp", - "opa": { - "allValues": true, - "inlineRego": "now = time.now_ns() / 1000000000\nallow = true\n" - }, - "priority": 20 - } - ], - "callbacks": [ - { - "http": { - "body": { - "valueFrom": { - "authJSON": "\\{\"requestId\":context.request.http.id,\"username\":\"{auth.identity.username}\",\"authorizationResult\":{auth.authorization}\\}\n" - } - }, - "contentType": "application/x-www-form-urlencoded", - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "endpoint": "http://telemetry.server", - "method": "POST", - "oauth2": { - "cache": true, - "clientId": "talker-api", - "clientSecretRef": { - "key": "client-secret", - "name": "talker-api-telemetry-credentials" - }, - "tokenUrl": "https://accounts.company.com/oauth2/v1/token" - } - }, - "metrics": false, - "name": "telemetry", - "priority": 0 - } - ], - "denyWith": { - "unauthenticated": { - "message": { - "value": "Authentication failed", - "valueFrom": {} - } - }, - "unauthorized": { - "body": { - "value": "{\n \"kind\": \"Error\",\n \"id\": \"403\",\n \"href\": \"/forbidden\",\n \"code\": \"FORBIDDEN-403\",\n \"reason\": \"Forbidden\"\n}\n" - }, - "headers": [ - { - "name": "content-type", - "value": "application/json", - "valueFrom": {} - }, - { - "name": "random", - "valueFrom": { - "authJSON": "auth.authorization.deny20percent" - } - } - ], - "message": { - "value": "Access denied", - "valueFrom": {} - } - } - }, - "hosts": [ - "talker-api.127.0.0.1.nip.io", - "talker-api.default.svc.cluster.local" - ], - "identity": [ - { - "anonymous": {}, - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "metrics": false, - "name": "anonymousAccess", - "priority": 1 - }, - { - "apiKey": { - "allNamespaces": false, - "selector": { - "matchLabels": { - "app": "talker-api", - "talker-api/credential-kind": "api-key" - } - } - }, - "credentials": { - "in": "authorization_header", - "keySelector": "API-KEY" - }, - "extendedProperties": [ - { - "name": "groups", - "overwrite": true, - "value": [ - "admin" - ], - "valueFrom": {} - } - ], - "metrics": false, - "name": "apiKeyUsers", - "priority": 0 - }, - { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "metrics": false, - "name": "fromEnvoy", - "plain": { - "authJSON": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.jwt_authn|verified_jwt" - }, - "priority": 0, - "when": [ - { - "operator": "neq", - "selector": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.jwt_authn" - } - ] - }, - { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "kubernetes": { - "audiences": [ - "talker-api.default.svc.cluster.local" - ] - }, - "metrics": false, - "name": "k8sServiceAccountTokens", - "priority": 0 - }, - { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "metrics": false, - "mtls": { - "allNamespaces": false, - "selector": { - "matchLabels": { - "app": "talker-api", - "talker-api/credential-kind": "ca-cert" - } - } - }, - "name": "mtlsUsers", - "priority": 0 - }, - { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "extendedProperties": [ - { - "name": "jwtRBAC", - "overwrite": true, - "value": true, - "valueFrom": {} - } - ], - "metrics": false, - "name": "oauth2OpaqueTokens", - "oauth2": { - "credentialsRef": { - "name": "oauth2-introspection-credentials" - }, - "tokenIntrospectionUrl": "https://accounts.company.com/oauth2/v1/introspect" - }, - "priority": 0 - }, - { - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "extendedProperties": [ - { - "name": "jwtRBAC", - "overwrite": true, - "value": true, - "valueFrom": {} - }, - { - "name": "username", - "overwrite": false, - "valueFrom": { - "authJSON": "auth.identity.preferred_username" - } - } - ], - "metrics": false, - "name": "oidcServerUsers", - "oidc": { - "endpoint": "https://accounts.company.com", - "ttl": 3600 - }, - "priority": 0 - } - ], - "metadata": [ - { - "cache": { - "key": { - "valueFrom": { - "authJSON": "context.request.http.headers.x-forwarded-for.@extract:{\"sep\":\",\"}" - } - }, - "ttl": 3600 - }, - "http": { - "contentType": "application/x-www-form-urlencoded", - "credentials": { - "in": "authorization_header", - "keySelector": "" - }, - "endpoint": "http://ip-location.authorino.svc.cluster.local:3000/{context.request.http.headers.x-forwarded-for.@extract:{\"sep\":\",\"}}", - "headers": [ - { - "name": "Accept", - "value": "application/json", - "valueFrom": {} - } - ], - "method": "GET", - "sharedSecretRef": { - "key": "shared-secret", - "name": "ip-location" - } - }, - "metrics": true, - "name": "geoInfo", - "priority": 0 - }, - { - "metrics": false, - "name": "oidcUserInfo", - "priority": 0, - "userInfo": { - "identitySource": "oidcServerUsers" - } - }, - { - "cache": { - "key": { - "valueFrom": { - "authJSON": "context.request.http.path" - } - }, - "ttl": 60 - }, - "metrics": false, - "name": "umaResourceInfo", - "priority": 0, - "uma": { - "credentialsRef": { - "name": "talker-api-uma-credentials" - }, - "endpoint": "http://keycloak.authorino.svc.cluster.local:8080/realms/kuadrant" - }, - "when": [ - { - "patternRef": "resourcePath" - } - ] - } - ], - "patterns": { - "adminPath": [ - { - "operator": "matches", - "selector": "context.request.http.path", - "value": "^/admin(/.*)?$" - } - ], - "resourcePath": [ - { - "operator": "matches", - "selector": "context.request.http.path", - "value": "^/greetings/\\d+$" - } - ] - }, - "response": [ - { - "metrics": false, - "name": "festival-wristband", - "priority": 0, - "wrapper": "httpHeader", - "wrapperKey": "x-wristband-token", - "wristband": { - "customClaims": [ - { - "name": "scope", - "valueFrom": { - "authJSON": "context.request.http.method.@case:lower" - } - }, - { - "name": "uri", - "valueFrom": { - "authJSON": "context.request.http.path" - } - }, - { - "name": "username", - "valueFrom": { - "authJSON": "auth.identity.username" - } - } - ], - "issuer": "https://authorino-authorino-oidc.authorino.svc.cluster.local:8083/authorino/e2e-test/festival-wristband", - "signingKeyRefs": [ - { - "algorithm": "ES256", - "name": "wristband-signing-key" - } - ], - "tokenDuration": 300 - } - }, - { - "metrics": false, - "name": "username", - "plain": { - "valueFrom": { - "authJSON": "auth.identity.username" - } - }, - "priority": 0, - "wrapper": "envoyDynamicMetadata", - "wrapperKey": "" - }, - { - "json": { - "properties": [ - { - "name": "geo", - "valueFrom": { - "authJSON": "auth.metadata.geoInfo" - } - }, - { - "name": "timestamp", - "valueFrom": { - "authJSON": "auth.authorization.timestamp" - } - }, - { - "name": "username", - "valueFrom": { - "authJSON": "auth.identity.username" - } - } - ] - }, - "metrics": false, - "name": "x-auth-data", - "priority": 0, - "wrapper": "httpHeader", - "wrapperKey": "" - }, - { - "metrics": false, - "name": "x-auth-service", - "plain": { - "value": "Authorino", - "valueFrom": {} - }, - "priority": 0, - "wrapper": "httpHeader", - "wrapperKey": "" - } - ], - "when": [ - { - "operator": "neq", - "selector": "context.metadata_context.filter_metadata.envoy\\.filters\\.http\\.skipper_lua_filter|skip", - "value": "true" - } - ] - }, - "status": { - "summary": { - "ready": false, - "hostsReady": [], - "numHostsReady": "", - "numIdentitySources": 0, - "numMetadataSources": 0, - "numAuthorizationPolicies": 0, - "numResponseItems": 0, - "festivalWristbandEnabled": false - } - } - }`), &authConfig) - if err != nil { - panic(err) - } - return authConfig -} diff --git a/api/v1beta1/auth_config_types.go b/api/v1beta1/auth_config_types.go deleted file mode 100644 index 546272ee..00000000 --- a/api/v1beta1/auth_config_types.go +++ /dev/null @@ -1,827 +0,0 @@ -/* -Copyright 2020 Red Hat, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta1 - -import ( - k8score "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -const ( - TypeUnknown = "UNKNOWN" - IdentityOAuth2 = "IDENTITY_OAUTH2" - IdentityOidc = "IDENTITY_OIDC" - IdentityApiKey = "IDENTITY_APIKEY" - IdentityMTLS = "IDENTITY_MTLS" - IdentityKubernetesAuth = "IDENTITY_KUBERNETESAUTH" - IdentityAnonymous = "IDENTITY_ANONYMOUS" - IdentityPlain = "IDENTITY_PLAIN" - MetadataUma = "METADATA_UMA" - MetadataGenericHTTP = "METADATA_GENERIC_HTTP" - MetadataUserinfo = "METADATA_USERINFO" - AuthorizationOPA = "AUTHORIZATION_OPA" - AuthorizationJSONPatternMatching = "AUTHORIZATION_JSON" - AuthorizationKubernetesAuthz = "AUTHORIZATION_KUBERNETESAUTHZ" - AuthorizationAuthzed = "AUTHORIZATION_AUTHZED" - ResponseWristband = "RESPONSE_WRISTBAND" - ResponseDynamicJSON = "RESPONSE_DYNAMIC_JSON" - ResponsePlain = "RESPONSE_PLAIN" - CallbackHTTP = "CALLBACK_HTTP" - EvaluatorDefaultCacheTTL = 60 - - // Status conditions - StatusConditionAvailable ConditionType = "Available" - StatusConditionReady ConditionType = "Ready" - - // Status reasons - StatusReasonReconciling string = "Reconciling" - StatusReasonReconciled string = "Reconciled" - StatusReasonInvalidResource string = "Invalid" - StatusReasonHostsLinked string = "HostsLinked" - StatusReasonHostsNotLinked string = "HostsNotLinked" - StatusReasonCachingError string = "CachingError" - StatusReasonUnknown string = "Unknown" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// SecretKeyReference selects a key of a Secret. -type SecretKeyReference struct { - // The name of the secret in the Authorino's namespace to select from. - Name string `json:"name"` - - // The key of the secret to select from. Must be a valid secret key. - Key string `json:"key"` -} - -// StaticOrDynamicValue is either a constant static string value or a config for fetching a value from a dynamic source (e.g. a path pattern of authorization JSON) -type StaticOrDynamicValue struct { - // Static value - Value string `json:"value,omitempty"` - // Dynamic value - ValueFrom ValueFrom `json:"valueFrom,omitempty"` -} - -type ValueFrom struct { - // Selector to fetch a value from the authorization JSON. - // It can be any path pattern to fetch from the authorization JSON (e.g. 'context.request.http.host') - // or a string template with variable placeholders that resolve to patterns (e.g. "Hello, {auth.identity.name}!"). - // Any patterns supported by https://pkg.go.dev/github.com/tidwall/gjson can be used. - // The following string modifiers are available: @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, @base64:encode|decode and @strip. - AuthJSON string `json:"authJSON,omitempty"` -} - -type JsonProperty struct { - // The name of the JSON property - Name string `json:"name"` - // Static value of the JSON property - // +kubebuilder:validation:Schemaless - // +kubebuilder:pruning:PreserveUnknownFields - Value runtime.RawExtension `json:"value,omitempty"` - // Dynamic value of the JSON property - ValueFrom ValueFrom `json:"valueFrom,omitempty"` -} - -type EvaluatorCaching struct { - // Key used to store the entry in the cache. - // Cache entries from different metadata configs are stored and managed separately regardless of the key. - Key StaticOrDynamicValue `json:"key"` - // Duration (in seconds) of the external data in the cache before pulled again from the source. - // +kubebuilder:default:=60 - TTL int `json:"ttl,omitempty"` -} - -// Specifies the desired state of the AuthConfig resource, i.e. the authencation/authorization scheme to be applied to protect the matching service hosts. -type AuthConfigSpec struct { - // Important: Run "make" to regenerate code after modifying this file - - // The list of public host names of the services protected by this authentication/authorization scheme. - // Authorino uses the requested host to lookup for the corresponding authentication/authorization configs to enforce. - Hosts []string `json:"hosts"` - - // Named sets of JSON patterns that can be referred in `when` conditionals and in JSON-pattern matching policy rules. - Patterns map[string]JSONPatternExpressions `json:"patterns,omitempty"` - - // Conditions for the AuthConfig to be enforced. - // If omitted, the AuthConfig will be enforced for all requests. - // If present, all conditions must match for the AuthConfig to be enforced; otherwise, Authorino skips the AuthConfig and returns immediately with status OK. - Conditions []JSONPattern `json:"when,omitempty"` - - // List of identity sources/authentication modes. - // At least one config of this list MUST evaluate to a valid identity for a request to be successful in the identity verification phase. - Identity []*Identity `json:"identity,omitempty"` - - // List of metadata source configs. - // Authorino fetches JSON content from sources on this list on every request. - Metadata []*Metadata `json:"metadata,omitempty"` - - // Authorization is the list of authorization policies. - // All policies in this list MUST evaluate to "true" for a request be successful in the authorization phase. - Authorization []*Authorization `json:"authorization,omitempty"` - - // List of response configs. - // Authorino gathers data from the auth pipeline to build custom responses for the client. - Response []*Response `json:"response,omitempty"` - - // List of callback configs. - // Authorino sends callbacks to specified endpoints at the end of the auth pipeline. - Callbacks []*Callback `json:"callbacks,omitempty"` - - // Custom denial response codes, statuses and headers to override default 40x's. - DenyWith *DenyWith `json:"denyWith,omitempty"` -} - -type JSONPattern struct { - JSONPatternRef `json:",omitempty"` - JSONPatternExpression `json:",omitempty"` - - // A list of pattern expressions to be evaluated as a logical AND. - All []UnstructuredJSONPattern `json:"all,omitempty"` - // A list of pattern expressions to be evaluated as a logical OR. - Any []UnstructuredJSONPattern `json:"any,omitempty"` -} - -type UnstructuredJSONPattern struct { - // +kubebuilder:pruning:PreserveUnknownFields - JSONPattern `json:",omitempty"` -} - -type JSONPatternRef struct { - // Name of a named pattern - JSONPatternName string `json:"patternRef,omitempty"` -} - -type JSONPatternExpressions []JSONPatternExpression - -type JSONPatternExpression struct { - // Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. - // The value is used to fetch content from the input authorization JSON built by Authorino along the identity and metadata phases. - Selector string `json:"selector,omitempty"` - // The binary operator to be applied to the content fetched from the authorization JSON, for comparison with "value". - // Possible values are: "eq" (equal to), "neq" (not equal to), "incl" (includes; for arrays), "excl" (excludes; for arrays), "matches" (regex) - Operator JSONPatternOperator `json:"operator,omitempty"` - // The value of reference for the comparison with the content fetched from the authorization JSON. - // If used with the "matches" operator, the value must compile to a valid Golang regex. - Value string `json:"value,omitempty"` -} - -// +kubebuilder:validation:Enum:=eq;neq;incl;excl;matches -type JSONPatternOperator string - -// +kubebuilder:validation:Enum:=authorization_header;custom_header;query;cookie -type Credentials_In string - -type Credentials struct { - // The location in the request where client credentials shall be passed on requests authenticating with this identity source/authentication mode. - // +kubebuilder:default:=authorization_header - In Credentials_In `json:"in,omitempty"` - // Used in conjunction with the `in` parameter. - // When used with `authorization_header`, the value is the prefix of the client credentials string, separated by a white-space, in the HTTP Authorization header (e.g. "Bearer", "Basic"). - // When used with `custom_header`, `query` or `cookie`, the value is the name of the HTTP header, query string parameter or cookie key, respectively. - KeySelector string `json:"keySelector"` -} - -type ExtendedProperty struct { - JsonProperty `json:",omitempty"` - // Whether the value should overwrite the value of an existing property with the same name. - // +kubebuilder:default:=false - Overwrite bool `json:"overwrite,omitempty"` -} - -// The identity source/authentication mode config. -// Apart from "name", one of the following parameters is required and only one of the following parameters is allowed: "oicd", "apiKey" or "kubernetes". -type Identity struct { - // The name of this identity source/authentication mode. - // It usually identifies a source of identities or group of users/clients of the protected service. - // It can be used to refer to the resolved identity object in other configs. - Name string `json:"name"` - - // Priority group of the config. - // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. - // +kubebuilder:default:=0 - Priority int `json:"priority,omitempty"` - - // Whether this identity config should generate individual observability metrics - // +kubebuilder:default:=false - Metrics bool `json:"metrics,omitempty"` - - // Conditions for Authorino to enforce this identity config. - // If omitted, the config will be enforced for all requests. - // If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. - Conditions []JSONPattern `json:"when,omitempty"` - - // Caching options for the identity resolved when applying this config. - // Omit it to avoid caching identity objects for this config. - Cache *EvaluatorCaching `json:"cache,omitempty"` - - // Defines where client credentials are required to be passed in the request for this identity source/authentication mode. - // If omitted, it defaults to client credentials passed in the HTTP Authorization header and the "Bearer" prefix expected prepended to the credentials value (token, API key, etc). - Credentials Credentials `json:"credentials,omitempty"` - - // Extends the resolved identity object with additional custom properties before appending to the authorization JSON. - // It requires the resolved identity object to always be of the JSON type 'object'. Other JSON types (array, string, etc) will break. - ExtendedProperties []ExtendedProperty `json:"extendedProperties,omitempty"` - - OAuth2 *Identity_OAuth2Config `json:"oauth2,omitempty"` - Oidc *Identity_OidcConfig `json:"oidc,omitempty"` - APIKey *Identity_APIKey `json:"apiKey,omitempty"` - MTLS *Identity_MTLS `json:"mtls,omitempty"` - KubernetesAuth *Identity_KubernetesAuth `json:"kubernetes,omitempty"` - Anonymous *Identity_Anonymous `json:"anonymous,omitempty"` - Plain *Identity_Plain `json:"plain,omitempty"` -} - -func (i *Identity) GetType() string { - if i.OAuth2 != nil { - return IdentityOAuth2 - } else if i.Oidc != nil { - return IdentityOidc - } else if i.APIKey != nil { - return IdentityApiKey - } else if i.MTLS != nil { - return IdentityMTLS - } else if i.KubernetesAuth != nil { - return IdentityKubernetesAuth - } else if i.Anonymous != nil { - return IdentityAnonymous - } else if i.Plain != nil { - return IdentityPlain - } else { - return TypeUnknown - } -} - -type Identity_OAuth2Config struct { - // The full URL of the token introspection endpoint. - TokenIntrospectionUrl string `json:"tokenIntrospectionUrl"` - // The token type hint for the token introspection. - // If omitted, it defaults to "access_token". - TokenTypeHint string `json:"tokenTypeHint,omitempty"` - - // Reference to a Kubernetes secret in the same namespace, that stores client credentials to the OAuth2 server. - Credentials *k8score.LocalObjectReference `json:"credentialsRef"` -} - -type Identity_OidcConfig struct { - // Endpoint of the OIDC issuer. - // Authorino will append to this value the well-known path to the OpenID Connect discovery endpoint (i.e. "/.well-known/openid-configuration"), used to automatically discover the OpenID Connect configuration, whose set of claims is expected to include (among others) the "jkws_uri" claim. - // The value must coincide with the value of the "iss" (issuer) claim of the discovered OpenID Connect configuration. - Endpoint string `json:"endpoint"` - // Decides how long to wait before refreshing the OIDC configuration (in seconds). - TTL int `json:"ttl,omitempty"` -} - -type Identity_APIKey struct { - // Label selector used by Authorino to match secrets from the cluster storing valid credentials to authenticate to this service - Selector *metav1.LabelSelector `json:"selector"` - - // Whether Authorino should look for API key secrets in all namespaces or only in the same namespace as the AuthConfig. - // Enabling this option in namespaced Authorino instances has no effect. - // +kubebuilder:default:=false - AllNamespaces bool `json:"allNamespaces,omitempty"` -} - -type Identity_MTLS struct { - // Label selector used by Authorino to match secrets from the cluster storing trusted CA certificates to validate clients trying to authenticate to this service - Selector *metav1.LabelSelector `json:"selector"` - - // Whether Authorino should look for TLS secrets in all namespaces or only in the same namespace as the AuthConfig. - // Enabling this option in namespaced Authorino instances has no effect. - // +kubebuilder:default:=false - AllNamespaces bool `json:"allNamespaces,omitempty"` -} - -type Identity_KubernetesAuth struct { - // The list of audiences (scopes) that must be claimed in a Kubernetes authentication token supplied in the request, and reviewed by Authorino. - // If omitted, Authorino will review tokens expecting the host name of the requested protected service amongst the audiences. - Audiences []string `json:"audiences,omitempty"` -} - -type Identity_Anonymous struct{} - -type Identity_Plain ValueFrom - -// The metadata config. -// Apart from "name", one of the following parameters is required and only one of the following parameters is allowed: "http", userInfo" or "uma". -type Metadata struct { - // The name of the metadata source. - // It can be used to refer to the resolved metadata object in other configs. - Name string `json:"name"` - - // Priority group of the config. - // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. - // +kubebuilder:default:=0 - Priority int `json:"priority,omitempty"` - - // Whether this metadata config should generate individual observability metrics - // +kubebuilder:default:=false - Metrics bool `json:"metrics,omitempty"` - - // Conditions for Authorino to apply this metadata config. - // If omitted, the config will be applied for all requests. - // If present, all conditions must match for the config to be applied; otherwise, the config will be skipped. - Conditions []JSONPattern `json:"when,omitempty"` - - // Caching options for the external metadata fetched when applying this config. - // Omit it to avoid caching metadata from this source. - Cache *EvaluatorCaching `json:"cache,omitempty"` - - UserInfo *Metadata_UserInfo `json:"userInfo,omitempty"` - UMA *Metadata_UMA `json:"uma,omitempty"` - GenericHTTP *Metadata_GenericHTTP `json:"http,omitempty"` -} - -func (m *Metadata) GetType() string { - if m.UserInfo != nil { - return MetadataUserinfo - } else if m.UMA != nil { - return MetadataUma - } else if m.GenericHTTP != nil { - return MetadataGenericHTTP - } - return TypeUnknown -} - -// OpendID Connect UserInfo linked to an OIDC identity config of this same spec. -type Metadata_UserInfo struct { - // The name of an OIDC identity source included in the "identity" section and whose OpenID Connect configuration discovered includes the OIDC "userinfo_endpoint" claim. - IdentitySource string `json:"identitySource"` -} - -// User-Managed Access (UMA) source of resource data. -type Metadata_UMA struct { - // The endpoint of the UMA server. - // The value must coincide with the "issuer" claim of the UMA config discovered from the well-known uma configuration endpoint. - Endpoint string `json:"endpoint"` - - // Reference to a Kubernetes secret in the same namespace, that stores client credentials to the resource registration API of the UMA server. - Credentials *k8score.LocalObjectReference `json:"credentialsRef"` -} - -// +kubebuilder:validation:Enum:=GET;POST -type GenericHTTP_Method string - -// +kubebuilder:validation:Enum:=application/x-www-form-urlencoded;application/json -type Metadata_GenericHTTP_ContentType string - -// Generic HTTP interface to obtain authorization metadata from a HTTP service. -type Metadata_GenericHTTP struct { - // Endpoint of the HTTP service. - // The endpoint accepts variable placeholders in the format "{selector}", where "selector" is any pattern supported - // by https://pkg.go.dev/github.com/tidwall/gjson and selects value from the authorization JSON. - // E.g. https://ext-auth-server.io/metadata?p={context.request.http.path} - Endpoint string `json:"endpoint"` - - // HTTP verb used in the request to the service. Accepted values: GET (default), POST. - // When the request method is POST, the authorization JSON is passed in the body of the request. - // +kubebuilder:default:=GET - Method *GenericHTTP_Method `json:"method,omitempty"` - - // Raw body of the HTTP request. - // Supersedes 'bodyParameters'; use either one or the other. - // Use it with method=POST; for GET requests, set parameters as query string in the 'endpoint' (placeholders can be used). - Body *StaticOrDynamicValue `json:"body,omitempty"` - - // Custom parameters to encode in the body of the HTTP request. - // Superseded by 'body'; use either one or the other. - // Use it with method=POST; for GET requests, set parameters as query string in the 'endpoint' (placeholders can be used). - Parameters []JsonProperty `json:"bodyParameters,omitempty"` - - // Content-Type of the request body. Shapes how 'bodyParameters' are encoded. - // Use it with method=POST; for GET requests, Content-Type is automatically set to 'text/plain'. - // +kubebuilder:default:=application/x-www-form-urlencoded - ContentType Metadata_GenericHTTP_ContentType `json:"contentType,omitempty"` - - // Custom headers in the HTTP request. - Headers []JsonProperty `json:"headers,omitempty"` - - // Reference to a Secret key whose value will be passed by Authorino in the request. - // The HTTP service can use the shared secret to authenticate the origin of the request. - // Ignored if used together with oauth2. - SharedSecret *SecretKeyReference `json:"sharedSecretRef,omitempty"` - - // Authentication with the HTTP service by OAuth2 Client Credentials grant. - OAuth2 *OAuth2ClientAuthentication `json:"oauth2,omitempty"` - - // Defines where client credentials will be passed in the request to the service. - // If omitted, it defaults to client credentials passed in the HTTP Authorization header and the "Bearer" prefix expected prepended to the secret value. - Credentials Credentials `json:"credentials,omitempty"` -} - -type OAuth2ClientAuthentication struct { - // Token endpoint URL of the OAuth2 resource server. - TokenUrl string `json:"tokenUrl"` - // OAuth2 Client ID. - ClientId string `json:"clientId"` - // Reference to a Kubernetes Secret key that stores that OAuth2 Client Secret. - ClientSecret SecretKeyReference `json:"clientSecretRef"` - // Optional scopes for the client credentials grant, if supported by he OAuth2 server. - Scopes []string `json:"scopes,omitempty"` - // Optional extra parameters for the requests to the token URL. - ExtraParams map[string]string `json:"extraParams,omitempty"` - // Caches and reuses the token until expired. - // Set it to false to force fetch the token at every authorization request regardless of expiration. - // +kubebuilder:default:=true - Cache *bool `json:"cache,omitempty"` -} - -// Authorization policy to be enforced. -// Apart from "name", one of the following parameters is required and only one of the following parameters is allowed: "opa", "json" or "kubernetes". -type Authorization struct { - // Name of the authorization policy. - // It can be used to refer to the resolved authorization object in other configs. - Name string `json:"name"` - - // Priority group of the config. - // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. - // +kubebuilder:default:=0 - Priority int `json:"priority,omitempty"` - - // Whether this authorization config should generate individual observability metrics - // +kubebuilder:default:=false - Metrics bool `json:"metrics,omitempty"` - - // Conditions for Authorino to enforce this authorization policy. - // If omitted, the config will be enforced for all requests. - // If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. - Conditions []JSONPattern `json:"when,omitempty"` - - // Caching options for the policy evaluation results when enforcing this config. - // Omit it to avoid caching policy evaluation results for this config. - Cache *EvaluatorCaching `json:"cache,omitempty"` - - OPA *Authorization_OPA `json:"opa,omitempty"` - JSON *Authorization_JSONPatternMatching `json:"json,omitempty"` - KubernetesAuthz *Authorization_KubernetesAuthz `json:"kubernetes,omitempty"` - Authzed *Authorization_Authzed `json:"authzed,omitempty"` -} - -func (a *Authorization) GetType() string { - if a.OPA != nil { - return AuthorizationOPA - } else if a.JSON != nil { - return AuthorizationJSONPatternMatching - } else if a.KubernetesAuthz != nil { - return AuthorizationKubernetesAuthz - } else if a.Authzed != nil { - return AuthorizationAuthzed - } - return TypeUnknown -} - -// ExternalRegistry specifies external source of data (i.e. OPA policy registry) -type ExternalRegistry struct { - // Endpoint of the HTTP external registry. - // The endpoint must respond with either plain/text or application/json content-type. - // In the latter case, the JSON returned in the body must include a path `result.raw`, where the raw Rego policy will be extracted from. This complies with the specification of the OPA REST API (https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-policy). - Endpoint string `json:"endpoint,omitempty"` - - // Reference to a Secret key whose value will be passed by Authorino in the request. - // The HTTP service can use the shared secret to authenticate the origin of the request. - SharedSecret *SecretKeyReference `json:"sharedSecretRef,omitempty"` - - // Defines where client credentials will be passed in the request to the service. - // If omitted, it defaults to client credentials passed in the HTTP Authorization header and the "Bearer" prefix expected prepended to the secret value. - Credentials Credentials `json:"credentials,omitempty"` - - // Duration (in seconds) of the external data in the cache before pulled again from the source. - TTL int `json:"ttl,omitempty"` -} - -// Open Policy Agent (OPA) authorization policy. -type Authorization_OPA struct { - // Authorization policy as a Rego language document. - // The Rego document must include the "allow" condition, set by Authorino to "false" by default (i.e. requests are unauthorized unless changed). - // The Rego document must NOT include the "package" declaration in line 1. - InlineRego string `json:"inlineRego,omitempty"` - - // External registry of OPA policies. - ExternalRegistry ExternalRegistry `json:"externalRegistry,omitempty"` - - // Returns the value of all Rego rules in the virtual document. Values can be read in subsequent evaluators/phases of the Auth Pipeline. - // Otherwise, only the default `allow` rule will be exposed. - // Returning all Rego rules can affect performance of OPA policies during reconciliation (policy precompile) and at runtime. - // +kubebuilder:default:=false - AllValues bool `json:"allValues,omitempty"` -} - -// JSON pattern matching authorization policy. -type Authorization_JSONPatternMatching struct { - // The rules that must all evaluate to "true" for the request to be authorized. - Rules []JSONPattern `json:"rules"` -} - -type Authorization_KubernetesAuthz_ResourceAttributes struct { - Namespace StaticOrDynamicValue `json:"namespace,omitempty"` - Group StaticOrDynamicValue `json:"group,omitempty"` - Resource StaticOrDynamicValue `json:"resource,omitempty"` - Name StaticOrDynamicValue `json:"name,omitempty"` - SubResource StaticOrDynamicValue `json:"subresource,omitempty"` - Verb StaticOrDynamicValue `json:"verb,omitempty"` -} - -// Kubernetes authorization policy based on `SubjectAccessReview` -// Path and Verb are inferred from the request. -type Authorization_KubernetesAuthz struct { - // User to test for. - // If without "Groups", then is it interpreted as "What if User were not a member of any groups" - User StaticOrDynamicValue `json:"user"` - - // Groups to test for. - Groups []string `json:"groups,omitempty"` - - // Use ResourceAttributes for checking permissions on Kubernetes resources - // If omitted, it performs a non-resource `SubjectAccessReview`, with verb and path inferred from the request. - ResourceAttributes *Authorization_KubernetesAuthz_ResourceAttributes `json:"resourceAttributes,omitempty"` -} - -// Authzed authorization -type Authorization_Authzed struct { - // Endpoint of the Authzed service. - Endpoint string `json:"endpoint"` - - // Insecure HTTP connection (i.e. disables TLS verification) - Insecure bool `json:"insecure,omitempty"` - - // Reference to a Secret key whose value will be used by Authorino to authenticate with the Authzed service. - SharedSecret *SecretKeyReference `json:"sharedSecretRef,omitempty"` - - // The subject that will be checked for the permission or relation. - Subject *AuthzedObject `json:"subject,omitempty"` - // The resource on which to check the permission or relation. - Resource *AuthzedObject `json:"resource,omitempty"` - // The name of the permission (or relation) on which to execute the check. - Permission StaticOrDynamicValue `json:"permission,omitempty"` -} - -type AuthzedObject struct { - Name StaticOrDynamicValue `json:"name,omitempty"` - Kind StaticOrDynamicValue `json:"kind,omitempty"` -} - -// +kubebuilder:validation:Enum:=httpHeader;envoyDynamicMetadata -type Response_Wrapper string - -// Dynamic response to return to the client. -// Apart from "name", one of the following parameters is required and only one of the following parameters is allowed: "wristband" or "json". -type Response struct { - // Name of the custom response. - // It can be used to refer to the resolved response object in other configs. - Name string `json:"name"` - - // Priority group of the config. - // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. - // +kubebuilder:default:=0 - Priority int `json:"priority,omitempty"` - - // Whether this response config should generate individual observability metrics - // +kubebuilder:default:=false - Metrics bool `json:"metrics,omitempty"` - - // Conditions for Authorino to enforce this custom response config. - // If omitted, the config will be enforced for all requests. - // If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. - Conditions []JSONPattern `json:"when,omitempty"` - - // Caching options for dynamic responses built when applying this config. - // Omit it to avoid caching dynamic responses for this config. - Cache *EvaluatorCaching `json:"cache,omitempty"` - - // How Authorino wraps the response. - // Use "httpHeader" (default) to wrap the response in an HTTP header; or "envoyDynamicMetadata" to wrap the response as Envoy Dynamic Metadata - // +kubebuilder:default:=httpHeader - Wrapper Response_Wrapper `json:"wrapper,omitempty"` - // The name of key used in the wrapped response (name of the HTTP header or property of the Envoy Dynamic Metadata JSON). - // If omitted, it will be set to the name of the configuration. - WrapperKey string `json:"wrapperKey,omitempty"` - - Wristband *Response_Wristband `json:"wristband,omitempty"` - JSON *Response_DynamicJSON `json:"json,omitempty"` - Plain *Response_Plain `json:"plain,omitempty"` -} - -func (r *Response) GetType() string { - if r.Wristband != nil { - return ResponseWristband - } else if r.JSON != nil { - return ResponseDynamicJSON - } else if r.Plain != nil { - return ResponsePlain - } - return TypeUnknown -} - -// Endpoints to callback at the end of each auth pipeline. -type Callback struct { - // Name of the callback. - // It can be used to refer to the resolved callback response in other configs. - Name string `json:"name"` - - // Priority group of the config. - // All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. - // +kubebuilder:default:=0 - Priority int `json:"priority,omitempty"` - - // Whether this callback config should generate individual observability metrics - // +kubebuilder:default:=false - Metrics bool `json:"metrics,omitempty"` - - // Conditions for Authorino to perform this callback. - // If omitted, the callback will be attempted for all requests. - // If present, all conditions must match for the callback to be attempted; otherwise, the callback will be skipped. - Conditions []JSONPattern `json:"when,omitempty"` - - HTTP *Metadata_GenericHTTP `json:"http"` // make this 'omitempty' if other alternate methods are added -} - -func (r *Callback) GetType() string { - if r.HTTP != nil { - return CallbackHTTP - } - return TypeUnknown -} - -// +kubebuilder:validation:Enum:=ES256;ES384;ES512;RS256;RS384;RS512 -type SigningKeyAlgorithm string - -type SigningKeyRef struct { - // Name of the signing key. - // The value is used to reference the Kubernetes secret that stores the key and in the `kid` claim of the wristband token header. - Name string `json:"name"` - - // Algorithm to sign the wristband token using the signing key provided - Algorithm SigningKeyAlgorithm `json:"algorithm"` -} - -type Response_Wristband struct { - // The endpoint to the Authorino service that issues the wristband (format: ://:/, where = /://:/, - where = /://:/, - where = / Date: Tue, 1 Oct 2024 12:12:49 +0100 Subject: [PATCH 3/5] refactor: oneOf for v1beta3 Signed-off-by: KevFan --- install/crd/patches/oneof_in_authconfigs.yaml | 214 ++++++++++++++++ install/manifests.yaml | 232 ++++++++++++++++++ 2 files changed, 446 insertions(+) diff --git a/install/crd/patches/oneof_in_authconfigs.yaml b/install/crd/patches/oneof_in_authconfigs.yaml index bfd752de..919f6e83 100644 --- a/install/crd/patches/oneof_in_authconfigs.yaml +++ b/install/crd/patches/oneof_in_authconfigs.yaml @@ -1,6 +1,220 @@ # Enables oneOf validation for the identity/authentication, metadata, authorization, and response fields. # v1beta2 +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/authentication/additionalProperties/oneOf + value: + - properties: + credentials: {} + oauth2Introspection: {} + required: [oauth2Introspection] + - properties: + credentials: {} + jwt: {} + required: [jwt] + - properties: + credentials: {} + apiKey: {} + required: [apiKey] + - properties: + credentials: {} + x509: {} + required: [x509] + - properties: + credentials: {} + kubernetesTokenReview: {} + required: [kubernetesTokenReview] + - properties: + credentials: {} + anonymous: {} + required: [anonymous] + - properties: + credentials: {} + plain: {} + required: [plain] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/metadata/additionalProperties/oneOf + value: + - properties: + userInfo: {} + required: [userInfo] + - properties: + uma: {} + required: [uma] + - properties: + http: {} + required: [http] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/authorization/additionalProperties/oneOf + value: + - properties: + opa: {} + required: [opa] + - properties: + patternMatching: {} + required: [patternMatching] + - properties: + kubernetesSubjectAccessReview: {} + required: [kubernetesSubjectAccessReview] + - properties: + spicedb: {} + required: [spicedb] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/response/properties/success/properties/headers/additionalProperties/oneOf + value: + - properties: + wristband: {} + required: [wristband] + - properties: + json: {} + required: [json] + - properties: + plain: {} + required: [plain] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/response/properties/success/properties/dynamicMetadata/additionalProperties/oneOf + value: + - properties: + wristband: {} + required: [wristband] + - properties: + json: {} + required: [json] + - properties: + plain: {} + required: [plain] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/authorization/additionalProperties/properties/patternMatching/properties/patterns/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/authentication/additionalProperties/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/metadata/additionalProperties/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/authorization/additionalProperties/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/response/properties/success/properties/headers/additionalProperties/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +- op: add + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/response/properties/success/properties/dynamicMetadata/additionalProperties/properties/when/items/oneOf + value: + - properties: + patternRef: {} + required: [patternRef] + - properties: + operator: {} + selector: {} + value: {} + required: [operator, selector] + - properties: + all: {} + required: [all] + - properties: + any: {} + required: [any] + +# v1beta3 - op: add path: /spec/versions/1/schema/openAPIV3Schema/properties/spec/properties/authentication/additionalProperties/oneOf value: diff --git a/install/manifests.yaml b/install/manifests.yaml index dcd3a7c5..b8d66669 100644 --- a/install/manifests.yaml +++ b/install/manifests.yaml @@ -76,6 +76,42 @@ spec: properties: authentication: additionalProperties: + oneOf: + - properties: + credentials: {} + oauth2Introspection: {} + required: + - oauth2Introspection + - properties: + credentials: {} + jwt: {} + required: + - jwt + - properties: + apiKey: {} + credentials: {} + required: + - apiKey + - properties: + credentials: {} + x509: {} + required: + - x509 + - properties: + credentials: {} + kubernetesTokenReview: {} + required: + - kubernetesTokenReview + - properties: + anonymous: {} + credentials: {} + required: + - anonymous + - properties: + credentials: {} + plain: {} + required: + - plain properties: anonymous: description: Anonymous access. @@ -324,6 +360,26 @@ spec: If omitted, the config will be enforced for all requests. If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to be evaluated @@ -433,6 +489,23 @@ spec: type: object authorization: additionalProperties: + oneOf: + - properties: + opa: {} + required: + - opa + - properties: + patternMatching: {} + required: + - patternMatching + - properties: + kubernetesSubjectAccessReview: {} + required: + - kubernetesSubjectAccessReview + - properties: + spicedb: {} + required: + - spicedb properties: cache: description: |- @@ -803,6 +876,26 @@ spec: properties: patterns: items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to be evaluated @@ -964,6 +1057,26 @@ spec: If omitted, the config will be enforced for all requests. If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to be evaluated @@ -1304,6 +1417,19 @@ spec: type: array metadata: additionalProperties: + oneOf: + - properties: + userInfo: {} + required: + - userInfo + - properties: + uma: {} + required: + - uma + - properties: + http: {} + required: + - http properties: cache: description: |- @@ -1574,6 +1700,26 @@ spec: If omitted, the config will be enforced for all requests. If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to be evaluated @@ -1665,6 +1811,19 @@ spec: dynamicMetadata: additionalProperties: description: Settings of the success custom response item. + oneOf: + - properties: + wristband: {} + required: + - wristband + - properties: + json: {} + required: + - json + - properties: + plain: {} + required: + - plain properties: cache: description: |- @@ -1752,6 +1911,26 @@ spec: If omitted, the config will be enforced for all requests. If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to @@ -1864,6 +2043,19 @@ spec: type: object headers: additionalProperties: + oneOf: + - properties: + wristband: {} + required: + - wristband + - properties: + json: {} + required: + - json + - properties: + plain: {} + required: + - plain properties: cache: description: |- @@ -1951,6 +2143,26 @@ spec: If omitted, the config will be enforced for all requests. If present, all conditions must match for the config to be enforced; otherwise, the config will be skipped. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to @@ -2182,6 +2394,26 @@ spec: If omitted, the AuthConfig will be enforced at all requests. If present, all conditions must match for the AuthConfig to be enforced; otherwise, Authorino skips the AuthConfig and returns to the auth request with status OK. items: + oneOf: + - properties: + patternRef: {} + required: + - patternRef + - properties: + operator: {} + selector: {} + value: {} + required: + - operator + - selector + - properties: + all: {} + required: + - all + - properties: + any: {} + required: + - any properties: all: description: A list of pattern expressions to be evaluated as From ae2dc8150af2e6cdb35957ba7305c4c2a76d6149 Mon Sep 17 00:00:00 2001 From: KevFan Date: Thu, 3 Oct 2024 09:58:43 +0100 Subject: [PATCH 4/5] refactor: v1beta3 as storage version Signed-off-by: KevFan --- Makefile | 2 +- api/v1beta2/auth_config_types.go | 1 - api/v1beta3/auth_config_types.go | 1 + controllers/auth_config_controller.go | 2 +- controllers/auth_config_controller_test.go | 2 +- controllers/auth_config_status_updater.go | 2 +- controllers/auth_config_status_updater_test.go | 2 +- install/crd/authorino.kuadrant.io_authconfigs.yaml | 4 ++-- install/manifests.yaml | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 17d17838..305b11dd 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ report-benchmarks: cover: ## Shows test coverage go tool cover -html=cover.out -AUTHCONFIG_VERSION ?= v1beta2 +AUTHCONFIG_VERSION ?= v1beta3 VERBOSE ?= 0 e2e: ## Runs the end-to-end tests on a local environment setup $(MAKE) local-setup NAMESPACE=authorino KIND_CLUSTER_NAME=authorino-e2e AUTHORINO_IMAGE=$(AUTHORINO_IMAGE) TLS_ENABLED=$(TLS_ENABLED) OPERATOR_BRANCH=$(OPERATOR_BRANCH) AUTHORINO_MANIFESTS=$(AUTHORINO_MANIFESTS) AUTHORINO_INSTANCE=$(AUTHORINO_INSTANCE) ENVOY_OVERLAY=$(ENVOY_OVERLAY) DEPLOY_KEYCLOAK=1 FF=1 diff --git a/api/v1beta2/auth_config_types.go b/api/v1beta2/auth_config_types.go index 098ceb05..c689e51b 100644 --- a/api/v1beta2/auth_config_types.go +++ b/api/v1beta2/auth_config_types.go @@ -91,7 +91,6 @@ type StatusConditionType string // AuthConfig is the schema for Authorino's AuthConfig API // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.summary.ready`,description="Ready for all hosts" // +kubebuilder:printcolumn:name="Hosts",type=string,JSONPath=`.status.summary.numHostsReady`,description="Number of hosts ready" // +kubebuilder:printcolumn:name="Authentication",type=integer,JSONPath=`.status.summary.numIdentitySources`,description="Number of trusted identity sources",priority=2 diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go index 693bb528..0c3be7ed 100644 --- a/api/v1beta3/auth_config_types.go +++ b/api/v1beta3/auth_config_types.go @@ -91,6 +91,7 @@ type StatusConditionType string // AuthConfig is the schema for Authorino's AuthConfig API // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.summary.ready`,description="Ready for all hosts" // +kubebuilder:printcolumn:name="Hosts",type=string,JSONPath=`.status.summary.numHostsReady`,description="Number of hosts ready" // +kubebuilder:printcolumn:name="Authentication",type=integer,JSONPath=`.status.summary.numIdentitySources`,description="Number of trusted identity sources",priority=2 diff --git a/controllers/auth_config_controller.go b/controllers/auth_config_controller.go index cca5ff1f..6de5191f 100644 --- a/controllers/auth_config_controller.go +++ b/controllers/auth_config_controller.go @@ -22,7 +22,7 @@ import ( "sort" "sync" - api "github.com/kuadrant/authorino/api/v1beta2" + api "github.com/kuadrant/authorino/api/v1beta3" "github.com/kuadrant/authorino/pkg/auth" "github.com/kuadrant/authorino/pkg/evaluators" authorization_evaluators "github.com/kuadrant/authorino/pkg/evaluators/authorization" diff --git a/controllers/auth_config_controller_test.go b/controllers/auth_config_controller_test.go index 84e58631..14beb56e 100644 --- a/controllers/auth_config_controller_test.go +++ b/controllers/auth_config_controller_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - api "github.com/kuadrant/authorino/api/v1beta2" + api "github.com/kuadrant/authorino/api/v1beta3" "github.com/kuadrant/authorino/pkg/evaluators" "github.com/kuadrant/authorino/pkg/httptest" "github.com/kuadrant/authorino/pkg/index" diff --git a/controllers/auth_config_status_updater.go b/controllers/auth_config_status_updater.go index d21f499a..9d3e3595 100644 --- a/controllers/auth_config_status_updater.go +++ b/controllers/auth_config_status_updater.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - api "github.com/kuadrant/authorino/api/v1beta2" + api "github.com/kuadrant/authorino/api/v1beta3" "github.com/kuadrant/authorino/pkg/log" "github.com/kuadrant/authorino/pkg/utils" diff --git a/controllers/auth_config_status_updater_test.go b/controllers/auth_config_status_updater_test.go index c35eecea..915e0e10 100644 --- a/controllers/auth_config_status_updater_test.go +++ b/controllers/auth_config_status_updater_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - api "github.com/kuadrant/authorino/api/v1beta2" + api "github.com/kuadrant/authorino/api/v1beta3" "github.com/kuadrant/authorino/pkg/log" "github.com/golang/mock/gomock" diff --git a/install/crd/authorino.kuadrant.io_authconfigs.yaml b/install/crd/authorino.kuadrant.io_authconfigs.yaml index 41ee823f..7c15cc4b 100644 --- a/install/crd/authorino.kuadrant.io_authconfigs.yaml +++ b/install/crd/authorino.kuadrant.io_authconfigs.yaml @@ -2313,7 +2313,7 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} - additionalPrinterColumns: @@ -4615,6 +4615,6 @@ spec: type: object type: object served: true - storage: false + storage: true subresources: status: {} diff --git a/install/manifests.yaml b/install/manifests.yaml index b8d66669..3bc0f460 100644 --- a/install/manifests.yaml +++ b/install/manifests.yaml @@ -2544,7 +2544,7 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} - additionalPrinterColumns: @@ -5078,7 +5078,7 @@ spec: type: object type: object served: true - storage: false + storage: true subresources: status: {} --- From bdfb97110a7f8cbeb81554258b1e8b0ad4d9782f Mon Sep 17 00:00:00 2001 From: KevFan Date: Thu, 10 Oct 2024 14:50:38 +0100 Subject: [PATCH 5/5] refactors: docs to v1beta3 Signed-off-by: KevFan --- api/v1beta3/groupversion_info.go | 2 +- controllers/auth_config_controller_test.go | 2 +- .../auth_config_status_updater_test.go | 2 +- docs/architecture.md | 2 +- docs/features.md | 14 ++--- docs/getting-started.md | 2 +- docs/user-guides/anonymous-access.md | 2 +- docs/user-guides/api-key-authentication.md | 2 +- ...ed-rate-limiting-envoy-dynamic-metadata.md | 2 +- docs/user-guides/authzed.md | 2 +- docs/user-guides/caching.md | 2 +- .../deny-with-redirect-to-login.md | 4 +- ...cation-architecture-festival-wristbands.md | 4 +- .../envoy-jwt-authn-and-authorino.md | 2 +- docs/user-guides/external-metadata.md | 2 +- docs/user-guides/host-override.md | 6 +- docs/user-guides/http-basic-authentication.md | 2 +- docs/user-guides/injecting-data.md | 2 +- .../json-pattern-matching-authorization.md | 2 +- .../keycloak-authorization-services.md | 2 +- .../kubernetes-subjectaccessreview.md | 2 +- docs/user-guides/kubernetes-tokenreview.md | 2 +- docs/user-guides/mtls-authentication.md | 2 +- .../user-guides/oauth2-token-introspection.md | 2 +- docs/user-guides/observability.md | 38 ++++++------- docs/user-guides/oidc-jwt-authentication.md | 2 +- docs/user-guides/oidc-rbac.md | 2 +- docs/user-guides/oidc-user-info.md | 2 +- docs/user-guides/opa-authorization.md | 2 +- docs/user-guides/passing-credentials.md | 2 +- .../resource-level-authorization-uma.md | 2 +- docs/user-guides/sharding.md | 4 +- docs/user-guides/token-normalization.md | 2 +- docs/user-guides/validating-webhook.md | 56 +++++++++---------- 34 files changed, 89 insertions(+), 91 deletions(-) diff --git a/api/v1beta3/groupversion_info.go b/api/v1beta3/groupversion_info.go index 09196aca..14081838 100644 --- a/api/v1beta3/groupversion_info.go +++ b/api/v1beta3/groupversion_info.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta2 contains API Schema definitions for the config v1beta2 API group +// Package v1beta3 contains API Schema definitions for the config v1beta3 API group // +kubebuilder:object:generate=true // +groupName=authorino.kuadrant.io package v1beta3 diff --git a/controllers/auth_config_controller_test.go b/controllers/auth_config_controller_test.go index 14beb56e..8169a6d3 100644 --- a/controllers/auth_config_controller_test.go +++ b/controllers/auth_config_controller_test.go @@ -110,7 +110,7 @@ func newTestAuthConfig(authConfigLabels map[string]string) api.AuthConfig { return api.AuthConfig{ TypeMeta: metav1.TypeMeta{ Kind: "AuthConfig", - APIVersion: "authorino.kuadrant.io/v1beta2", + APIVersion: "authorino.kuadrant.io/v1beta3", }, ObjectMeta: metav1.ObjectMeta{ Name: "auth-config-1", diff --git a/controllers/auth_config_status_updater_test.go b/controllers/auth_config_status_updater_test.go index 915e0e10..787b64b9 100644 --- a/controllers/auth_config_status_updater_test.go +++ b/controllers/auth_config_status_updater_test.go @@ -176,7 +176,7 @@ func mockStatusUpdateAuthConfigWithLabelsAndHosts(labels map[string]string, host return api.AuthConfig{ TypeMeta: metav1.TypeMeta{ Kind: "AuthConfig", - APIVersion: "authorino.kuadrant.io/v1beta2", + APIVersion: "authorino.kuadrant.io/v1beta3", }, ObjectMeta: metav1.ObjectMeta{ Name: "auth-config-1", diff --git a/docs/architecture.md b/docs/architecture.md index ff12f91c..677de8ac 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -71,7 +71,7 @@ The desired protection for a service is declaratively stated by applying an `Aut An `AuthConfig` resource typically looks like the following: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: my-api-protection diff --git a/docs/features.md b/docs/features.md index 0b7201e8..4978dc39 100644 --- a/docs/features.md +++ b/docs/features.md @@ -89,7 +89,7 @@ Whenever an `AuthConfig` is indexed, Authorino will also index all matching API **Example.** For the following `AuthConfig`: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: my-api-protection @@ -135,7 +135,7 @@ The list of `audiences` of the token must include the requested host and port of For the following `AuthConfig` CR, the Kubernetes token must include the audience `my-api.io`: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: my-api-protection @@ -150,7 +150,7 @@ spec: Whereas for the following `AuthConfig` CR, the Kubernetes token audiences must include **foo** and **bar**: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: my-api-protection @@ -606,7 +606,7 @@ User-defined dynamic JSON objects generated by Authorino in the response phase, The following Authorino `AuthConfig` custom resource is an example that defines 3 dynamic JSON response items, where two items are returned to the client, stringified, in added HTTP headers, and the third as Envoy Dynamic Metadata. Envoy proxy can be configured to propagate the dynamic metadata emitted by Authorino into another filter – e.g. the rate limit filter. ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: namespace: my-namespace @@ -656,7 +656,7 @@ Festival Wristbands are signed OpenID Connect JSON Web Tokens (JWTs) issued by A The Authorino `AuthConfig` custom resource below sets an API protection that issues a wristband after a successful authentication via API key. Apart from standard JWT claims, the wristband contains 2 custom claims: a static value `aud=internal` and a dynamic value `born` that fetches from the authorization JSON the date/time of creation of the secret that represents the API key used to authenticate. ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: namespace: my-namespace @@ -750,7 +750,7 @@ Priorities can be set using the `priority` property available in all evaluator c Consider the following example to understand how priorities work: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: talker-api-protection @@ -1116,7 +1116,7 @@ By default, Authorino will only export metrics down to the level of the AuthConf E.g.: ```yaml -apiVersion: authorino.kuadrant.io/v1beta2 +apiVersion: authorino.kuadrant.io/v1beta3 kind: AuthConfig metadata: name: my-authconfig diff --git a/docs/getting-started.md b/docs/getting-started.md index 40fc1ae5..7c1af613 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -333,7 +333,7 @@ For authentication based on OpenID Connect (OIDC) JSON Web Tokens (JWT), plus on ```sh kubectl -n myapp apply -f -<&1 >/dev/null & ```sh kubectl -n internal apply -f -<Booting up the service ```jsonc - {"level":"info","ts":1669220526.929678,"logger":"authorino","msg":"setting instance base logger","min level":"debug","mode":"production"} - {"level":"info","ts":1669220526.929718,"logger":"authorino","msg":"booting up authorino","version":"7688cfa32317a49f0461414e741c980e9c05dba3"} - {"level":"debug","ts":1669220526.9297278,"logger":"authorino","msg":"setting up with options","auth-config-label-selector":"","deep-metrics-enabled":"false","enable-leader-election":"false","evaluator-cache-size":"1","ext-auth-grpc-port":"50051","ext-auth-http-port":"5001","health-probe-addr":":8081","log-level":"debug","log-mode":"production","max-http-request-body-size":"8192","metrics-addr":":8080","oidc-http-port":"8083","oidc-tls-cert":"/etc/ssl/certs/oidc.crt","oidc-tls-cert-key":"/etc/ssl/private/oidc.key","secret-label-selector":"authorino.kuadrant.io/managed-by=authorino","timeout":"0","tls-cert":"/etc/ssl/certs/tls.crt","tls-cert-key":"/etc/ssl/private/tls.key","watch-namespace":"default"} - {"level":"info","ts":1669220527.9816976,"logger":"authorino.controller-runtime.metrics","msg":"Metrics server is starting to listen","addr":":8080"} - {"level":"info","ts":1669220527.9823213,"logger":"authorino","msg":"starting grpc auth service","port":50051,"tls":true} - {"level":"info","ts":1669220527.9823658,"logger":"authorino","msg":"starting http auth service","port":5001,"tls":true} - {"level":"info","ts":1669220527.9824295,"logger":"authorino","msg":"starting http oidc service","port":8083,"tls":true} - {"level":"info","ts":1669220527.9825335,"logger":"authorino","msg":"starting manager"} - {"level":"info","ts":1669220527.982721,"logger":"authorino","msg":"Starting server","path":"/metrics","kind":"metrics","addr":"[::]:8080"} - {"level":"info","ts":1669220527.982766,"logger":"authorino","msg":"Starting server","kind":"health probe","addr":"[::]:8081"} - {"level":"info","ts":1669220527.9829438,"logger":"authorino.controller.secret","msg":"Starting EventSource","reconciler group":"","reconciler kind":"Secret","source":"kind source: *v1.Secret"} - {"level":"info","ts":1669220527.9829693,"logger":"authorino.controller.secret","msg":"Starting Controller","reconciler group":"","reconciler kind":"Secret"} - {"level":"info","ts":1669220527.9829714,"logger":"authorino.controller.authconfig","msg":"Starting EventSource","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig","source":"kind source: *v1beta1.AuthConfig"} - {"level":"info","ts":1669220527.9830208,"logger":"authorino.controller.authconfig","msg":"Starting Controller","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig"} - {"level":"info","ts":1669220528.0834699,"logger":"authorino.controller.authconfig","msg":"Starting workers","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig","worker count":1} - {"level":"info","ts":1669220528.0836608,"logger":"authorino.controller.secret","msg":"Starting workers","reconciler group":"","reconciler kind":"Secret","worker count":1} - {"level":"info","ts":1669220529.041266,"logger":"authorino","msg":"starting status update manager"} - {"level":"info","ts":1669220529.0418258,"logger":"authorino.controller.authconfig","msg":"Starting EventSource","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig","source":"kind source: *v1beta1.AuthConfig"} - {"level":"info","ts":1669220529.0418813,"logger":"authorino.controller.authconfig","msg":"Starting Controller","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig"} - {"level":"info","ts":1669220529.1432905,"logger":"authorino.controller.authconfig","msg":"Starting workers","reconciler group":"authorino.kuadrant.io","reconciler kind":"AuthConfig","worker count":1} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"setting instance base logger","min level":"info","mode":"production"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"build information","version":"v1beta3","commit":"ae2dc8150af2e6cdb35957ba7305c4c2a76d6149","dirty":"false","cmd":"server"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting http auth service","port":5001,"tls":false} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting grpc auth service","port":50051,"tls":false} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting http oidc service","port":8083,"tls":false} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting reconciliation manager"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting server","kind":"health probe","addr":"[::]:8081"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino.controller-runtime.metrics","msg":"Starting metrics server"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino.controller-runtime.metrics","msg":"Serving metrics server","bindAddress":":8080","secure":false} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting EventSource","controller":"authconfig","controllerGroup":"authorino.kuadrant.io","controllerKind":"AuthConfig","source":"kind source: *v1beta3.AuthConfig"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting EventSource","controller":"secret","controllerGroup":"","controllerKind":"Secret","source":"kind source: *v1.Secret"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting Controller","controller":"secret","controllerGroup":"","controllerKind":"Secret"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting Controller","controller":"authconfig","controllerGroup":"authorino.kuadrant.io","controllerKind":"AuthConfig"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"starting status update manager"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting EventSource","controller":"authconfig","controllerGroup":"authorino.kuadrant.io","controllerKind":"AuthConfig","source":"kind source: *v1beta3.AuthConfig"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting Controller","controller":"authconfig","controllerGroup":"authorino.kuadrant.io","controllerKind":"AuthConfig"} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting workers","controller":"secret","controllerGroup":"","controllerKind":"Secret","worker count":1} + {"level":"info","ts":"2024-10-07T10:31:02+01:00","logger":"authorino","msg":"Starting workers","controller":"authconfig","controllerGroup":"authorino.kuadrant.io","controllerKind":"AuthConfig","worker count":1} ``` diff --git a/docs/user-guides/oidc-jwt-authentication.md b/docs/user-guides/oidc-jwt-authentication.md index 62bd3255..1b19cbd7 100644 --- a/docs/user-guides/oidc-jwt-authentication.md +++ b/docs/user-guides/oidc-jwt-authentication.md @@ -135,7 +135,7 @@ Create an Authorino `AuthConfig` custom resource declaring the auth rules to be ```sh kubectl apply -f -<