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 = /