diff --git a/api/v1beta3/auth_config_types.go b/api/v1beta3/auth_config_types.go index 0c3be7ed..db2bbb2d 100644 --- a/api/v1beta3/auth_config_types.go +++ b/api/v1beta3/auth_config_types.go @@ -164,13 +164,21 @@ type PatternExpression struct { Value string `json:"value,omitempty"` } +type CelExpression struct { + Expression string `json:"expression,omitempty"` +} + +type CelPredicate struct { + Predicate string `json:"predicate,omitempty"` +} + // +kubebuilder:validation:Enum:=eq;neq;incl;excl;matches type PatternExpressionOperator string type PatternExpressionOrRef struct { PatternExpression `json:",omitempty"` PatternRef `json:",omitempty"` - + CelPredicate `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. @@ -199,6 +207,8 @@ type ValueOrSelector struct { // 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"` + + Expression CelExpression `json:",omitempty"` } type CommonEvaluatorSpec struct { @@ -402,6 +412,8 @@ type PlainIdentitySpec struct { // 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"` + + Expression CelExpression `json:",omitempty"` } type AnonymousAccessSpec struct{} @@ -439,6 +451,8 @@ type HttpEndpointSpec struct { // E.g. https://ext-auth-server.io/metadata?p={request.path} Url string `json:"url"` + UrlExpression CelExpression `json:",omitempty"` + // 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 diff --git a/api/v1beta3/zz_generated.deepcopy.go b/api/v1beta3/zz_generated.deepcopy.go index 5b0414ce..1f89a632 100644 --- a/api/v1beta3/zz_generated.deepcopy.go +++ b/api/v1beta3/zz_generated.deepcopy.go @@ -481,6 +481,36 @@ func (in *CallbackSpec) DeepCopy() *CallbackSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CelExpression) DeepCopyInto(out *CelExpression) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CelExpression. +func (in *CelExpression) DeepCopy() *CelExpression { + if in == nil { + return nil + } + out := new(CelExpression) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CelPredicate) DeepCopyInto(out *CelPredicate) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CelPredicate. +func (in *CelPredicate) DeepCopy() *CelPredicate { + if in == nil { + return nil + } + out := new(CelPredicate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CommonEvaluatorSpec) DeepCopyInto(out *CommonEvaluatorSpec) { *out = *in @@ -667,6 +697,7 @@ func (in *HeaderSuccessResponseSpec) DeepCopy() *HeaderSuccessResponseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HttpEndpointSpec) DeepCopyInto(out *HttpEndpointSpec) { *out = *in + out.UrlExpression = in.UrlExpression if in.Method != nil { in, out := &in.Method, &out.Method *out = new(HttpMethod) @@ -998,6 +1029,7 @@ func (in *PatternExpressionOrRef) DeepCopyInto(out *PatternExpressionOrRef) { *out = *in out.PatternExpression = in.PatternExpression out.PatternRef = in.PatternRef + out.CelPredicate = in.CelPredicate if in.All != nil { in, out := &in.All, &out.All *out = make([]UnstructuredPatternExpressionOrRef, len(*in)) @@ -1084,6 +1116,7 @@ func (in *PatternRef) DeepCopy() *PatternRef { func (in *PlainAuthResponseSpec) DeepCopyInto(out *PlainAuthResponseSpec) { *out = *in in.Value.DeepCopyInto(&out.Value) + out.Expression = in.Expression } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlainAuthResponseSpec. @@ -1099,6 +1132,7 @@ func (in *PlainAuthResponseSpec) DeepCopy() *PlainAuthResponseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PlainIdentitySpec) DeepCopyInto(out *PlainIdentitySpec) { *out = *in + out.Expression = in.Expression } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlainIdentitySpec. @@ -1287,6 +1321,7 @@ func (in *UserInfoMetadataSpec) DeepCopy() *UserInfoMetadataSpec { func (in *ValueOrSelector) DeepCopyInto(out *ValueOrSelector) { *out = *in in.Value.DeepCopyInto(&out.Value) + out.Expression = in.Expression } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValueOrSelector. diff --git a/controllers/auth_config_controller.go b/controllers/auth_config_controller.go index 6de5191f..e871c961 100644 --- a/controllers/auth_config_controller.go +++ b/controllers/auth_config_controller.go @@ -29,6 +29,8 @@ import ( identity_evaluators "github.com/kuadrant/authorino/pkg/evaluators/identity" metadata_evaluators "github.com/kuadrant/authorino/pkg/evaluators/metadata" response_evaluators "github.com/kuadrant/authorino/pkg/evaluators/response" + "github.com/kuadrant/authorino/pkg/expressions" + "github.com/kuadrant/authorino/pkg/expressions/cel" "github.com/kuadrant/authorino/pkg/index" "github.com/kuadrant/authorino/pkg/json" "github.com/kuadrant/authorino/pkg/jsonexp" @@ -181,22 +183,26 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf for identityCfgName, identity := range authConfigIdentityConfigs { extendedProperties := make([]evaluators.IdentityExtension, len(identity.Defaults)+len(identity.Overrides)) for propertyName, property := range identity.Defaults { - extendedProperties = append(extendedProperties, evaluators.NewIdentityExtension(propertyName, json.JSONValue{ + extendedProperties = append(extendedProperties, evaluators.NewIdentityExtension(propertyName, &json.JSONValue{ Static: property.Value, Pattern: property.Selector, }, false)) } for propertyName, property := range identity.Overrides { - extendedProperties = append(extendedProperties, evaluators.NewIdentityExtension(propertyName, json.JSONValue{ + extendedProperties = append(extendedProperties, evaluators.NewIdentityExtension(propertyName, &json.JSONValue{ Static: property.Value, Pattern: property.Selector, }, true)) } + predicates, err := buildPredicates(authConfig, identity.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedIdentity := &evaluators.IdentityConfig{ Name: identityCfgName, Priority: identity.Priority, - Conditions: buildJSONExpression(authConfig, identity.Conditions, jsonexp.All), + Conditions: predicates, ExtendedProperties: extendedProperties, Metrics: identity.Metrics, } @@ -206,8 +212,12 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf if ttl == 0 { ttl = api.EvaluatorDefaultCacheTTL } + key, err := getJsonFromStaticDynamic(&identity.Cache.Key) + if err != nil { + return nil, err + } translatedIdentity.Cache = evaluators.NewEvaluatorCache( - *getJsonFromStaticDynamic(&identity.Cache.Key), + key, ttl, ) } @@ -272,7 +282,15 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf } case api.PlainIdentityAuthentication: - translatedIdentity.Plain = &identity_evaluators.Plain{Pattern: identity.Plain.Selector} + if identity.Plain.Expression.Expression != "" { + expression, err := cel.NewStringExpression(identity.Plain.Expression.Expression) + if err != nil { + return nil, err + } + translatedIdentity.Plain = &identity_evaluators.Plain{Value: expression, Pattern: identity.Plain.Expression.Expression} + } else { + translatedIdentity.Plain = &identity_evaluators.Plain{Value: &json.JSONValue{Pattern: identity.Plain.Selector}, Pattern: identity.Plain.Selector} + } case api.AnonymousAccessAuthentication: translatedIdentity.Noop = &identity_evaluators.Noop{AuthCredentials: authCred} @@ -288,10 +306,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf interfacedMetadataConfigs := make([]auth.AuthConfigEvaluator, 0) for name, metadata := range authConfig.Spec.Metadata { + predicates, err := buildPredicates(authConfig, metadata.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedMetadata := &evaluators.MetadataConfig{ Name: name, Priority: metadata.Priority, - Conditions: buildJSONExpression(authConfig, metadata.Conditions, jsonexp.All), + Conditions: predicates, Metrics: metadata.Metrics, } @@ -300,8 +322,12 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf if ttl == 0 { ttl = api.EvaluatorDefaultCacheTTL } + key, err := getJsonFromStaticDynamic(&metadata.Cache.Key) + if err != nil { + return nil, err + } translatedMetadata.Cache = evaluators.NewEvaluatorCache( - *getJsonFromStaticDynamic(&metadata.Cache.Key), + key, ttl, ) } @@ -357,10 +383,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf authzIndex := 0 for authzName, authorization := range authConfig.Spec.Authorization { + predicates, err := buildPredicates(authConfig, authorization.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedAuthorization := &evaluators.AuthorizationConfig{ Name: authzName, Priority: authorization.Priority, - Conditions: buildJSONExpression(authConfig, authorization.Conditions, jsonexp.All), + Conditions: predicates, Metrics: authorization.Metrics, } @@ -369,8 +399,12 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf if ttl == 0 { ttl = api.EvaluatorDefaultCacheTTL } + key, err := getJsonFromStaticDynamic(&authorization.Cache.Key) + if err != nil { + return nil, err + } translatedAuthorization.Cache = evaluators.NewEvaluatorCache( - *getJsonFromStaticDynamic(&authorization.Cache.Key), + key, ttl, ) } @@ -415,8 +449,12 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf // json case api.PatternMatchingAuthorization: + rules, err := buildPredicates(authConfig, authorization.PatternMatching.Patterns, jsonexp.All) + if err != nil { + return nil, err + } translatedAuthorization.JSON = &authorization_evaluators.JSONPatternMatching{ - Rules: buildJSONExpression(authConfig, authorization.PatternMatching.Patterns, jsonexp.All), + Rules: rules, } case api.KubernetesSubjectAccessReviewAuthorization: @@ -427,17 +465,17 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf resourceAttributes := authorization.KubernetesSubjectAccessReview.ResourceAttributes if resourceAttributes != nil { authorinoResourceAttributes = &authorization_evaluators.KubernetesAuthzResourceAttributes{ - Namespace: json.JSONValue{Static: resourceAttributes.Namespace.Value, Pattern: resourceAttributes.Namespace.Selector}, - Group: json.JSONValue{Static: resourceAttributes.Group.Value, Pattern: resourceAttributes.Group.Selector}, - Resource: json.JSONValue{Static: resourceAttributes.Resource.Value, Pattern: resourceAttributes.Resource.Selector}, - Name: json.JSONValue{Static: resourceAttributes.Name.Value, Pattern: resourceAttributes.Name.Selector}, - SubResource: json.JSONValue{Static: resourceAttributes.SubResource.Value, Pattern: resourceAttributes.SubResource.Selector}, - Verb: json.JSONValue{Static: resourceAttributes.Verb.Value, Pattern: resourceAttributes.Verb.Selector}, + Namespace: &json.JSONValue{Static: resourceAttributes.Namespace.Value, Pattern: resourceAttributes.Namespace.Selector}, + Group: &json.JSONValue{Static: resourceAttributes.Group.Value, Pattern: resourceAttributes.Group.Selector}, + Resource: &json.JSONValue{Static: resourceAttributes.Resource.Value, Pattern: resourceAttributes.Resource.Selector}, + Name: &json.JSONValue{Static: resourceAttributes.Name.Value, Pattern: resourceAttributes.Name.Selector}, + SubResource: &json.JSONValue{Static: resourceAttributes.SubResource.Value, Pattern: resourceAttributes.SubResource.Selector}, + Verb: &json.JSONValue{Static: resourceAttributes.Verb.Value, Pattern: resourceAttributes.Verb.Selector}, } } var err error - translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(authorinoUser, authorization.KubernetesSubjectAccessReview.Groups, authorinoResourceAttributes) + translatedAuthorization.KubernetesAuthz, err = authorization_evaluators.NewKubernetesAuthz(&authorinoUser, authorization.KubernetesSubjectAccessReview.Groups, authorinoResourceAttributes) if err != nil { return nil, err } @@ -454,15 +492,24 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf sharedSecret = string(secret.Data[secretRef.Key]) } + permission, err := getJsonFromStaticDynamic(&authzed.Permission) + if err != nil { + return nil, err + } translatedAuthzed := &authorization_evaluators.Authzed{ Endpoint: authzed.Endpoint, Insecure: authzed.Insecure, SharedSecret: sharedSecret, - Permission: *getJsonFromStaticDynamic(&authzed.Permission), + Permission: permission, + } + translatedAuthzed.Subject, translatedAuthzed.SubjectKind, err = spiceDBObjectToJsonValues(authzed.Subject) + if err != nil { + return nil, err + } + translatedAuthzed.Resource, translatedAuthzed.ResourceKind, err = spiceDBObjectToJsonValues(authzed.Resource) + if err != nil { + return nil, err } - translatedAuthzed.Subject, translatedAuthzed.SubjectKind = spiceDBObjectToJsonValues(authzed.Subject) - translatedAuthzed.Resource, translatedAuthzed.ResourceKind = spiceDBObjectToJsonValues(authzed.Resource) - translatedAuthorization.Authzed = translatedAuthzed case api.UnknownAuthorizationMethod: @@ -477,10 +524,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf if responseConfig := authConfig.Spec.Response; responseConfig != nil { for responseName, headerSuccessResponse := range responseConfig.Success.Headers { + predicates, err := buildPredicates(authConfig, headerSuccessResponse.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedResponse := evaluators.NewResponseConfig( responseName, headerSuccessResponse.Priority, - buildJSONExpression(authConfig, headerSuccessResponse.Conditions, jsonexp.All), + predicates, "httpHeader", headerSuccessResponse.Key, headerSuccessResponse.Metrics, @@ -495,10 +546,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf } for responseName, successResponse := range responseConfig.Success.DynamicMetadata { + predicates, err := buildPredicates(authConfig, successResponse.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedResponse := evaluators.NewResponseConfig( responseName, successResponse.Priority, - buildJSONExpression(authConfig, successResponse.Conditions, jsonexp.All), + predicates, "envoyDynamicMetadata", successResponse.Key, successResponse.Metrics, @@ -516,10 +571,14 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf interfacedCallbackConfigs := make([]auth.AuthConfigEvaluator, 0) for callbackName, callback := range authConfig.Spec.Callbacks { + predicates, err := buildPredicates(authConfig, callback.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedCallback := &evaluators.CallbackConfig{ Name: callbackName, Priority: callback.Priority, - Conditions: buildJSONExpression(authConfig, callback.Conditions, jsonexp.All), + Conditions: predicates, Metrics: callback.Metrics, } @@ -539,8 +598,12 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf interfacedCallbackConfigs = append(interfacedCallbackConfigs, translatedCallback) } + predicates, err := buildPredicates(authConfig, authConfig.Spec.Conditions, jsonexp.All) + if err != nil { + return nil, err + } translatedAuthConfig := &evaluators.AuthConfig{ - Conditions: buildJSONExpression(authConfig, authConfig.Spec.Conditions, jsonexp.All), + Conditions: predicates, IdentityConfigs: interfacedIdentityConfigs, MetadataConfigs: interfacedMetadataConfigs, AuthorizationConfigs: interfacedAuthorizationConfigs, @@ -552,10 +615,18 @@ func (r *AuthConfigReconciler) translateAuthConfig(ctx context.Context, authConf // denyWith if responseConfig := authConfig.Spec.Response; responseConfig != nil { if denyWith := responseConfig.Unauthenticated; denyWith != nil { - translatedAuthConfig.Unauthenticated = buildAuthorinoDenyWithValues(denyWith) + value, err := buildAuthorinoDenyWithValues(denyWith) + if err != nil { + return nil, err + } + translatedAuthConfig.Unauthenticated = value } if denyWith := responseConfig.Unauthorized; denyWith != nil { - translatedAuthConfig.Unauthorized = buildAuthorinoDenyWithValues(denyWith) + value, err := buildAuthorinoDenyWithValues(denyWith) + if err != nil { + return nil, err + } + translatedAuthConfig.Unauthorized = value } } @@ -594,7 +665,7 @@ func injectResponseConfig(ctx context.Context, authConfig *api.AuthConfig, succe for claimName, claim := range wristband.CustomClaims { customClaims = append(customClaims, json.JSONProperty{ Name: claimName, - Value: json.JSONValue{ + Value: &json.JSONValue{ Static: claim.Value, Pattern: claim.Selector, }, @@ -619,7 +690,7 @@ func injectResponseConfig(ctx context.Context, authConfig *api.AuthConfig, succe for propertyName, property := range successResponse.Json.Properties { jsonProperties = append(jsonProperties, json.JSONProperty{ Name: propertyName, - Value: json.JSONValue{ + Value: &json.JSONValue{ Static: property.Value, Pattern: property.Selector, }, @@ -631,7 +702,7 @@ func injectResponseConfig(ctx context.Context, authConfig *api.AuthConfig, succe // plain case api.PlainAuthResponse: translatedResponse.Plain = &response_evaluators.Plain{ - JSONValue: json.JSONValue{ + Value: &json.JSONValue{ Static: successResponse.Plain.Value, Pattern: successResponse.Plain.Selector, }, @@ -643,17 +714,22 @@ func injectResponseConfig(ctx context.Context, authConfig *api.AuthConfig, succe return nil } -func injectCache(cache *api.EvaluatorCaching, translatedResponse *evaluators.ResponseConfig) { +func injectCache(cache *api.EvaluatorCaching, translatedResponse *evaluators.ResponseConfig) error { if cache != nil { ttl := cache.TTL if ttl == 0 { ttl = api.EvaluatorDefaultCacheTTL } - translatedResponse.Cache = evaluators.NewEvaluatorCache( - *getJsonFromStaticDynamic(&cache.Key), - ttl, - ) + if key, err := getJsonFromStaticDynamic(&cache.Key); err != nil { + return err + } else { + translatedResponse.Cache = evaluators.NewEvaluatorCache( + key, + ttl, + ) + } } + return nil } func (r *AuthConfigReconciler) addToIndex(ctx context.Context, resourceNamespace, resourceId string, authConfig *evaluators.AuthConfig, hosts []string) (linkedHosts, looseHosts []string, err error) { @@ -805,7 +881,7 @@ func (r *AuthConfigReconciler) buildGenericHttpEvaluator(ctx context.Context, ht for name, param := range http.Parameters { params = append(params, json.JSONProperty{ Name: name, - Value: json.JSONValue{ + Value: &json.JSONValue{ Static: param.Value, Pattern: param.Selector, }, @@ -816,7 +892,7 @@ func (r *AuthConfigReconciler) buildGenericHttpEvaluator(ctx context.Context, ht for name, header := range http.Headers { headers = append(headers, json.JSONProperty{ Name: name, - Value: json.JSONValue{ + Value: &json.JSONValue{ Static: header.Value, Pattern: header.Selector, }, @@ -828,8 +904,19 @@ func (r *AuthConfigReconciler) buildGenericHttpEvaluator(ctx context.Context, ht method = string(*m) } + var dynamicEndpoint expressions.Value + if http.UrlExpression.Expression != "" { + endpoint, err := cel.NewStringExpression(http.UrlExpression.Expression) + if err != nil { + return nil, err + } else { + dynamicEndpoint = endpoint + } + } + ev := &metadata_evaluators.GenericHttp{ Endpoint: http.Url, + DynamicEndpoint: dynamicEndpoint, Method: method, Body: body, Parameters: params, @@ -875,18 +962,26 @@ func findIdentityConfigByName(identityConfigs []evaluators.IdentityConfig, name return nil, fmt.Errorf("missing identity config %v", name) } -func buildJSONExpression(authConfig *api.AuthConfig, patterns []api.PatternExpressionOrRef, op func(...jsonexp.Expression) jsonexp.Expression) jsonexp.Expression { +func buildPredicates(authConfig *api.AuthConfig, patterns []api.PatternExpressionOrRef, op func(...jsonexp.Expression) jsonexp.Expression) (jsonexp.Expression, error) { var expression []jsonexp.Expression for _, pattern := range patterns { // patterns or refs - expression = append(expression, buildJSONExpressionPatterns(authConfig, pattern)...) + expressions, err := buildJSONExpressionPatterns(authConfig, pattern) + if err != nil { + return nil, err + } + expression = append(expression, expressions...) // all if len(pattern.All) > 0 { p := make([]api.PatternExpressionOrRef, len(pattern.All)) for i, ptn := range pattern.All { p[i] = ptn.PatternExpressionOrRef } - expression = append(expression, buildJSONExpression(authConfig, p, jsonexp.All)) + predicates, err := buildPredicates(authConfig, p, jsonexp.All) + if err != nil { + return nil, err + } + expression = append(expression, predicates) } // any if len(pattern.Any) > 0 { @@ -894,25 +989,35 @@ func buildJSONExpression(authConfig *api.AuthConfig, patterns []api.PatternExpre for i, ptn := range pattern.Any { p[i] = ptn.PatternExpressionOrRef } - expression = append(expression, buildJSONExpression(authConfig, p, jsonexp.Any)) + predicates, err := buildPredicates(authConfig, p, jsonexp.Any) + if err != nil { + return nil, err + } + expression = append(expression, predicates) } } - return op(expression...) + return op(expression...), nil } -func buildJSONExpressionPatterns(authConfig *api.AuthConfig, pattern api.PatternExpressionOrRef) []jsonexp.Expression { +func buildJSONExpressionPatterns(authConfig *api.AuthConfig, pattern api.PatternExpressionOrRef) ([]jsonexp.Expression, error) { expressionsToAdd := api.PatternExpressions{} + expressions := make([]jsonexp.Expression, len(expressionsToAdd)) if expressionsByRef, found := authConfig.Spec.NamedPatterns[pattern.PatternRef.Name]; found { expressionsToAdd = append(expressionsToAdd, expressionsByRef...) } else if pattern.PatternExpression.Operator != "" { expressionsToAdd = append(expressionsToAdd, pattern.PatternExpression) + } else if pattern.Predicate != "" { + if predicate, err := cel.NewPredicate(pattern.Predicate); err != nil { + return nil, err + } else { + expressions = append(expressions, predicate) + } } - expressions := make([]jsonexp.Expression, len(expressionsToAdd)) - for i, expression := range expressionsToAdd { - expressions[i] = buildJSONExpressionPattern(expression) + for _, expression := range expressionsToAdd { + expressions = append(expressions, buildJSONExpressionPattern(expression)) } - return expressions + return expressions, nil } func buildJSONExpressionPattern(expression api.PatternExpression) jsonexp.Expression { @@ -923,42 +1028,59 @@ func buildJSONExpressionPattern(expression api.PatternExpression) jsonexp.Expres } } -func buildAuthorinoDenyWithValues(denyWithSpec *api.DenyWithSpec) *evaluators.DenyWithValues { +func buildAuthorinoDenyWithValues(denyWithSpec *api.DenyWithSpec) (*evaluators.DenyWithValues, error) { if denyWithSpec == nil { - return nil + return nil, nil } headers := make([]json.JSONProperty, 0, len(denyWithSpec.Headers)) for name, header := range denyWithSpec.Headers { - headers = append(headers, json.JSONProperty{Name: name, Value: json.JSONValue{Static: header.Value, Pattern: header.Selector}}) + headers = append(headers, json.JSONProperty{Name: name, Value: &json.JSONValue{Static: header.Value, Pattern: header.Selector}}) } + message, err := getJsonFromStaticDynamic(denyWithSpec.Message) + if err != nil { + return nil, err + } + body, err := getJsonFromStaticDynamic(denyWithSpec.Body) + if err != nil { + return nil, err + } return &evaluators.DenyWithValues{ Code: int32(denyWithSpec.Code), - Message: getJsonFromStaticDynamic(denyWithSpec.Message), + Message: message, Headers: headers, - Body: getJsonFromStaticDynamic(denyWithSpec.Body), - } + Body: body, + }, nil } -func getJsonFromStaticDynamic(value *api.ValueOrSelector) *json.JSONValue { +func getJsonFromStaticDynamic(value *api.ValueOrSelector) (expressions.Value, error) { if value == nil { - return nil + return nil, nil + } + + if value.Expression.Expression != "" { + return cel.NewExpression(value.Expression.Expression) } return &json.JSONValue{ Static: value.Value, Pattern: value.Selector, - } + }, nil } -func spiceDBObjectToJsonValues(obj *api.SpiceDBObject) (name json.JSONValue, kind json.JSONValue) { +func spiceDBObjectToJsonValues(obj *api.SpiceDBObject) (name expressions.Value, kind expressions.Value, err error) { if obj == nil { return } - name = *getJsonFromStaticDynamic(&obj.Name) - kind = *getJsonFromStaticDynamic(&obj.Kind) - - return name, kind + nameResolved, err := getJsonFromStaticDynamic(&obj.Name) + if err != nil { + return nil, nil, err + } + kindResolved, err := getJsonFromStaticDynamic(&obj.Kind) + if err != nil { + return nil, nil, err + } + return nameResolved, kindResolved, nil } diff --git a/controllers/auth_config_controller_test.go b/controllers/auth_config_controller_test.go index 8169a6d3..bd8bd803 100644 --- a/controllers/auth_config_controller_test.go +++ b/controllers/auth_config_controller_test.go @@ -95,10 +95,8 @@ func newTestAuthConfig(authConfigLabels map[string]string) api.AuthConfig { PatternMatching: &api.PatternMatchingAuthorizationSpec{ Patterns: []api.PatternExpressionOrRef{ { - PatternExpression: api.PatternExpression{ - Selector: "context.identity.role", - Operator: "eq", - Value: "admin", + CelPredicate: api.CelPredicate{ + Predicate: "auth.identity.role == 'admin'", }, }, }, diff --git a/go.mod b/go.mod index 3917d46c..80ee0f7a 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/gogo/googleapis v1.4.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/mock v1.6.0 + github.com/google/cel-go v0.21.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hashicorp/go-multierror v1.1.1 github.com/open-policy-agent/opa v0.68.0 @@ -43,6 +44,7 @@ require ( require ( cloud.google.com/go/compute/metadata v0.3.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -59,6 +61,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect diff --git a/go.sum b/go.sum index 784a7ad9..d9ef5732 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache/v2 v2.2.5 h1:mRc8r6GQjuJsmSKQNPsR5jQVXc8IJ1xsW5YXUYMLfqI= github.com/allegro/bigcache/v2 v2.2.5/go.mod h1:FppZsIO+IZk7gCuj5FiIDHGygD9xvWQcqg1uIPMb6tY= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -226,6 +228,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= +github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -506,6 +510,8 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -517,6 +523,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/install/crd/authorino.kuadrant.io_authconfigs.yaml b/install/crd/authorino.kuadrant.io_authconfigs.yaml index 7c15cc4b..d21dd4eb 100644 --- a/install/crd/authorino.kuadrant.io_authconfigs.yaml +++ b/install/crd/authorino.kuadrant.io_authconfigs.yaml @@ -2313,6 +2313,2414 @@ spec: type: object type: object served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - description: Ready for all hosts + jsonPath: .status.summary.ready + name: Ready + type: string + - description: Number of hosts ready + jsonPath: .status.summary.numHostsReady + name: Hosts + type: string + - description: Number of trusted identity sources + jsonPath: .status.summary.numIdentitySources + name: Authentication + priority: 2 + type: integer + - description: Number of external metadata sources + jsonPath: .status.summary.numMetadataSources + name: Metadata + priority: 2 + type: integer + - description: Number of authorization policies + jsonPath: .status.summary.numAuthorizationPolicies + name: Authorization + priority: 2 + type: integer + - description: Number of items added to the authorization response + jsonPath: .status.summary.numResponseItems + name: Response + priority: 2 + type: integer + - description: Whether issuing Festival Wristbands + jsonPath: .status.summary.festivalWristbandEnabled + name: Wristband + priority: 2 + type: boolean + name: v1beta3 + schema: + openAPIV3Schema: + description: AuthConfig is the schema for Authorino's AuthConfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Specifies the desired state of the AuthConfig resource, i.e. + the authencation/authorization scheme to be applied to protect the matching + service hosts. + properties: + authentication: + additionalProperties: + properties: + anonymous: + description: Anonymous access. + type: object + apiKey: + description: Authentication based on API keys stored in Kubernetes + secrets. + properties: + allNamespaces: + default: false + description: |- + 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. + type: boolean + selector: + description: Label selector used by Authorino to match secrets + from the cluster storing valid credentials to authenticate + to this service + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - selector + type: object + cache: + description: |- + Caching options for the resolved object returned when applying this config. + Omit it to avoid caching objects for this config. + properties: + key: + description: |- + Key used to store the entry in the cache. + The resolved key must be unique within the scope of this particular config. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + ttl: + default: 60 + description: Duration (in seconds) of the external data + in the cache before pulled again from the source. + type: integer + required: + - key + type: object + credentials: + description: |- + 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. + properties: + authorizationHeader: + properties: + prefix: + type: string + type: object + cookie: + properties: + name: + type: string + required: + - name + type: object + customHeader: + properties: + name: + type: string + required: + - name + type: object + queryString: + properties: + name: + type: string + required: + - name + type: object + type: object + defaults: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + 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). + type: object + jwt: + description: Authentication based on JWT tokens. + properties: + issuerUrl: + description: |- + 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. + type: string + ttl: + description: |- + Decides how long to wait before refreshing the JWKS (in seconds). + If omitted, Authorino will never refresh the JWKS. + type: integer + type: object + kubernetesTokenReview: + description: Authentication by Kubernetes token review. + properties: + audiences: + description: |- + 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. + items: + type: string + type: array + type: object + metrics: + default: false + description: Whether this config should generate individual + observability metrics + type: boolean + oauth2Introspection: + description: Authentication by OAuth2 token introspection. + properties: + credentialsRef: + description: Reference to a Kubernetes secret in the same + namespace, that stores client credentials to the OAuth2 + server. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + endpoint: + description: The full URL of the token introspection endpoint. + type: string + tokenTypeHint: + description: |- + The token type hint for the token introspection. + If omitted, it defaults to "access_token". + type: string + required: + - credentialsRef + - endpoint + type: object + overrides: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + 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). + type: object + plain: + description: |- + 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. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + required: + - selector + type: object + priority: + default: 0 + description: |- + Priority group of the config. + All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + type: integer + when: + description: |- + 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. + items: + properties: + all: + description: A list of pattern expressions to be evaluated + as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to be evaluated + as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + x509: + description: |- + 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. + properties: + allNamespaces: + default: false + description: |- + 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. + type: boolean + selector: + description: |- + Label selector used by Authorino to match secrets from the cluster storing trusted CA certificates to validate + clients trying to authenticate to this service + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - selector + type: object + type: object + description: |- + Authentication configs. + At least one config MUST evaluate to a valid identity object for the auth request to be successful. + type: object + authorization: + additionalProperties: + properties: + cache: + description: |- + Caching options for the resolved object returned when applying this config. + Omit it to avoid caching objects for this config. + properties: + key: + description: |- + Key used to store the entry in the cache. + The resolved key must be unique within the scope of this particular config. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + ttl: + default: 60 + description: Duration (in seconds) of the external data + in the cache before pulled again from the source. + type: integer + required: + - key + type: object + kubernetesSubjectAccessReview: + description: Authorization by Kubernetes SubjectAccessReview + properties: + groups: + description: Groups the user must be a member of or, if + `user` is omitted, the groups to check for authorization + in the Kubernetes RBAC. + items: + type: string + type: array + resourceAttributes: + description: |- + Use resourceAttributes to check permissions on Kubernetes resources. + If omitted, it performs a non-resource SubjectAccessReview, with verb and path inferred from the request. + properties: + group: + description: |- + API group of the resource. + Use '*' for all API groups. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + name: + description: |- + Resource name + Omit it to check for authorization on all resources of the specified kind. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + namespace: + description: Namespace where the user must have permissions + on the resource. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + resource: + description: |- + Resource kind + Use '*' for all resource kinds. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + subresource: + description: Subresource kind + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + verb: + description: |- + Verb to check for authorization on the resource. + Use '*' for all verbs. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + user: + description: |- + User to check for authorization in the Kubernetes RBAC. + Omit it to check for group authorization only. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + metrics: + default: false + description: Whether this config should generate individual + observability metrics + type: boolean + opa: + description: Open Policy Agent (OPA) Rego policy. + properties: + allValues: + default: false + description: |- + 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. + type: boolean + externalPolicy: + description: |- + 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'. + properties: + body: + description: |- + 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). + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + bodyParameters: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + 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). + type: object + contentType: + default: application/x-www-form-urlencoded + description: |- + 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'. + enum: + - application/x-www-form-urlencoded + - application/json + type: string + credentials: + description: |- + 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. + properties: + authorizationHeader: + properties: + prefix: + type: string + type: object + cookie: + properties: + name: + type: string + required: + - name + type: object + customHeader: + properties: + name: + type: string + required: + - name + type: object + queryString: + properties: + name: + type: string + required: + - name + type: object + type: object + expression: + type: string + headers: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: Custom headers in the HTTP request. + type: object + method: + default: GET + description: |- + 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. + enum: + - GET + - POST + - PUT + - PATCH + - DELETE + - HEAD + - OPTIONS + - CONNECT + - TRACE + type: string + oauth2: + description: Authentication with the HTTP service by + OAuth2 Client Credentials grant. + properties: + cache: + default: true + description: |- + Caches and reuses the token until expired. + Set it to false to force fetch the token at every authorization request regardless of expiration. + type: boolean + clientId: + description: OAuth2 Client ID. + type: string + clientSecretRef: + description: Reference to a Kuberentes Secret key + that stores that OAuth2 Client Secret. + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + extraParams: + additionalProperties: + type: string + description: Optional extra parameters for the requests + to the token URL. + type: object + scopes: + description: Optional scopes for the client credentials + grant, if supported by he OAuth2 server. + items: + type: string + type: array + tokenUrl: + description: Token endpoint URL of the OAuth2 resource + server. + type: string + required: + - clientId + - clientSecretRef + - tokenUrl + type: object + sharedSecretRef: + description: |- + 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. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + ttl: + description: Duration (in seconds) of the external data + in the cache before pulled again from the source. + type: integer + url: + description: |- + 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} + type: string + required: + - url + type: object + rego: + description: |- + 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. + type: string + type: object + patternMatching: + description: Pattern-matching authorization rules. + properties: + patterns: + items: + properties: + all: + description: A list of pattern expressions to be evaluated + as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to be evaluated + as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + required: + - patterns + type: object + priority: + default: 0 + description: |- + Priority group of the config. + All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + type: integer + spicedb: + description: Authorization decision delegated to external Authzed/SpiceDB + server. + properties: + endpoint: + description: Hostname and port number to the GRPC interface + of the SpiceDB server (e.g. spicedb:50051). + type: string + insecure: + description: Insecure HTTP connection (i.e. disables TLS + verification) + type: boolean + permission: + description: The name of the permission (or relation) on + which to execute the check. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + resource: + description: The resource on which to check the permission + or relation. + properties: + kind: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + name: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + sharedSecretRef: + description: Reference to a Secret key whose value will + be used by Authorino to authenticate with the Authzed + service. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + subject: + description: The subject that will be checked for the permission + or relation. + properties: + kind: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + name: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + required: + - endpoint + type: object + when: + description: |- + 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. + items: + properties: + all: + description: A list of pattern expressions to be evaluated + as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to be evaluated + as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + type: object + description: |- + Authorization policies. + All policies MUST evaluate to "allowed = true" for the auth request be successful. + type: object + callbacks: + additionalProperties: + properties: + cache: + description: |- + Caching options for the resolved object returned when applying this config. + Omit it to avoid caching objects for this config. + properties: + key: + description: |- + Key used to store the entry in the cache. + The resolved key must be unique within the scope of this particular config. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + ttl: + default: 60 + description: Duration (in seconds) of the external data + in the cache before pulled again from the source. + type: integer + required: + - key + type: object + http: + description: Settings of the external HTTP request + properties: + body: + description: |- + 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). + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + bodyParameters: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + 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). + type: object + contentType: + default: application/x-www-form-urlencoded + description: |- + 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'. + enum: + - application/x-www-form-urlencoded + - application/json + type: string + credentials: + description: |- + 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. + properties: + authorizationHeader: + properties: + prefix: + type: string + type: object + cookie: + properties: + name: + type: string + required: + - name + type: object + customHeader: + properties: + name: + type: string + required: + - name + type: object + queryString: + properties: + name: + type: string + required: + - name + type: object + type: object + expression: + type: string + headers: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: Custom headers in the HTTP request. + type: object + method: + default: GET + description: |- + 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. + enum: + - GET + - POST + - PUT + - PATCH + - DELETE + - HEAD + - OPTIONS + - CONNECT + - TRACE + type: string + oauth2: + description: Authentication with the HTTP service by OAuth2 + Client Credentials grant. + properties: + cache: + default: true + description: |- + Caches and reuses the token until expired. + Set it to false to force fetch the token at every authorization request regardless of expiration. + type: boolean + clientId: + description: OAuth2 Client ID. + type: string + clientSecretRef: + description: Reference to a Kuberentes Secret key that + stores that OAuth2 Client Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + extraParams: + additionalProperties: + type: string + description: Optional extra parameters for the requests + to the token URL. + type: object + scopes: + description: Optional scopes for the client credentials + grant, if supported by he OAuth2 server. + items: + type: string + type: array + tokenUrl: + description: Token endpoint URL of the OAuth2 resource + server. + type: string + required: + - clientId + - clientSecretRef + - tokenUrl + type: object + sharedSecretRef: + description: |- + 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. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + url: + description: |- + 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} + type: string + required: + - url + type: object + metrics: + default: false + description: Whether this config should generate individual + observability metrics + type: boolean + priority: + default: 0 + description: |- + Priority group of the config. + All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + type: integer + when: + description: |- + 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. + items: + properties: + all: + description: A list of pattern expressions to be evaluated + as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to be evaluated + as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + required: + - http + type: object + description: |- + Callback functions. + Authorino sends callbacks at the end of the auth pipeline to the endpoints specified in this config. + type: object + hosts: + description: |- + 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. + items: + type: string + type: array + metadata: + additionalProperties: + properties: + cache: + description: |- + Caching options for the resolved object returned when applying this config. + Omit it to avoid caching objects for this config. + properties: + key: + description: |- + Key used to store the entry in the cache. + The resolved key must be unique within the scope of this particular config. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + ttl: + default: 60 + description: Duration (in seconds) of the external data + in the cache before pulled again from the source. + type: integer + required: + - key + type: object + http: + description: External source of auth metadata via HTTP request + properties: + body: + description: |- + 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). + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + bodyParameters: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + 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). + type: object + contentType: + default: application/x-www-form-urlencoded + description: |- + 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'. + enum: + - application/x-www-form-urlencoded + - application/json + type: string + credentials: + description: |- + 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. + properties: + authorizationHeader: + properties: + prefix: + type: string + type: object + cookie: + properties: + name: + type: string + required: + - name + type: object + customHeader: + properties: + name: + type: string + required: + - name + type: object + queryString: + properties: + name: + type: string + required: + - name + type: object + type: object + expression: + type: string + headers: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: Custom headers in the HTTP request. + type: object + method: + default: GET + description: |- + 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. + enum: + - GET + - POST + - PUT + - PATCH + - DELETE + - HEAD + - OPTIONS + - CONNECT + - TRACE + type: string + oauth2: + description: Authentication with the HTTP service by OAuth2 + Client Credentials grant. + properties: + cache: + default: true + description: |- + Caches and reuses the token until expired. + Set it to false to force fetch the token at every authorization request regardless of expiration. + type: boolean + clientId: + description: OAuth2 Client ID. + type: string + clientSecretRef: + description: Reference to a Kuberentes Secret key that + stores that OAuth2 Client Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + extraParams: + additionalProperties: + type: string + description: Optional extra parameters for the requests + to the token URL. + type: object + scopes: + description: Optional scopes for the client credentials + grant, if supported by he OAuth2 server. + items: + type: string + type: array + tokenUrl: + description: Token endpoint URL of the OAuth2 resource + server. + type: string + required: + - clientId + - clientSecretRef + - tokenUrl + type: object + sharedSecretRef: + description: |- + 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. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + url: + description: |- + 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} + type: string + required: + - url + type: object + metrics: + default: false + description: Whether this config should generate individual + observability metrics + type: boolean + priority: + default: 0 + description: |- + Priority group of the config. + All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + type: integer + uma: + description: User-Managed Access (UMA) source of resource data. + properties: + credentialsRef: + description: Reference to a Kubernetes secret in the same + namespace, that stores client credentials to the resource + registration API of the UMA server. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + endpoint: + description: |- + 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. + type: string + required: + - credentialsRef + - endpoint + type: object + userInfo: + description: OpendID Connect UserInfo linked to an OIDC authentication + config specified in this same AuthConfig. + properties: + identitySource: + description: The name of an OIDC-enabled JWT authentication + config whose OpenID Connect configuration discovered includes + the OIDC "userinfo_endpoint" claim. + type: string + required: + - identitySource + type: object + when: + description: |- + 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. + items: + properties: + all: + description: A list of pattern expressions to be evaluated + as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to be evaluated + as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + type: object + description: |- + Metadata sources. + Authorino fetches auth metadata as JSON from sources specified in this config. + type: object + patterns: + additionalProperties: + items: + properties: + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + description: Named sets of patterns that can be referred in `when` + conditions and in pattern-matching authorization policy rules. + type: object + response: + description: |- + Response items. + Authorino builds custom responses to the client of the auth request. + properties: + success: + description: |- + 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. + properties: + dynamicMetadata: + additionalProperties: + description: Settings of the success custom response item. + properties: + cache: + description: |- + Caching options for the resolved object returned when applying this config. + Omit it to avoid caching objects for this config. + properties: + key: + description: |- + Key used to store the entry in the cache. + The resolved key must be unique within the scope of this particular config. + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + ttl: + default: 60 + description: Duration (in seconds) of the external + data in the cache before pulled again from the + source. + type: integer + required: + - key + type: object + json: + description: |- + 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. + properties: + properties: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + required: + - properties + type: object + key: + description: |- + 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. + type: string + metrics: + default: false + description: Whether this config should generate individual + observability metrics + type: boolean + plain: + description: Plain text content + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + priority: + default: 0 + description: |- + Priority group of the config. + All configs in the same priority group are evaluated concurrently; consecutive priority groups are evaluated sequentially. + type: integer + when: + description: |- + 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. + items: + properties: + all: + description: A list of pattern expressions to + be evaluated as a logical AND. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + any: + description: A list of pattern expressions to + be evaluated as a logical OR. + items: + type: object + x-kubernetes-preserve-unknown-fields: true + type: array + operator: + description: |- + 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) + enum: + - eq + - neq + - incl + - excl + - matches + type: string + patternRef: + description: Reference to a named set of pattern + expressions + type: string + predicate: + type: string + selector: + description: |- + 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. + type: string + value: + description: |- + 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. + type: string + type: object + type: array + wristband: + description: Authorino Festival Wristband token + properties: + customClaims: + additionalProperties: + properties: + expression: + type: string + selector: + description: |- + 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. + type: string + value: + description: Static value + x-kubernetes-preserve-unknown-fields: true + type: object + description: Any claims to be added to the wristband + token apart from the standard JWT claims (iss, + iat, exp) added by default. + type: object + issuer: + description: 'The endpoint to the Authorino service + that issues the wristband (format: ://:/, + where = /://:/, + where = /://:/, + where = /://:/, + where = / 0 { headers := make([]map[string]string, 0) for _, header := range denyWith.Headers { - value, _ := json.StringifyJSON(header.Value.ResolveFor(authJSON)) + resolved, _ := header.Value.ResolveFor(authJSON) + value, _ := json.StringifyJSON(resolved) headers = append(headers, map[string]string{header.Name: value}) } authResult.Headers = headers diff --git a/pkg/service/auth_pipeline_test.go b/pkg/service/auth_pipeline_test.go index 446ddad7..d3feadf9 100644 --- a/pkg/service/auth_pipeline_test.go +++ b/pkg/service/auth_pipeline_test.go @@ -340,8 +340,8 @@ func TestEvaluateWithCustomDenyOptions(t *testing.T) { Unauthenticated: &evaluators.DenyWithValues{ Code: 302, Headers: []json.JSONProperty{ - {Name: "X-Static-Header", Value: json.JSONValue{Static: "some-value"}}, - {Name: "Location", Value: json.JSONValue{Pattern: "https://my-app.io/login?redirect_to=https://{context.request.http.host}{context.request.http.path}"}}, + {Name: "X-Static-Header", Value: &json.JSONValue{Static: "some-value"}}, + {Name: "Location", Value: &json.JSONValue{Pattern: "https://my-app.io/login?redirect_to=https://{context.request.http.host}{context.request.http.path}"}}, }, Body: &json.JSONValue{ Static: authConfigStaticResponse, diff --git a/pkg/service/auth_test.go b/pkg/service/auth_test.go index 26366e04..d23ce0f7 100644 --- a/pkg/service/auth_test.go +++ b/pkg/service/auth_test.go @@ -256,7 +256,7 @@ func TestAuthServiceRawHTTPAuthorization_WithHeaders(t *testing.T) { Wrapper: "httpHeader", WrapperKey: "x-auth-data", DynamicJSON: &response.DynamicJSON{ - Properties: []json.JSONProperty{{Name: "headers", Value: json.JSONValue{Pattern: "context.request.http.headers"}}}, + Properties: []json.JSONProperty{{Name: "headers", Value: &json.JSONValue{Pattern: "context.request.http.headers"}}}, }, }} indexMock := mock_index.NewMockIndex(mockController) diff --git a/tests/v1beta3/authconfig.yaml b/tests/v1beta3/authconfig.yaml index 6a6c067a..c10fdc6d 100644 --- a/tests/v1beta3/authconfig.yaml +++ b/tests/v1beta3/authconfig.yaml @@ -8,13 +8,9 @@ spec: patterns: admin-path: - - selector: context.request.http.path - operator: matches - value: ^/admin(/.*)?$ + - predicate: request.http.path.matches("^/admin(/.*)?$") resource-path: - - selector: context.request.http.path - operator: matches - value: ^/greetings/\d+$ + - predicate: request.http.path.matches("^/greetings/\d+$") authentication: k8s-auth: @@ -25,8 +21,7 @@ spec: kubernetes-rbac: value: true username: - selector: auth.identity.user.username - value: null + expression: auth.identity.user.username api-key: apiKey: selector: @@ -39,7 +34,7 @@ spec: kubernetes-rbac: value: true username: - selector: auth.identity.metadata.annotations.username + expression: auth.identity.metadata.annotations.username keycloak: jwt: issuerUrl: http://keycloak.authorino.svc.cluster.local:8080/realms/kuadrant @@ -48,9 +43,9 @@ spec: jwt-rbac: value: true roles: - selector: auth.identity.realm_access.roles + expression: auth.identity.realm_access.roles username: - selector: auth.identity.preferred_username + expression: auth.identity.preferred_username oauth2-introspection: oauth2Introspection: credentialsRef: @@ -64,22 +59,18 @@ spec: jwt-rbac: value: true roles: - selector: auth.identity.realm_access.roles + expression: auth.identity.realm_access.roles username: - selector: auth.identity.preferred_username + expression: auth.identity.preferred_username cache: key: - selector: context.request.http.headers.authorization + expression: request.http.headers.authorization anonymous: anonymous: {} priority: 1 when: - - selector: context.request.http.method - operator: eq - value: GET - - selector: context.request.http.path - operator: matches - value: ^/$ + - predicate: request.http.method == "GET" + - predicate: request.http.path.matches("^/$") defaults: username: value: global @@ -95,13 +86,13 @@ spec: url: http://ip-location.authorino.svc.cluster.local:3000/{context.request.http.headers.x-forwarded-for.@extract:{"sep":","}} cache: key: - selector: context.request.http.headers.x-forwarded-for.@extract:{"sep":","} + selector: request.http.headers.x-forwarded-for.@extract:{"sep":","} user-info: userInfo: identitySource: keycloak cache: key: - selector: context.request.http.headers.authorization + expression: request.http.headers.authorization resource-info: when: - patternRef: resource-path @@ -111,7 +102,7 @@ spec: endpoint: http://keycloak.authorino.svc.cluster.local:8080/realms/kuadrant cache: key: - selector: context.request.http.path + expression: request.http.path authorization: allowed-methods: @@ -131,23 +122,17 @@ spec: admin-kubernetes-rbac: when: - patternRef: admin-path - - selector: auth.identity.kubernetes-rbac - operator: eq - value: 'true' + - predicate: auth.identity.kubernetes-rbac kubernetesSubjectAccessReview: user: - selector: auth.identity.username + expression: auth.identity.username admin-jwt-rbac: when: - patternRef: admin-path - - selector: auth.identity.jwt-rbac - operator: eq - value: 'true' + - predicate: auth.identity.jwt-rbac patternMatching: patterns: - - selector: auth.identity.roles - operator: incl - value: admin + - predicate: auth.identity.roles.exists("admin") resource-owner: when: - patternRef: resource-path @@ -180,40 +165,38 @@ spec: value: Authorino x-username: plain: - selector: auth.identity.username + expression: auth.identity.username x-auth-data: json: properties: username: - selector: auth.identity.username + expression: auth.identity.username geo: - selector: auth.metadata.geo-info + expression: auth.metadata.geo-info timestamp: - selector: auth.authorization.timestamp.now + expression: auth.authorization.timestamp.now wristband: wristband: issuer: https://authorino-authorino-oidc.authorino.svc.cluster.local:8083/authorino/e2e-test/wristband tokenDuration: 300 customClaims: username: - selector: auth.identity.username + expression: auth.identity.username uri: - selector: context.request.http.path + expression: request.http.path scope: - selector: context.request.http.method.@case:lower + selector: request.http.method.@case:lower signingKeyRefs: - name: wristband-signing-key algorithm: ES256 when: - - selector: auth.identity.anonymous - operator: neq - value: 'true' + - predicate: auth.identity.anonymous == false dynamicMetadata: rate-limit-data: json: properties: username: - selector: auth.identity.username + expression: auth.identity.username key: ext_auth_data --- apiVersion: v1