Merge pull request #63403 from deads2k/server-14-creationpower

Automatic merge from submit-queue (batch tested with PRs 63258, 63398, 63403). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

don't block creation on lack of delete powers

Create and delete aren't the same thing, but the alternatives seem worse. This stops checking for deletion powers on create.  You still have to know the UID to create an effective ownerref, so name hunting is unrealistic.

@kubernetes/sig-api-machinery-pr-reviews 

```release-note
owner references can be set during creation without deletion power
```
This commit is contained in:
Kubernetes Submit Queue 2018-05-03 12:48:16 -07:00 committed by GitHub
commit 88c25ca2d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 32 deletions

View File

@ -95,21 +95,26 @@ func (a *gcPermissionsEnforcement) Validate(attributes admission.Attributes) (er
return nil return nil
} }
deleteAttributes := authorizer.AttributesRecord{ // if you are creating a thing, you should always be allowed to set an owner ref since you logically had the power
User: attributes.GetUserInfo(), // to never create it. We still need to check block owner deletion below, because the power to delete does not
Verb: "delete", // imply the power to prevent deletion on other resources.
Namespace: attributes.GetNamespace(), if attributes.GetOperation() != admission.Create {
APIGroup: attributes.GetResource().Group, deleteAttributes := authorizer.AttributesRecord{
APIVersion: attributes.GetResource().Version, User: attributes.GetUserInfo(),
Resource: attributes.GetResource().Resource, Verb: "delete",
Subresource: attributes.GetSubresource(), Namespace: attributes.GetNamespace(),
Name: attributes.GetName(), APIGroup: attributes.GetResource().Group,
ResourceRequest: true, APIVersion: attributes.GetResource().Version,
Path: "", Resource: attributes.GetResource().Resource,
} Subresource: attributes.GetSubresource(),
decision, reason, err := a.authorizer.Authorize(deleteAttributes) Name: attributes.GetName(),
if decision != authorizer.DecisionAllow { ResourceRequest: true,
return admission.NewForbidden(attributes, fmt.Errorf("cannot set an ownerRef on a resource you can't delete: %v, %v", reason, err)) Path: "",
}
decision, reason, err := a.authorizer.Authorize(deleteAttributes)
if decision != authorizer.DecisionAllow {
return admission.NewForbidden(attributes, fmt.Errorf("cannot set an ownerRef on a resource you can't delete: %v, %v", reason, err))
}
} }
// Further check if the user is setting ownerReference.blockOwnerDeletion to // Further check if the user is setting ownerReference.blockOwnerDeletion to
@ -119,7 +124,7 @@ func (a *gcPermissionsEnforcement) Validate(attributes admission.Attributes) (er
for _, ref := range newBlockingRefs { for _, ref := range newBlockingRefs {
records, err := a.ownerRefToDeleteAttributeRecords(ref, attributes) records, err := a.ownerRefToDeleteAttributeRecords(ref, attributes)
if err != nil { if err != nil {
return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion in this case because cannot find RESTMapping for APIVersion %s Kind %s: %v, %v", ref.APIVersion, ref.Kind, reason, err)) return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion in this case because cannot find RESTMapping for APIVersion %s Kind %s: %v", ref.APIVersion, ref.Kind, err))
} }
// Multiple records are returned if ref.Kind could map to multiple // Multiple records are returned if ref.Kind could map to multiple
// resources. User needs to have delete permission on all the // resources. User needs to have delete permission on all the

View File

@ -102,6 +102,9 @@ func TestGCAdmission(t *testing.T) {
return err == nil return err == nil
} }
expectCantSetOwnerRefError := func(err error) bool { expectCantSetOwnerRefError := func(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete") return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete")
} }
tests := []struct { tests := []struct {
@ -140,7 +143,7 @@ func TestGCAdmission(t *testing.T) {
username: "non-deleter", username: "non-deleter",
resource: api.SchemeGroupVersion.WithResource("pods"), resource: api.SchemeGroupVersion.WithResource("pods"),
newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
checkError: expectCantSetOwnerRefError, checkError: expectNoError,
}, },
{ {
name: "non-pod-deleter, create, no objectref change", name: "non-pod-deleter, create, no objectref change",
@ -154,7 +157,7 @@ func TestGCAdmission(t *testing.T) {
username: "non-pod-deleter", username: "non-pod-deleter",
resource: api.SchemeGroupVersion.WithResource("pods"), resource: api.SchemeGroupVersion.WithResource("pods"),
newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
checkError: expectCantSetOwnerRefError, checkError: expectNoError,
}, },
{ {
name: "non-pod-deleter, create, objectref change, but not a pod", name: "non-pod-deleter, create, objectref change, but not a pod",
@ -254,23 +257,26 @@ func TestGCAdmission(t *testing.T) {
checkError: expectNoError, checkError: expectNoError,
}, },
} }
gcAdmit, err := newGCPermissionsEnforcement()
if err != nil {
t.Error(err)
}
for _, tc := range tests { for _, tc := range tests {
operation := admission.Create t.Run(tc.name, func(t *testing.T) {
if tc.oldObj != nil { gcAdmit, err := newGCPermissionsEnforcement()
operation = admission.Update if err != nil {
} t.Error(err)
user := &user.DefaultInfo{Name: tc.username} }
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, user)
err := gcAdmit.Validate(attributes) operation := admission.Create
if !tc.checkError(err) { if tc.oldObj != nil {
t.Errorf("%v: unexpected err: %v", tc.name, err) operation = admission.Update
} }
user := &user.DefaultInfo{Name: tc.username}
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, user)
err = gcAdmit.Validate(attributes)
if !tc.checkError(err) {
t.Errorf("unexpected err: %v", err)
}
})
} }
} }