Skip to content

Commit cc2ba35

Browse files
deads2kk8s-publishing-bot
authored andcommitted
add field and label selectors to authorization attributes
Co-authored-by: Jordan Liggitt <[email protected]> Kubernetes-commit: 92e3445e9d7a587ddb56b3ff4b1445244fbf9abd
1 parent ce76a8f commit cc2ba35

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

Diff for: pkg/apis/meta/v1/validation/validation.go

+40-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ import (
3232
type LabelSelectorValidationOptions struct {
3333
// Allow invalid label value in selector
3434
AllowInvalidLabelValueInSelector bool
35+
36+
// Allows an operator that is not interpretable to pass validation. This is useful for cases where a broader check
37+
// can be performed, as in a *SubjectAccessReview
38+
AllowUnknownOperatorInRequirement bool
3539
}
3640

3741
// LabelSelectorHasInvalidLabelValue returns true if the given selector contains an invalid label value in a match expression.
@@ -79,7 +83,9 @@ func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, opts L
7983
allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
8084
}
8185
default:
82-
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
86+
if !opts.AllowUnknownOperatorInRequirement {
87+
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
88+
}
8389
}
8490
allErrs = append(allErrs, ValidateLabelName(sr.Key, fldPath.Child("key"))...)
8591
if !opts.AllowInvalidLabelValueInSelector {
@@ -113,6 +119,39 @@ func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorLi
113119
return allErrs
114120
}
115121

122+
// FieldSelectorValidationOptions is a struct that can be passed to ValidateFieldSelectorRequirement to record the validate options
123+
type FieldSelectorValidationOptions struct {
124+
// Allows an operator that is not interpretable to pass validation. This is useful for cases where a broader check
125+
// can be performed, as in a *SubjectAccessReview
126+
AllowUnknownOperatorInRequirement bool
127+
}
128+
129+
// ValidateLabelSelectorRequirement validates the requirement according to the opts and returns any validation errors.
130+
func ValidateFieldSelectorRequirement(requirement metav1.FieldSelectorRequirement, opts FieldSelectorValidationOptions, fldPath *field.Path) field.ErrorList {
131+
allErrs := field.ErrorList{}
132+
133+
if len(requirement.Key) == 0 {
134+
allErrs = append(allErrs, field.Required(fldPath.Child("key"), "must be specified"))
135+
}
136+
137+
switch requirement.Operator {
138+
case metav1.FieldSelectorOpIn, metav1.FieldSelectorOpNotIn:
139+
if len(requirement.Values) == 0 {
140+
allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
141+
}
142+
case metav1.FieldSelectorOpExists, metav1.FieldSelectorOpDoesNotExist:
143+
if len(requirement.Values) > 0 {
144+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
145+
}
146+
default:
147+
if !opts.AllowUnknownOperatorInRequirement {
148+
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), requirement.Operator, "not a valid selector operator"))
149+
}
150+
}
151+
152+
return allErrs
153+
}
154+
116155
func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList {
117156
allErrs := field.ErrorList{}
118157
//lint:file-ignore SA1019 Keep validation for deprecated OrphanDependents option until it's being removed

Diff for: pkg/apis/meta/v1/validation/validation_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func TestLabelSelectorMatchExpression(t *testing.T) {
470470
}}
471471
for index, testCase := range testCases {
472472
t.Run(testCase.name, func(t *testing.T) {
473-
allErrs := ValidateLabelSelector(testCase.labelSelector, LabelSelectorValidationOptions{false}, field.NewPath("labelSelector"))
473+
allErrs := ValidateLabelSelector(testCase.labelSelector, LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, field.NewPath("labelSelector"))
474474
if len(allErrs) != testCase.wantErrorNumber {
475475
t.Errorf("case[%d]: expected failure", index)
476476
}

Diff for: pkg/labels/selector.go

+20
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ var (
4545
// Requirements is AND of all requirements.
4646
type Requirements []Requirement
4747

48+
func (r Requirements) String() string {
49+
var sb strings.Builder
50+
51+
for i, requirement := range r {
52+
if i > 0 {
53+
sb.WriteString(", ")
54+
}
55+
sb.WriteString(requirement.String())
56+
}
57+
58+
return sb.String()
59+
}
60+
4861
// Selector represents a label selector.
4962
type Selector interface {
5063
// Matches returns true if this selector matches the given set of labels.
@@ -285,6 +298,13 @@ func (r *Requirement) Values() sets.String {
285298
return ret
286299
}
287300

301+
// ValuesUnsorted returns a copy of requirement values as passed to NewRequirement without sorting.
302+
func (r *Requirement) ValuesUnsorted() []string {
303+
ret := make([]string, 0, len(r.strValues))
304+
ret = append(ret, r.strValues...)
305+
return ret
306+
}
307+
288308
// Equal checks the equality of requirement.
289309
func (r Requirement) Equal(x Requirement) bool {
290310
if r.key != x.key {

0 commit comments

Comments
 (0)