...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/certificates/approval/admission_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/certificates/approval

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package approval
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"testing"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	"k8s.io/apiserver/pkg/admission"
    28  	"k8s.io/apiserver/pkg/authentication/user"
    29  	"k8s.io/apiserver/pkg/authorization/authorizer"
    30  
    31  	certificatesapi "k8s.io/kubernetes/pkg/apis/certificates"
    32  )
    33  
    34  func TestPlugin_Validate(t *testing.T) {
    35  	tests := map[string]struct {
    36  		attributes  admission.Attributes
    37  		allowedName string
    38  		allowed     bool
    39  		authzErr    error
    40  	}{
    41  		"wrong type": {
    42  			attributes: &testAttributes{
    43  				resource:    certificatesapi.Resource("certificatesigningrequests"),
    44  				subresource: "approval",
    45  				oldObj:      &certificatesapi.CertificateSigningRequestList{},
    46  				operation:   admission.Update,
    47  			},
    48  			allowed: false,
    49  		},
    50  		"reject requests if looking up permissions fails": {
    51  			attributes: &testAttributes{
    52  				resource:    certificatesapi.Resource("certificatesigningrequests"),
    53  				subresource: "approval",
    54  				oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
    55  					SignerName: "abc.com/xyz",
    56  				}},
    57  				operation: admission.Update,
    58  			},
    59  			authzErr: errors.New("forced error"),
    60  			allowed:  false,
    61  		},
    62  		"should allow request if user is authorized for specific signerName": {
    63  			allowedName: "abc.com/xyz",
    64  			attributes: &testAttributes{
    65  				resource:    certificatesapi.Resource("certificatesigningrequests"),
    66  				subresource: "approval",
    67  				oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
    68  					SignerName: "abc.com/xyz",
    69  				}},
    70  				operation: admission.Update,
    71  			},
    72  			allowed: true,
    73  		},
    74  		"should allow request if user is authorized with wildcard": {
    75  			allowedName: "abc.com/*",
    76  			attributes: &testAttributes{
    77  				resource:    certificatesapi.Resource("certificatesigningrequests"),
    78  				subresource: "approval",
    79  				oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
    80  					SignerName: "abc.com/xyz",
    81  				}},
    82  				operation: admission.Update,
    83  			},
    84  			allowed: true,
    85  		},
    86  		"should deny request if user does not have permission for this signerName": {
    87  			allowedName: "notabc.com/xyz",
    88  			attributes: &testAttributes{
    89  				resource:    certificatesapi.Resource("certificatesigningrequests"),
    90  				subresource: "approval",
    91  				oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
    92  					SignerName: "abc.com/xyz",
    93  				}},
    94  				operation: admission.Update,
    95  			},
    96  			allowed: false,
    97  		},
    98  		"should deny request if user attempts to update signerName to a new value they *do* have permission to approve for": {
    99  			allowedName: "allowed.com/xyz",
   100  			attributes: &testAttributes{
   101  				resource:    certificatesapi.Resource("certificatesigningrequests"),
   102  				subresource: "approval",
   103  				oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
   104  					SignerName: "notallowed.com/xyz",
   105  				}},
   106  				obj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{
   107  					SignerName: "allowed.com/xyz",
   108  				}},
   109  				operation: admission.Update,
   110  			},
   111  			allowed: false,
   112  		},
   113  	}
   114  
   115  	for n, test := range tests {
   116  		t.Run(n, func(t *testing.T) {
   117  			p := Plugin{
   118  				authz: fakeAuthorizer{
   119  					t:           t,
   120  					verb:        "approve",
   121  					allowedName: test.allowedName,
   122  					decision:    authorizer.DecisionAllow,
   123  					err:         test.authzErr,
   124  				},
   125  			}
   126  			err := p.Validate(context.Background(), test.attributes, nil)
   127  			if err == nil && !test.allowed {
   128  				t.Errorf("Expected authorization policy to reject CSR but it was allowed")
   129  			}
   130  			if err != nil && test.allowed {
   131  				t.Errorf("Expected authorization policy to accept CSR but it was rejected: %v", err)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  type fakeAuthorizer struct {
   138  	t           *testing.T
   139  	verb        string
   140  	allowedName string
   141  	decision    authorizer.Decision
   142  	err         error
   143  }
   144  
   145  func (f fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
   146  	if f.err != nil {
   147  		return f.decision, "forced error", f.err
   148  	}
   149  	if a.GetVerb() != f.verb {
   150  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised verb '%s'", a.GetVerb()), nil
   151  	}
   152  	if a.GetAPIGroup() != "certificates.k8s.io" {
   153  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised groupName '%s'", a.GetAPIGroup()), nil
   154  	}
   155  	if a.GetAPIVersion() != "*" {
   156  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised apiVersion '%s'", a.GetAPIVersion()), nil
   157  	}
   158  	if a.GetResource() != "signers" {
   159  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised resource '%s'", a.GetResource()), nil
   160  	}
   161  	if a.GetName() != f.allowedName {
   162  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised resource name '%s'", a.GetName()), nil
   163  	}
   164  	if !a.IsResourceRequest() {
   165  		return authorizer.DecisionDeny, fmt.Sprintf("unrecognised IsResourceRequest '%t'", a.IsResourceRequest()), nil
   166  	}
   167  	return f.decision, "", nil
   168  }
   169  
   170  type testAttributes struct {
   171  	resource    schema.GroupResource
   172  	subresource string
   173  	operation   admission.Operation
   174  	obj, oldObj runtime.Object
   175  	name        string
   176  
   177  	admission.Attributes // nil panic if any other methods called
   178  }
   179  
   180  func (t *testAttributes) GetResource() schema.GroupVersionResource {
   181  	return t.resource.WithVersion("ignored")
   182  }
   183  
   184  func (t *testAttributes) GetSubresource() string {
   185  	return t.subresource
   186  }
   187  
   188  func (t *testAttributes) GetObject() runtime.Object {
   189  	return t.obj
   190  }
   191  
   192  func (t *testAttributes) GetOldObject() runtime.Object {
   193  	return t.oldObj
   194  }
   195  
   196  func (t *testAttributes) GetName() string {
   197  	return t.name
   198  }
   199  
   200  func (t *testAttributes) GetOperation() admission.Operation {
   201  	return t.operation
   202  }
   203  
   204  func (t *testAttributes) GetUserInfo() user.Info {
   205  	return &user.DefaultInfo{Name: "ignored"}
   206  }
   207  

View as plain text