Skip to content

Commit

Permalink
drop user-defined top-level conditions in the authpolicy if favour of…
Browse files Browse the repository at this point in the history
… cel predicates only

Signed-off-by: Guilherme Cassolato <[email protected]>
  • Loading branch information
guicassolato committed Nov 7, 2024
1 parent a925273 commit a189890
Show file tree
Hide file tree
Showing 18 changed files with 239 additions and 720 deletions.
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 @@ import (
"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 @@ const (
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 @@ func (p *AuthPolicy) Rules() map[string]kuadrantv1.MergeableRule {
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) Rules() map[string]kuadrantv1.MergeableRule {
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 @@ func (p *AuthPolicy) SetRules(rules map[string]kuadrantv1.MergeableRule) {
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 @@ type AuthPolicySpecProper struct {
// 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 @@ func (r *MergeablePatternExpressionOrRef) WithSource(source string) kuadrantv1.M
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

0 comments on commit a189890

Please sign in to comment.