1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package validation
18
19import (
20	"testing"
21
22	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23	"k8s.io/apimachinery/pkg/util/validation/field"
24	"k8s.io/kubernetes/pkg/apis/rbac"
25)
26
27func TestValidateClusterRoleBinding(t *testing.T) {
28	errs := ValidateClusterRoleBinding(
29		&rbac.ClusterRoleBinding{
30			ObjectMeta: metav1.ObjectMeta{Name: "master"},
31			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
32			Subjects: []rbac.Subject{
33				{Name: "validsaname", APIGroup: "", Namespace: "foo", Kind: rbac.ServiceAccountKind},
34				{Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind},
35				{Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind},
36			},
37		},
38	)
39	if len(errs) != 0 {
40		t.Errorf("expected success: %v", errs)
41	}
42
43	errorCases := map[string]struct {
44		A rbac.ClusterRoleBinding
45		T field.ErrorType
46		F string
47	}{
48		"bad group": {
49			A: rbac.ClusterRoleBinding{
50				ObjectMeta: metav1.ObjectMeta{Name: "default"},
51				RoleRef:    rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"},
52			},
53			T: field.ErrorTypeNotSupported,
54			F: "roleRef.apiGroup",
55		},
56		"bad kind": {
57			A: rbac.ClusterRoleBinding{
58				ObjectMeta: metav1.ObjectMeta{Name: "default"},
59				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"},
60			},
61			T: field.ErrorTypeNotSupported,
62			F: "roleRef.kind",
63		},
64		"reference role": {
65			A: rbac.ClusterRoleBinding{
66				ObjectMeta: metav1.ObjectMeta{Name: "default"},
67				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
68			},
69			T: field.ErrorTypeNotSupported,
70			F: "roleRef.kind",
71		},
72		"zero-length name": {
73			A: rbac.ClusterRoleBinding{
74				ObjectMeta: metav1.ObjectMeta{},
75				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
76			},
77			T: field.ErrorTypeRequired,
78			F: "metadata.name",
79		},
80		"bad role": {
81			A: rbac.ClusterRoleBinding{
82				ObjectMeta: metav1.ObjectMeta{Name: "default"},
83				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole"},
84			},
85			T: field.ErrorTypeRequired,
86			F: "roleRef.name",
87		},
88		"bad subject kind": {
89			A: rbac.ClusterRoleBinding{
90				ObjectMeta: metav1.ObjectMeta{Name: "master"},
91				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
92				Subjects:   []rbac.Subject{{Name: "subject"}},
93			},
94			T: field.ErrorTypeNotSupported,
95			F: "subjects[0].kind",
96		},
97		"bad subject name": {
98			A: rbac.ClusterRoleBinding{
99				ObjectMeta: metav1.ObjectMeta{Name: "master"},
100				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
101				Subjects:   []rbac.Subject{{Namespace: "foo", Name: "subject:bad", Kind: rbac.ServiceAccountKind}},
102			},
103			T: field.ErrorTypeInvalid,
104			F: "subjects[0].name",
105		},
106		"missing SA namespace": {
107			A: rbac.ClusterRoleBinding{
108				ObjectMeta: metav1.ObjectMeta{Name: "master"},
109				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
110				Subjects:   []rbac.Subject{{Name: "good", Kind: rbac.ServiceAccountKind}},
111			},
112			T: field.ErrorTypeRequired,
113			F: "subjects[0].namespace",
114		},
115		"missing subject name": {
116			A: rbac.ClusterRoleBinding{
117				ObjectMeta: metav1.ObjectMeta{Name: "master"},
118				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
119				Subjects:   []rbac.Subject{{Namespace: "foo", Kind: rbac.ServiceAccountKind}},
120			},
121			T: field.ErrorTypeRequired,
122			F: "subjects[0].name",
123		},
124	}
125	for k, v := range errorCases {
126		errs := ValidateClusterRoleBinding(&v.A)
127		if len(errs) == 0 {
128			t.Errorf("expected failure %s for %v", k, v.A)
129			continue
130		}
131		for i := range errs {
132			if errs[i].Type != v.T {
133				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
134			}
135			if errs[i].Field != v.F {
136				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
137			}
138		}
139	}
140}
141
142func TestValidateRoleBinding(t *testing.T) {
143	errs := ValidateRoleBinding(
144		&rbac.RoleBinding{
145			ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"},
146			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
147			Subjects: []rbac.Subject{
148				{Name: "validsaname", APIGroup: "", Kind: rbac.ServiceAccountKind},
149				{Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind},
150				{Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind},
151			},
152		},
153	)
154	if len(errs) != 0 {
155		t.Errorf("expected success: %v", errs)
156	}
157
158	errorCases := map[string]struct {
159		A rbac.RoleBinding
160		T field.ErrorType
161		F string
162	}{
163		"bad group": {
164			A: rbac.RoleBinding{
165				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"},
166				RoleRef:    rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"},
167			},
168			T: field.ErrorTypeNotSupported,
169			F: "roleRef.apiGroup",
170		},
171		"bad kind": {
172			A: rbac.RoleBinding{
173				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"},
174				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"},
175			},
176			T: field.ErrorTypeNotSupported,
177			F: "roleRef.kind",
178		},
179		"zero-length namespace": {
180			A: rbac.RoleBinding{
181				ObjectMeta: metav1.ObjectMeta{Name: "default"},
182				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
183			},
184			T: field.ErrorTypeRequired,
185			F: "metadata.namespace",
186		},
187		"zero-length name": {
188			A: rbac.RoleBinding{
189				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault},
190				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
191			},
192			T: field.ErrorTypeRequired,
193			F: "metadata.name",
194		},
195		"bad role": {
196			A: rbac.RoleBinding{
197				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "default"},
198				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role"},
199			},
200			T: field.ErrorTypeRequired,
201			F: "roleRef.name",
202		},
203		"bad subject kind": {
204			A: rbac.RoleBinding{
205				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"},
206				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
207				Subjects:   []rbac.Subject{{Name: "subject"}},
208			},
209			T: field.ErrorTypeNotSupported,
210			F: "subjects[0].kind",
211		},
212		"bad subject name": {
213			A: rbac.RoleBinding{
214				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"},
215				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
216				Subjects:   []rbac.Subject{{Name: "subject:bad", Kind: rbac.ServiceAccountKind}},
217			},
218			T: field.ErrorTypeInvalid,
219			F: "subjects[0].name",
220		},
221		"missing subject name": {
222			A: rbac.RoleBinding{
223				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"},
224				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
225				Subjects:   []rbac.Subject{{Kind: rbac.ServiceAccountKind}},
226			},
227			T: field.ErrorTypeRequired,
228			F: "subjects[0].name",
229		},
230	}
231	for k, v := range errorCases {
232		errs := ValidateRoleBinding(&v.A)
233		if len(errs) == 0 {
234			t.Errorf("expected failure %s for %v", k, v.A)
235			continue
236		}
237		for i := range errs {
238			if errs[i].Type != v.T {
239				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
240			}
241			if errs[i].Field != v.F {
242				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
243			}
244		}
245	}
246}
247
248func TestValidateRoleBindingUpdate(t *testing.T) {
249	old := &rbac.RoleBinding{
250		ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"},
251		RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
252	}
253
254	errs := ValidateRoleBindingUpdate(
255		&rbac.RoleBinding{
256			ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"},
257			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
258		},
259		old,
260	)
261	if len(errs) != 0 {
262		t.Errorf("expected success: %v", errs)
263	}
264
265	errorCases := map[string]struct {
266		A rbac.RoleBinding
267		T field.ErrorType
268		F string
269	}{
270		"changedRef": {
271			A: rbac.RoleBinding{
272				ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master", ResourceVersion: "1"},
273				RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "changed"},
274			},
275			T: field.ErrorTypeInvalid,
276			F: "roleRef",
277		},
278	}
279	for k, v := range errorCases {
280		errs := ValidateRoleBindingUpdate(&v.A, old)
281		if len(errs) == 0 {
282			t.Errorf("expected failure %s for %v", k, v.A)
283			continue
284		}
285		for i := range errs {
286			if errs[i].Type != v.T {
287				t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
288			}
289			if errs[i].Field != v.F {
290				t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
291			}
292		}
293	}
294}
295
296type ValidateRoleTest struct {
297	role    rbac.Role
298	wantErr bool
299	errType field.ErrorType
300	field   string
301}
302
303func (v ValidateRoleTest) test(t *testing.T) {
304	errs := ValidateRole(&v.role)
305	if len(errs) == 0 {
306		if v.wantErr {
307			t.Fatal("expected validation error")
308		}
309		return
310	}
311	if !v.wantErr {
312		t.Errorf("didn't expect error, got %v", errs)
313		return
314	}
315	for i := range errs {
316		if errs[i].Type != v.errType {
317			t.Errorf("expected errors to have type %s: %v", v.errType, errs[i])
318		}
319		if errs[i].Field != v.field {
320			t.Errorf("expected errors to have field %s: %v", v.field, errs[i])
321		}
322	}
323}
324
325type ValidateClusterRoleTest struct {
326	role    rbac.ClusterRole
327	wantErr bool
328	errType field.ErrorType
329	field   string
330}
331
332func (v ValidateClusterRoleTest) test(t *testing.T) {
333	errs := ValidateClusterRole(&v.role)
334	if len(errs) == 0 {
335		if v.wantErr {
336			t.Fatal("expected validation error")
337		}
338		return
339	}
340	if !v.wantErr {
341		t.Errorf("didn't expect error, got %v", errs)
342		return
343	}
344	for i := range errs {
345		if errs[i].Type != v.errType {
346			t.Errorf("expected errors to have type %s: %v", v.errType, errs[i])
347		}
348		if errs[i].Field != v.field {
349			t.Errorf("expected errors to have field %s: %v", v.field, errs[i])
350		}
351	}
352}
353
354func TestValidateRoleZeroLengthNamespace(t *testing.T) {
355	ValidateRoleTest{
356		role: rbac.Role{
357			ObjectMeta: metav1.ObjectMeta{Name: "default"},
358		},
359		wantErr: true,
360		errType: field.ErrorTypeRequired,
361		field:   "metadata.namespace",
362	}.test(t)
363}
364
365func TestValidateRoleZeroLengthName(t *testing.T) {
366	ValidateRoleTest{
367		role: rbac.Role{
368			ObjectMeta: metav1.ObjectMeta{Namespace: "default"},
369		},
370		wantErr: true,
371		errType: field.ErrorTypeRequired,
372		field:   "metadata.name",
373	}.test(t)
374}
375
376func TestValidateRoleValidRole(t *testing.T) {
377	ValidateRoleTest{
378		role: rbac.Role{
379			ObjectMeta: metav1.ObjectMeta{
380				Namespace: "default",
381				Name:      "default",
382			},
383		},
384		wantErr: false,
385	}.test(t)
386}
387
388func TestValidateRoleValidRoleNoNamespace(t *testing.T) {
389	ValidateClusterRoleTest{
390		role: rbac.ClusterRole{
391			ObjectMeta: metav1.ObjectMeta{
392				Name: "default",
393			},
394		},
395		wantErr: false,
396	}.test(t)
397}
398
399func TestValidateRoleNonResourceURL(t *testing.T) {
400	ValidateClusterRoleTest{
401		role: rbac.ClusterRole{
402			ObjectMeta: metav1.ObjectMeta{
403				Name: "default",
404			},
405			Rules: []rbac.PolicyRule{
406				{
407					Verbs:           []string{"get"},
408					NonResourceURLs: []string{"/*"},
409				},
410			},
411		},
412		wantErr: false,
413	}.test(t)
414}
415
416func TestValidateRoleNamespacedNonResourceURL(t *testing.T) {
417	ValidateRoleTest{
418		role: rbac.Role{
419			ObjectMeta: metav1.ObjectMeta{
420				Namespace: "default",
421				Name:      "default",
422			},
423			Rules: []rbac.PolicyRule{
424				{
425					// non-resource URLs are invalid for namespaced rules
426					Verbs:           []string{"get"},
427					NonResourceURLs: []string{"/*"},
428				},
429			},
430		},
431		wantErr: true,
432		errType: field.ErrorTypeInvalid,
433		field:   "rules[0].nonResourceURLs",
434	}.test(t)
435}
436
437func TestValidateRoleNonResourceURLNoVerbs(t *testing.T) {
438	ValidateClusterRoleTest{
439		role: rbac.ClusterRole{
440			ObjectMeta: metav1.ObjectMeta{
441				Name: "default",
442			},
443			Rules: []rbac.PolicyRule{
444				{
445					Verbs:           []string{},
446					NonResourceURLs: []string{"/*"},
447				},
448			},
449		},
450		wantErr: true,
451		errType: field.ErrorTypeRequired,
452		field:   "rules[0].verbs",
453	}.test(t)
454}
455
456func TestValidateRoleMixedNonResourceAndResource(t *testing.T) {
457	ValidateRoleTest{
458		role: rbac.Role{
459			ObjectMeta: metav1.ObjectMeta{
460				Name:      "default",
461				Namespace: "default",
462			},
463			Rules: []rbac.PolicyRule{
464				{
465					Verbs:           []string{"get"},
466					NonResourceURLs: []string{"/*"},
467					APIGroups:       []string{"v1"},
468					Resources:       []string{"pods"},
469				},
470			},
471		},
472		wantErr: true,
473		errType: field.ErrorTypeInvalid,
474		field:   "rules[0].nonResourceURLs",
475	}.test(t)
476}
477
478func TestValidateRoleValidResource(t *testing.T) {
479	ValidateRoleTest{
480		role: rbac.Role{
481			ObjectMeta: metav1.ObjectMeta{
482				Name:      "default",
483				Namespace: "default",
484			},
485			Rules: []rbac.PolicyRule{
486				{
487					Verbs:     []string{"get"},
488					APIGroups: []string{"v1"},
489					Resources: []string{"pods"},
490				},
491			},
492		},
493		wantErr: false,
494	}.test(t)
495}
496
497func TestValidateRoleNoAPIGroup(t *testing.T) {
498	ValidateRoleTest{
499		role: rbac.Role{
500			ObjectMeta: metav1.ObjectMeta{
501				Name:      "default",
502				Namespace: "default",
503			},
504			Rules: []rbac.PolicyRule{
505				{
506					Verbs:     []string{"get"},
507					Resources: []string{"pods"},
508				},
509			},
510		},
511		wantErr: true,
512		errType: field.ErrorTypeRequired,
513		field:   "rules[0].apiGroups",
514	}.test(t)
515}
516
517func TestValidateRoleNoResources(t *testing.T) {
518	ValidateRoleTest{
519		role: rbac.Role{
520			ObjectMeta: metav1.ObjectMeta{
521				Name:      "default",
522				Namespace: "default",
523			},
524			Rules: []rbac.PolicyRule{
525				{
526					Verbs:     []string{"get"},
527					APIGroups: []string{"v1"},
528				},
529			},
530		},
531		wantErr: true,
532		errType: field.ErrorTypeRequired,
533		field:   "rules[0].resources",
534	}.test(t)
535}
536