add field and label selectors to authorization attributes

Co-authored-by: Jordan Liggitt <liggitt@google.com>
This commit is contained in:
David Eads
2024-05-23 15:12:26 -04:00
committed by Jordan Liggitt
parent f5e5bef2e0
commit 92e3445e9d
25 changed files with 2388 additions and 226 deletions

View File

@@ -29,6 +29,50 @@ func TestValidateSARSpec(t *testing.T) {
successCases := []authorizationapi.SubjectAccessReviewSpec{
{ResourceAttributes: &authorizationapi.ResourceAttributes{}, User: "me"},
{NonResourceAttributes: &authorizationapi.NonResourceAttributes{}, Groups: []string{"my-group"}},
{ // field raw selector
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
RawSelector: "***foo",
},
},
},
{ // label raw selector
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
RawSelector: "***foo",
},
},
},
{ // unknown field operator
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "k",
Operator: metav1.FieldSelectorOperator("fake"),
Values: []string{"val"},
},
},
},
},
},
{ // unknown label operator
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "k",
Operator: metav1.LabelSelectorOperator("fake"),
Values: []string{"val"},
},
},
},
},
},
}
for _, successCase := range successCases {
if errs := ValidateSubjectAccessReviewSpec(successCase, field.NewPath("spec")); len(errs) != 0 {
@@ -58,29 +102,257 @@ func TestValidateSARSpec(t *testing.T) {
ResourceAttributes: &authorizationapi.ResourceAttributes{},
},
msg: `spec.user: Invalid value: "": at least one of user or group must be specified`,
}, {
name: "resource attributes: field selector specify both",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
RawSelector: "foo",
Requirements: []metav1.FieldSelectorRequirement{
{},
},
},
},
},
msg: `spec.resourceAttributes.fieldSelector.rawSelector: Invalid value: "foo": may not specified at the same time as requirements`,
}, {
name: "resource attributes: field selector specify neither",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{},
},
},
msg: `spec.resourceAttributes.fieldSelector.requirements: Required value: when spec.resourceAttributes.fieldSelector is specified, requirements or rawSelector is required`,
}, {
name: "resource attributes: field selector no key",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "",
},
},
},
},
},
msg: `spec.resourceAttributes.fieldSelector.requirements[0].key: Required value: must be specified`,
}, {
name: "resource attributes: field selector no value for in",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "k",
Operator: metav1.FieldSelectorOpIn,
Values: []string{},
},
},
},
},
},
msg: "spec.resourceAttributes.fieldSelector.requirements[0].values: Required value: must be specified when `operator` is 'In' or 'NotIn'",
}, {
name: "resource attributes: field selector no value for not in",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "k",
Operator: metav1.FieldSelectorOpNotIn,
Values: []string{},
},
},
},
},
},
msg: "spec.resourceAttributes.fieldSelector.requirements[0].values: Required value: must be specified when `operator` is 'In' or 'NotIn'",
}, {
name: "resource attributes: field selector values for exists",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "k",
Operator: metav1.FieldSelectorOpExists,
Values: []string{"val"},
},
},
},
},
},
msg: "spec.resourceAttributes.fieldSelector.requirements[0].values: Forbidden: may not be specified when `operator` is 'Exists' or 'DoesNotExist'",
}, {
name: "resource attributes: field selector values for not exists",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
FieldSelector: &authorizationapi.FieldSelectorAttributes{
Requirements: []metav1.FieldSelectorRequirement{
{
Key: "k",
Operator: metav1.FieldSelectorOpDoesNotExist,
Values: []string{"val"},
},
},
},
},
},
msg: "spec.resourceAttributes.fieldSelector.requirements[0].values: Forbidden: may not be specified when `operator` is 'Exists' or 'DoesNotExist'",
}, {
name: "resource attributes: label selector specify both",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
RawSelector: "foo",
Requirements: []metav1.LabelSelectorRequirement{
{},
},
},
},
},
msg: `spec.resourceAttributes.labelSelector.rawSelector: Invalid value: "foo": may not specified at the same time as requirements`,
}, {
name: "resource attributes: label selector specify neither",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{},
},
},
msg: `spec.resourceAttributes.labelSelector.requirements: Required value: when spec.resourceAttributes.labelSelector is specified, requirements or rawSelector is required`,
}, {
name: "resource attributes: label selector no key",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "",
},
},
},
},
},
msg: `spec.resourceAttributes.labelSelector.requirements[0].key: Invalid value: "": name part must be non-empty`,
}, {
name: "resource attributes: label selector invalid label name",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "()foo",
},
},
},
},
},
msg: `spec.resourceAttributes.labelSelector.requirements[0].key: Invalid value: "()foo": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`,
}, {
name: "resource attributes: label selector no value for in",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "k",
Operator: metav1.LabelSelectorOpIn,
Values: []string{},
},
},
},
},
},
msg: "spec.resourceAttributes.labelSelector.requirements[0].values: Required value: must be specified when `operator` is 'In' or 'NotIn'",
}, {
name: "resource attributes: label selector no value for not in",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "k",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{},
},
},
},
},
},
msg: "spec.resourceAttributes.labelSelector.requirements[0].values: Required value: must be specified when `operator` is 'In' or 'NotIn'",
}, {
name: "resource attributes: label selector values for exists",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "k",
Operator: metav1.LabelSelectorOpExists,
Values: []string{"val"},
},
},
},
},
},
msg: "spec.resourceAttributes.labelSelector.requirements[0].values: Forbidden: may not be specified when `operator` is 'Exists' or 'DoesNotExist'",
}, {
name: "resource attributes: label selector values for not exists",
obj: authorizationapi.SubjectAccessReviewSpec{
User: "me",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
Requirements: []metav1.LabelSelectorRequirement{
{
Key: "k",
Operator: metav1.LabelSelectorOpDoesNotExist,
Values: []string{"val"},
},
},
},
},
},
msg: "spec.resourceAttributes.labelSelector.requirements[0].values: Forbidden: may not be specified when `operator` is 'Exists' or 'DoesNotExist'",
}}
for _, c := range errorCases {
errs := ValidateSubjectAccessReviewSpec(c.obj, field.NewPath("spec"))
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
errs = ValidateSubjectAccessReview(&authorizationapi.SubjectAccessReview{Spec: c.obj})
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
errs = ValidateLocalSubjectAccessReview(&authorizationapi.LocalSubjectAccessReview{Spec: c.obj})
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
t.Run(c.name, func(t *testing.T) {
errs := ValidateSubjectAccessReviewSpec(c.obj, field.NewPath("spec"))
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
errs = ValidateSubjectAccessReview(&authorizationapi.SubjectAccessReview{Spec: c.obj})
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
errs = ValidateLocalSubjectAccessReview(&authorizationapi.LocalSubjectAccessReview{Spec: c.obj})
if len(errs) == 0 {
t.Errorf("%s: expected failure for %q", c.name, c.msg)
} else if !strings.Contains(errs[0].Error(), c.msg) {
t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg)
}
})
}
}
@@ -109,6 +381,20 @@ func TestValidateSelfSAR(t *testing.T) {
NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
},
msg: "cannot be specified in combination with resourceAttributes",
}, {
// here we only test one to be sure the function is called. The more exhaustive suite is tested above.
name: "resource attributes: label selector specify both",
obj: authorizationapi.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
RawSelector: "foo",
Requirements: []metav1.LabelSelectorRequirement{
{},
},
},
},
},
msg: `spec.resourceAttributes.labelSelector.rawSelector: Invalid value: "foo": may not specified at the same time as requirements`,
}}
for _, c := range errorCases {
@@ -175,6 +461,23 @@ func TestValidateLocalSAR(t *testing.T) {
},
},
msg: "disallowed on this kind of request",
}, {
// here we only test one to be sure the function is called. The more exhaustive suite is tested above.
name: "resource attributes: label selector specify both",
obj: &authorizationapi.LocalSubjectAccessReview{
Spec: authorizationapi.SubjectAccessReviewSpec{
User: "user",
ResourceAttributes: &authorizationapi.ResourceAttributes{
LabelSelector: &authorizationapi.LabelSelectorAttributes{
RawSelector: "foo",
Requirements: []metav1.LabelSelectorRequirement{
{},
},
},
},
},
},
msg: `spec.resourceAttributes.labelSelector.rawSelector: Invalid value: "foo": may not specified at the same time as requirements`,
}}
for _, c := range errorCases {