...

Source file src/k8s.io/kubernetes/pkg/apis/rbac/helpers_test.go

Documentation: k8s.io/kubernetes/pkg/apis/rbac

     1  /*
     2  Copyright 2017 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 rbac_test
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    26  	"k8s.io/kubernetes/pkg/apis/rbac"
    27  	v1 "k8s.io/kubernetes/pkg/apis/rbac/v1"
    28  
    29  	// install RBAC types
    30  	_ "k8s.io/kubernetes/pkg/apis/rbac/install"
    31  )
    32  
    33  // TestHelpersRoundTrip confirms that the rbac.New* helper functions produce RBAC objects that match objects
    34  // that have gone through conversion and defaulting.  This is required because these helper functions are
    35  // used to create the bootstrap RBAC policy which is used during reconciliation.  If they produced objects
    36  // that did not match, reconciliation would incorrectly add duplicate data to the cluster's RBAC policy.
    37  func TestHelpersRoundTrip(t *testing.T) {
    38  	rb := rbac.NewRoleBinding("role", "ns").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
    39  	rbcr := rbac.NewRoleBindingForClusterRole("role", "ns").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
    40  	crb := rbac.NewClusterBinding("role").Groups("g").SAs("ns", "sa").Users("u").BindingOrDie()
    41  
    42  	role := &rbac.Role{
    43  		Rules: []rbac.PolicyRule{
    44  			rbac.NewRule("verb").Groups("g").Resources("foo").RuleOrDie(),
    45  			rbac.NewRule("verb").URLs("/foo").RuleOrDie(),
    46  		},
    47  	}
    48  	clusterRole := &rbac.ClusterRole{
    49  		Rules: []rbac.PolicyRule{
    50  			rbac.NewRule("verb").Groups("g").Resources("foo").RuleOrDie(),
    51  			rbac.NewRule("verb").URLs("/foo").RuleOrDie(),
    52  		},
    53  	}
    54  
    55  	for _, internalObj := range []runtime.Object{&rb, &rbcr, &crb, role, clusterRole} {
    56  		v1Obj, err := legacyscheme.Scheme.ConvertToVersion(internalObj, v1.SchemeGroupVersion)
    57  		if err != nil {
    58  			t.Errorf("err on %T: %v", internalObj, err)
    59  			continue
    60  		}
    61  		legacyscheme.Scheme.Default(v1Obj)
    62  		roundTrippedObj, err := legacyscheme.Scheme.ConvertToVersion(v1Obj, rbac.SchemeGroupVersion)
    63  		if err != nil {
    64  			t.Errorf("err on %T: %v", internalObj, err)
    65  			continue
    66  		}
    67  		if !reflect.DeepEqual(internalObj, roundTrippedObj) {
    68  			t.Errorf("err on %T: got difference:\n%s", internalObj, cmp.Diff(internalObj, roundTrippedObj))
    69  			continue
    70  		}
    71  	}
    72  }
    73  
    74  func TestResourceMatches(t *testing.T) {
    75  	tests := []struct {
    76  		name                      string
    77  		ruleResources             []string
    78  		combinedRequestedResource string
    79  		requestedSubresource      string
    80  		expected                  bool
    81  	}{
    82  		{
    83  			name:                      "all matches 01",
    84  			ruleResources:             []string{"*"},
    85  			combinedRequestedResource: "foo",
    86  			expected:                  true,
    87  		},
    88  		{
    89  			name:                      "checks all rules",
    90  			ruleResources:             []string{"doesn't match", "*"},
    91  			combinedRequestedResource: "foo",
    92  			expected:                  true,
    93  		},
    94  		{
    95  			name:                      "matches exact rule",
    96  			ruleResources:             []string{"foo/bar"},
    97  			combinedRequestedResource: "foo/bar",
    98  			requestedSubresource:      "bar",
    99  			expected:                  true,
   100  		},
   101  		{
   102  			name:                      "matches exact rule 02",
   103  			ruleResources:             []string{"foo/bar"},
   104  			combinedRequestedResource: "foo",
   105  			expected:                  false,
   106  		},
   107  		{
   108  			name:                      "matches subresource",
   109  			ruleResources:             []string{"*/scale"},
   110  			combinedRequestedResource: "foo/scale",
   111  			requestedSubresource:      "scale",
   112  			expected:                  true,
   113  		},
   114  		{
   115  			name:                      "doesn't match partial subresource hit",
   116  			ruleResources:             []string{"foo/bar", "*/other"},
   117  			combinedRequestedResource: "foo/other/segment",
   118  			requestedSubresource:      "other/segment",
   119  			expected:                  false,
   120  		},
   121  		{
   122  			name:                      "matches subresource with multiple slashes",
   123  			ruleResources:             []string{"*/other/segment"},
   124  			combinedRequestedResource: "foo/other/segment",
   125  			requestedSubresource:      "other/segment",
   126  			expected:                  true,
   127  		},
   128  		{
   129  			name:                      "doesn't fail on empty",
   130  			ruleResources:             []string{""},
   131  			combinedRequestedResource: "foo/other/segment",
   132  			requestedSubresource:      "other/segment",
   133  			expected:                  false,
   134  		},
   135  		{
   136  			name:                      "doesn't fail on slash",
   137  			ruleResources:             []string{"/"},
   138  			combinedRequestedResource: "foo/other/segment",
   139  			requestedSubresource:      "other/segment",
   140  			expected:                  false,
   141  		},
   142  		{
   143  			name:                      "doesn't fail on missing subresource",
   144  			ruleResources:             []string{"*/"},
   145  			combinedRequestedResource: "foo/other/segment",
   146  			requestedSubresource:      "other/segment",
   147  			expected:                  false,
   148  		},
   149  		{
   150  			name:                      "doesn't match on not star",
   151  			ruleResources:             []string{"*something/other/segment"},
   152  			combinedRequestedResource: "foo/other/segment",
   153  			requestedSubresource:      "other/segment",
   154  			expected:                  false,
   155  		},
   156  		{
   157  			name:                      "doesn't match on something else",
   158  			ruleResources:             []string{"something/other/segment"},
   159  			combinedRequestedResource: "foo/other/segment",
   160  			requestedSubresource:      "other/segment",
   161  			expected:                  false,
   162  		},
   163  	}
   164  
   165  	for _, tc := range tests {
   166  		t.Run(tc.name, func(t *testing.T) {
   167  			rule := &rbac.PolicyRule{
   168  				Resources: tc.ruleResources,
   169  			}
   170  			actual := rbac.ResourceMatches(rule, tc.combinedRequestedResource, tc.requestedSubresource)
   171  			if tc.expected != actual {
   172  				t.Errorf("expected %v, got %v", tc.expected, actual)
   173  			}
   174  
   175  		})
   176  	}
   177  }
   178  
   179  func TestPolicyRuleBuilder(t *testing.T) {
   180  	tests := []struct {
   181  		testName   string
   182  		verbs      []string
   183  		groups     []string
   184  		resources  []string
   185  		names      []string
   186  		urls       []string
   187  		expected   bool
   188  		policyRule rbac.PolicyRule
   189  	}{
   190  		{
   191  			testName:   "all empty",
   192  			verbs:      nil,
   193  			groups:     nil,
   194  			resources:  nil,
   195  			names:      nil,
   196  			urls:       nil,
   197  			expected:   false,
   198  			policyRule: rbac.PolicyRule{},
   199  		},
   200  		{
   201  			testName:  "normal resource case",
   202  			verbs:     []string{"get"},
   203  			groups:    []string{""},
   204  			resources: []string{"pod"},
   205  			names:     []string{"gakki"},
   206  			urls:      nil,
   207  			expected:  true,
   208  			policyRule: rbac.PolicyRule{
   209  				Verbs:           []string{"get"},
   210  				APIGroups:       []string{""},
   211  				Resources:       []string{"pod"},
   212  				ResourceNames:   []string{"gakki"},
   213  				NonResourceURLs: []string{},
   214  			},
   215  		},
   216  		{
   217  			testName:  "normal noResourceURLs case",
   218  			verbs:     []string{"get"},
   219  			groups:    nil,
   220  			resources: nil,
   221  			names:     nil,
   222  			urls:      []string{"/api/registry/healthz"},
   223  			expected:  true,
   224  			policyRule: rbac.PolicyRule{
   225  				Verbs:           []string{"get"},
   226  				APIGroups:       []string{},
   227  				Resources:       []string{},
   228  				ResourceNames:   []string{},
   229  				NonResourceURLs: []string{"/api/registry/healthz"},
   230  			},
   231  		},
   232  		{
   233  			testName:   "nonResourceURLs with no-empty groups",
   234  			verbs:      []string{"get"},
   235  			groups:     []string{""},
   236  			resources:  nil,
   237  			names:      nil,
   238  			urls:       []string{"/api/registry/healthz"},
   239  			expected:   false,
   240  			policyRule: rbac.PolicyRule{},
   241  		},
   242  		{
   243  			testName:   "nonResourceURLs with no-empty resources",
   244  			verbs:      []string{"get"},
   245  			groups:     nil,
   246  			resources:  []string{"deployments", "secrets"},
   247  			names:      nil,
   248  			urls:       []string{"/api/registry/healthz"},
   249  			expected:   false,
   250  			policyRule: rbac.PolicyRule{},
   251  		},
   252  		{
   253  			testName:   "nonResourceURLs with no-empty resourceNames",
   254  			verbs:      []string{"get"},
   255  			groups:     nil,
   256  			resources:  nil,
   257  			names:      []string{"gakki"},
   258  			urls:       []string{"/api/registry/healthz"},
   259  			expected:   false,
   260  			policyRule: rbac.PolicyRule{},
   261  		},
   262  		{
   263  			testName:   "resource without apiGroups",
   264  			verbs:      []string{"get"},
   265  			groups:     nil,
   266  			resources:  []string{"pod"},
   267  			names:      []string{""},
   268  			urls:       nil,
   269  			expected:   false,
   270  			policyRule: rbac.PolicyRule{},
   271  		},
   272  		{
   273  			testName:   "resourceNames with illegal verb",
   274  			verbs:      []string{"list", "watch", "create", "deletecollection"},
   275  			groups:     []string{""},
   276  			resources:  []string{"pod"},
   277  			names:      []string{"gakki"},
   278  			urls:       nil,
   279  			expected:   false,
   280  			policyRule: rbac.PolicyRule{},
   281  		},
   282  		{
   283  			testName:   "no nonResourceURLs nor resources",
   284  			verbs:      []string{"get"},
   285  			groups:     []string{"rbac.authorization.k8s.io"},
   286  			resources:  nil,
   287  			names:      []string{"gakki"},
   288  			urls:       nil,
   289  			expected:   false,
   290  			policyRule: rbac.PolicyRule{},
   291  		},
   292  	}
   293  	for _, tc := range tests {
   294  		actual, err := rbac.NewRule(tc.verbs...).Groups(tc.groups...).Resources(tc.resources...).Names(tc.names...).URLs(tc.urls...).Rule()
   295  		if err != nil {
   296  			if tc.expected {
   297  				t.Error(err)
   298  			} else {
   299  				continue
   300  			}
   301  		}
   302  		if !reflect.DeepEqual(actual, tc.policyRule) {
   303  			t.Errorf("Expected %s got %s.", tc.policyRule, actual)
   304  		}
   305  	}
   306  }
   307  

View as plain text