Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AuthPolicy top-level conditions based on CEL only #988

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 5 additions & 77 deletions api/v1beta3/authpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"github.com/google/go-cmp/cmp"
authorinov1beta3 "github.com/kuadrant/authorino/api/v1beta3"
"github.com/kuadrant/policy-machinery/machinery"
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
Expand All @@ -42,43 +41,6 @@
AuthPolicyDirectReferenceAnnotationName = "kuadrant.io/authpolicy"
)

const (
EqualOperator WhenConditionOperator = "eq"
NotEqualOperator WhenConditionOperator = "neq"
StartsWithOperator WhenConditionOperator = "startsWith"
EndsWithOperator WhenConditionOperator = "endsWith"
IncludeOperator WhenConditionOperator = "incl"
ExcludeOperator WhenConditionOperator = "excl"
MatchesOperator WhenConditionOperator = "matches"
)

// +kubebuilder:validation:Enum:=eq;neq;startswith;endswith;incl;excl;matches
type WhenConditionOperator string

// ContextSelector defines one item from the well known attributes
// Attributes: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
// Well-known selectors: https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors
// They are named by a dot-separated path (e.g. request.path)
// Example: "request.path" -> The path portion of the URL
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
type ContextSelector string

// WhenCondition defines semantics for matching an HTTP request based on conditions
// https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteSpec
type WhenCondition struct {
// Selector defines one item from the well known selectors
// TODO Document properly "Well-known selector" https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors
Selector ContextSelector `json:"selector"`

// The binary operator to be applied to the content fetched from the selector
// Possible values are: "eq" (equal to), "neq" (not equal to)
Operator WhenConditionOperator `json:"operator"`

// The value of reference for the comparison.
Value string `json:"value"`
}

var (
AuthPolicyGroupKind = schema.GroupKind{Group: SchemeGroupVersion.Group, Kind: "AuthPolicy"}
AuthPoliciesResource = SchemeGroupVersion.WithResource("authpolicies")
Expand Down Expand Up @@ -170,9 +132,8 @@
rules[fmt.Sprintf("patterns#%s", ruleID)] = kuadrantv1.NewMergeableRule(&rule, policyLocator)
}

for ruleID := range spec.Conditions {
rule := spec.Conditions[ruleID]
rules[fmt.Sprintf("conditions#%d", ruleID)] = kuadrantv1.NewMergeableRule(&rule, policyLocator)
if whenPredicates := spec.MergeableWhenPredicates; len(whenPredicates.Predicates) > 0 {
rules["conditions#"] = kuadrantv1.NewMergeableRule(&whenPredicates, policyLocator)

Check warning on line 136 in api/v1beta3/authpolicy_types.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta3/authpolicy_types.go#L135-L136

Added lines #L135 - L136 were not covered by tests
}

if spec.AuthScheme == nil {
Expand Down Expand Up @@ -226,7 +187,7 @@
func (p *AuthPolicy) SetRules(rules map[string]kuadrantv1.MergeableRule) {
// clear all rules of the policy before setting new ones
p.Spec.Proper().NamedPatterns = nil
p.Spec.Proper().Conditions = nil
p.Spec.Proper().Predicates = nil

Check warning on line 190 in api/v1beta3/authpolicy_types.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta3/authpolicy_types.go#L190

Added line #L190 was not covered by tests
p.Spec.Proper().AuthScheme = nil

ensureNamedPatterns := func() {
Expand Down Expand Up @@ -305,7 +266,7 @@
ensureNamedPatterns()
p.Spec.Proper().NamedPatterns[ruleID] = *rule.(*MergeablePatternExpressions)
case "conditions":
p.Spec.Proper().Conditions = append(p.Spec.Proper().Conditions, *rule.(*MergeablePatternExpressionOrRef))
p.Spec.Proper().MergeableWhenPredicates = *rule.(*MergeableWhenPredicates)

Check warning on line 269 in api/v1beta3/authpolicy_types.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta3/authpolicy_types.go#L269

Added line #L269 was not covered by tests
case "authentication":
ensureAuthentication()
p.Spec.Proper().AuthScheme.Authentication[ruleID] = *rule.(*MergeableAuthenticationSpec)
Expand Down Expand Up @@ -429,7 +390,7 @@
// If omitted, the AuthPolicy will be enforced at all requests to the protected routes.
// If present, all conditions must match for the AuthPolicy to be enforced; otherwise, the authorization service skips the AuthPolicy and returns to the auth request with status OK.
// +optional
Conditions []MergeablePatternExpressionOrRef `json:"when,omitempty"`
MergeableWhenPredicates `json:""`

// The auth rules of the policy.
// See Authorino's AuthConfig CRD for more details.
Expand Down Expand Up @@ -490,39 +451,6 @@
r.Source = source
return r
}
func (r *MergeablePatternExpressionOrRef) ToWhenConditions(namedPatterns map[string]MergeablePatternExpressions) []WhenCondition {
if ref := r.PatternRef.Name; ref != "" {
if pattern, ok := namedPatterns[ref]; ok {
return lo.Map(pattern.PatternExpressions, func(p authorinov1beta3.PatternExpression, _ int) WhenCondition {
return WhenCondition{
Selector: ContextSelector(p.Selector),
Operator: WhenConditionOperator(p.Operator),
Value: p.Value,
}
})
}
}

if allOf := r.All; len(allOf) > 0 {
return lo.Map(allOf, func(p authorinov1beta3.UnstructuredPatternExpressionOrRef, _ int) WhenCondition {
return WhenCondition{
Selector: ContextSelector(p.Selector),
Operator: WhenConditionOperator(p.Operator),
Value: p.Value,
}
})
}

// FIXME: anyOf cannot be represented in the current schema of the wasm config

return []WhenCondition{
{
Selector: ContextSelector(r.Selector),
Operator: WhenConditionOperator(r.Operator),
Value: r.Value,
},
}
}

type MergeableAuthenticationSpec struct {
authorinov1beta3.AuthenticationSpec `json:",inline"`
Expand Down
66 changes: 66 additions & 0 deletions api/v1beta3/common_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package v1beta3

import (
"github.com/samber/lo"

kuadrantv1 "github.com/kuadrant/kuadrant-operator/api/v1"
)

func NewPredicate(predicate string) Predicate {
return Predicate{Predicate: predicate}
}

// Predicate defines one CEL expression that must be evaluated to bool
type Predicate struct {
// +kubebuilder:validation:MinLength=1
Predicate string `json:"predicate"`
}

func NewWhenPredicates(predicates ...string) WhenPredicates {
whenPredicates := make(WhenPredicates, 0)
for _, predicate := range predicates {
whenPredicates = append(whenPredicates, NewPredicate(predicate))
}

return whenPredicates
}

type WhenPredicates []Predicate

func (w WhenPredicates) Extend(other WhenPredicates) WhenPredicates {
return append(w, other...)
}

func (w WhenPredicates) Into() []string {
if w == nil {
return nil
}

return lo.Map(w, func(p Predicate, _ int) string { return p.Predicate })
}

type MergeableWhenPredicates struct {
// Overall conditions for the policy to be enforced.
// If omitted, the policy will be enforced at all requests to the protected routes.
// If present, all conditions must match for the policy to be enforced.
// +optional
Predicates WhenPredicates `json:"when,omitempty"`

// Source stores the locator of the policy where the limit is orignaly defined (internal use)
Source string `json:"-"`
}

var _ kuadrantv1.MergeableRule = &MergeableWhenPredicates{}

func (p *MergeableWhenPredicates) GetSpec() any {
return p.Predicates
}

func (p *MergeableWhenPredicates) GetSource() string {
return p.Source
}

func (p *MergeableWhenPredicates) WithSource(source string) kuadrantv1.MergeableRule {
p.Source = source
return p
}
74 changes: 8 additions & 66 deletions api/v1beta3/ratelimitpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"time"

"github.com/kuadrant/policy-machinery/machinery"
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
Expand Down Expand Up @@ -120,16 +119,14 @@ func (p *RateLimitPolicy) Empty() bool {
func (p *RateLimitPolicy) Rules() map[string]kuadrantv1.MergeableRule {
rules := make(map[string]kuadrantv1.MergeableRule)
policyLocator := p.GetLocator()
spec := p.Spec.Proper()

if len(p.Spec.Proper().When) > 0 {
rules[RulesKeyTopLevelPredicates] = kuadrantv1.NewMergeableRule(
&WhenPredicatesMergeableRule{When: p.Spec.Proper().When, Source: policyLocator},
policyLocator,
)
if whenPredicates := spec.MergeableWhenPredicates; len(whenPredicates.Predicates) > 0 {
rules[RulesKeyTopLevelPredicates] = kuadrantv1.NewMergeableRule(&whenPredicates, policyLocator)
}

for ruleID := range p.Spec.Proper().Limits {
limit := p.Spec.Proper().Limits[ruleID]
for ruleID := range spec.Limits {
limit := spec.Limits[ruleID]
rules[ruleID] = kuadrantv1.NewMergeableRule(&limit, policyLocator)
}

Expand All @@ -139,15 +136,15 @@ func (p *RateLimitPolicy) Rules() map[string]kuadrantv1.MergeableRule {
func (p *RateLimitPolicy) SetRules(rules map[string]kuadrantv1.MergeableRule) {
// clear all rules of the policy before setting new ones
p.Spec.Proper().Limits = nil
p.Spec.Proper().When = nil
p.Spec.Proper().Predicates = nil

if len(rules) > 0 {
p.Spec.Proper().Limits = make(map[string]Limit)
}

for ruleID := range rules {
if ruleID == RulesKeyTopLevelPredicates {
p.Spec.Proper().When = rules[ruleID].(*WhenPredicatesMergeableRule).When
p.Spec.Proper().MergeableWhenPredicates = *rules[ruleID].(*MergeableWhenPredicates)
} else {
p.Spec.Proper().Limits[ruleID] = *rules[ruleID].(*Limit)
}
Expand Down Expand Up @@ -238,68 +235,13 @@ type MergeableRateLimitPolicySpec struct {
type RateLimitPolicySpecProper struct {
// When holds a list of "top-level" `Predicate`s
// +optional
When WhenPredicates `json:"when,omitempty"`
MergeableWhenPredicates `json:""`

// Limits holds the struct of limits indexed by a unique name
// +optional
Limits map[string]Limit `json:"limits,omitempty"`
}

// Predicate defines one CEL expression that must be evaluated to bool
type Predicate struct {
// +kubebuilder:validation:MinLength=1
Predicate string `json:"predicate"`
}

func NewPredicate(predicate string) Predicate {
return Predicate{Predicate: predicate}
}

type WhenPredicates []Predicate

func NewWhenPredicates(predicates ...string) WhenPredicates {
whenPredicates := make(WhenPredicates, 0)
for _, predicate := range predicates {
whenPredicates = append(whenPredicates, NewPredicate(predicate))
}

return whenPredicates
}

func (w WhenPredicates) Extend(other WhenPredicates) WhenPredicates {
return append(w, other...)
}

func (w WhenPredicates) Into() []string {
if w == nil {
return nil
}

return lo.Map(w, func(p Predicate, _ int) string { return p.Predicate })
}

type WhenPredicatesMergeableRule struct {
When WhenPredicates

// Source stores the locator of the policy where the limit is orignaly defined (internal use)
Source string
}

var _ kuadrantv1.MergeableRule = &WhenPredicatesMergeableRule{}

func (w *WhenPredicatesMergeableRule) GetSpec() any {
return w.When
}

func (w *WhenPredicatesMergeableRule) GetSource() string {
return w.Source
}

func (w *WhenPredicatesMergeableRule) WithSource(source string) kuadrantv1.MergeableRule {
w.Source = source
return w
}

type Counter struct {
Expression Expression `json:"expression"`
}
Expand Down
Loading
Loading