1
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
30 _ "k8s.io/kubernetes/pkg/apis/rbac/install"
31 )
32
33
34
35
36
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