Browse code

change internal representation of rolebindings to use subjects

deads2k authored on 2015/08/21 00:16:43
Showing 31 changed files
... ...
@@ -14618,6 +14618,7 @@
14618 14618
     "required": [
14619 14619
      "userNames",
14620 14620
      "groupNames",
14621
+     "subjects",
14621 14622
      "roleRef"
14622 14623
     ],
14623 14624
     "properties": {
... ...
@@ -14646,6 +14647,13 @@
14646 14646
       },
14647 14647
       "description": "all the groups directly bound to the role"
14648 14648
      },
14649
+     "subjects": {
14650
+      "type": "array",
14651
+      "items": {
14652
+       "$ref": "v1.ObjectReference"
14653
+      },
14654
+      "description": "references to subjects bound to the role.  Only User, Group, SystemUser, SystemGroup, and ServiceAccount are allowed."
14655
+     },
14649 14656
      "roleRef": {
14650 14657
       "$ref": "v1.ObjectReference",
14651 14658
       "description": "reference to the policy role"
... ...
@@ -16900,6 +16908,7 @@
16900 16900
     "required": [
16901 16901
      "userNames",
16902 16902
      "groupNames",
16903
+     "subjects",
16903 16904
      "roleRef"
16904 16905
     ],
16905 16906
     "properties": {
... ...
@@ -16928,6 +16937,13 @@
16928 16928
       },
16929 16929
       "description": "all the groups directly bound to the role"
16930 16930
      },
16931
+     "subjects": {
16932
+      "type": "array",
16933
+      "items": {
16934
+       "$ref": "v1.ObjectReference"
16935
+      },
16936
+      "description": "references to subjects bound to the role.  Only User, Group, SystemUser, SystemGroup, and ServiceAccount are allowed."
16937
+     },
16931 16938
      "roleRef": {
16932 16939
       "$ref": "v1.ObjectReference",
16933 16940
       "description": "a reference to a role"
... ...
@@ -181,29 +181,17 @@ func deepCopy_api_ClusterRoleBinding(in api.ClusterRoleBinding, out *api.Cluster
181 181
 	} else {
182 182
 		out.ObjectMeta = newVal.(pkgapi.ObjectMeta)
183 183
 	}
184
-	if in.Users != nil {
185
-		out.Users = make(util.StringSet)
186
-		for key, val := range in.Users {
187
-			if newVal, err := c.DeepCopy(val); err != nil {
188
-				return err
189
-			} else {
190
-				out.Users[key] = newVal.(util.Empty)
191
-			}
192
-		}
193
-	} else {
194
-		out.Users = nil
195
-	}
196
-	if in.Groups != nil {
197
-		out.Groups = make(util.StringSet)
198
-		for key, val := range in.Groups {
199
-			if newVal, err := c.DeepCopy(val); err != nil {
184
+	if in.Subjects != nil {
185
+		out.Subjects = make([]pkgapi.ObjectReference, len(in.Subjects))
186
+		for i := range in.Subjects {
187
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
200 188
 				return err
201 189
 			} else {
202
-				out.Groups[key] = newVal.(util.Empty)
190
+				out.Subjects[i] = newVal.(pkgapi.ObjectReference)
203 191
 			}
204 192
 		}
205 193
 	} else {
206
-		out.Groups = nil
194
+		out.Subjects = nil
207 195
 	}
208 196
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
209 197
 		return err
... ...
@@ -560,29 +548,17 @@ func deepCopy_api_RoleBinding(in api.RoleBinding, out *api.RoleBinding, c *conve
560 560
 	} else {
561 561
 		out.ObjectMeta = newVal.(pkgapi.ObjectMeta)
562 562
 	}
563
-	if in.Users != nil {
564
-		out.Users = make(util.StringSet)
565
-		for key, val := range in.Users {
566
-			if newVal, err := c.DeepCopy(val); err != nil {
567
-				return err
568
-			} else {
569
-				out.Users[key] = newVal.(util.Empty)
570
-			}
571
-		}
572
-	} else {
573
-		out.Users = nil
574
-	}
575
-	if in.Groups != nil {
576
-		out.Groups = make(util.StringSet)
577
-		for key, val := range in.Groups {
578
-			if newVal, err := c.DeepCopy(val); err != nil {
563
+	if in.Subjects != nil {
564
+		out.Subjects = make([]pkgapi.ObjectReference, len(in.Subjects))
565
+		for i := range in.Subjects {
566
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
579 567
 				return err
580 568
 			} else {
581
-				out.Groups[key] = newVal.(util.Empty)
569
+				out.Subjects[i] = newVal.(pkgapi.ObjectReference)
582 570
 			}
583 571
 		}
584 572
 	} else {
585
-		out.Groups = nil
573
+		out.Subjects = nil
586 574
 	}
587 575
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
588 576
 		return err
... ...
@@ -1,6 +1,7 @@
1 1
 package api_test
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"math/rand"
5 6
 	"reflect"
6 7
 	"strings"
... ...
@@ -11,8 +12,10 @@ import (
11 11
 	"k8s.io/kubernetes/pkg/api"
12 12
 	"k8s.io/kubernetes/pkg/api/meta"
13 13
 	apitesting "k8s.io/kubernetes/pkg/api/testing"
14
+	"k8s.io/kubernetes/pkg/api/validation"
14 15
 	"k8s.io/kubernetes/pkg/conversion"
15 16
 	"k8s.io/kubernetes/pkg/runtime"
17
+	"k8s.io/kubernetes/pkg/types"
16 18
 	"k8s.io/kubernetes/pkg/util"
17 19
 
18 20
 	osapi "github.com/openshift/origin/pkg/api"
... ...
@@ -24,6 +27,7 @@ import (
24 24
 	deploy "github.com/openshift/origin/pkg/deploy/api"
25 25
 	image "github.com/openshift/origin/pkg/image/api"
26 26
 	template "github.com/openshift/origin/pkg/template/api"
27
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
27 28
 )
28 29
 
29 30
 func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object {
... ...
@@ -42,6 +46,82 @@ func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, se
42 42
 		func(j *authorizationapi.ClusterPolicyBinding, c fuzz.Continue) {
43 43
 			j.RoleBindings = make(map[string]*authorizationapi.ClusterRoleBinding)
44 44
 		},
45
+		func(j *authorizationapi.RoleBinding, c fuzz.Continue) {
46
+			c.FuzzNoCustom(j)
47
+			for i := range j.Subjects {
48
+				kinds := []string{authorizationapi.UserKind, authorizationapi.SystemUserKind, authorizationapi.GroupKind, authorizationapi.SystemGroupKind, authorizationapi.ServiceAccountKind}
49
+				j.Subjects[i].Kind = kinds[c.Intn(len(kinds))]
50
+				switch j.Subjects[i].Kind {
51
+				case authorizationapi.UserKind:
52
+					j.Subjects[i].Namespace = ""
53
+					if valid, _ := uservalidation.ValidateUserName(j.Subjects[i].Name, false); !valid {
54
+						j.Subjects[i].Name = fmt.Sprintf("validusername%d", i)
55
+					}
56
+
57
+				case authorizationapi.GroupKind:
58
+					j.Subjects[i].Namespace = ""
59
+					if valid, _ := uservalidation.ValidateGroupName(j.Subjects[i].Name, false); !valid {
60
+						j.Subjects[i].Name = fmt.Sprintf("validgroupname%d", i)
61
+					}
62
+
63
+				case authorizationapi.ServiceAccountKind:
64
+					if valid, _ := validation.ValidateNamespaceName(j.Subjects[i].Namespace, false); !valid {
65
+						j.Subjects[i].Namespace = fmt.Sprintf("sanamespacehere%d", i)
66
+					}
67
+					if valid, _ := validation.ValidateServiceAccountName(j.Subjects[i].Name, false); !valid {
68
+						j.Subjects[i].Name = fmt.Sprintf("sanamehere%d", i)
69
+					}
70
+
71
+				case authorizationapi.SystemUserKind, authorizationapi.SystemGroupKind:
72
+					j.Subjects[i].Namespace = ""
73
+					j.Subjects[i].Name = ":" + j.Subjects[i].Name
74
+
75
+				}
76
+
77
+				j.Subjects[i].UID = types.UID("")
78
+				j.Subjects[i].APIVersion = ""
79
+				j.Subjects[i].ResourceVersion = ""
80
+				j.Subjects[i].FieldPath = ""
81
+			}
82
+		},
83
+		func(j *authorizationapi.ClusterRoleBinding, c fuzz.Continue) {
84
+			c.FuzzNoCustom(j)
85
+			for i := range j.Subjects {
86
+				kinds := []string{authorizationapi.UserKind, authorizationapi.SystemUserKind, authorizationapi.GroupKind, authorizationapi.SystemGroupKind, authorizationapi.ServiceAccountKind}
87
+				j.Subjects[i].Kind = kinds[c.Intn(len(kinds))]
88
+				switch j.Subjects[i].Kind {
89
+				case authorizationapi.UserKind:
90
+					j.Subjects[i].Namespace = ""
91
+					if valid, _ := uservalidation.ValidateUserName(j.Subjects[i].Name, false); !valid {
92
+						j.Subjects[i].Name = fmt.Sprintf("validusername%d", i)
93
+					}
94
+
95
+				case authorizationapi.GroupKind:
96
+					j.Subjects[i].Namespace = ""
97
+					if valid, _ := uservalidation.ValidateGroupName(j.Subjects[i].Name, false); !valid {
98
+						j.Subjects[i].Name = fmt.Sprintf("validgroupname%d", i)
99
+					}
100
+
101
+				case authorizationapi.ServiceAccountKind:
102
+					if valid, _ := validation.ValidateNamespaceName(j.Subjects[i].Namespace, false); !valid {
103
+						j.Subjects[i].Namespace = fmt.Sprintf("sanamespacehere%d", i)
104
+					}
105
+					if valid, _ := validation.ValidateServiceAccountName(j.Subjects[i].Name, false); !valid {
106
+						j.Subjects[i].Name = fmt.Sprintf("sanamehere%d", i)
107
+					}
108
+
109
+				case authorizationapi.SystemUserKind, authorizationapi.SystemGroupKind:
110
+					j.Subjects[i].Namespace = ""
111
+					j.Subjects[i].Name = ":" + j.Subjects[i].Name
112
+
113
+				}
114
+
115
+				j.Subjects[i].UID = types.UID("")
116
+				j.Subjects[i].APIVersion = ""
117
+				j.Subjects[i].ResourceVersion = ""
118
+				j.Subjects[i].FieldPath = ""
119
+			}
120
+		},
45 121
 		func(j *template.Template, c fuzz.Continue) {
46 122
 			c.Fuzz(&j.ObjectMeta)
47 123
 			c.Fuzz(&j.Parameters)
... ...
@@ -194,6 +194,18 @@ func deepCopy_v1_ClusterRoleBinding(in v1.ClusterRoleBinding, out *v1.ClusterRol
194 194
 	} else {
195 195
 		out.GroupNames = nil
196 196
 	}
197
+	if in.Subjects != nil {
198
+		out.Subjects = make([]pkgapiv1.ObjectReference, len(in.Subjects))
199
+		for i := range in.Subjects {
200
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
201
+				return err
202
+			} else {
203
+				out.Subjects[i] = newVal.(pkgapiv1.ObjectReference)
204
+			}
205
+		}
206
+	} else {
207
+		out.Subjects = nil
208
+	}
197 209
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
198 210
 		return err
199 211
 	} else {
... ...
@@ -565,6 +577,18 @@ func deepCopy_v1_RoleBinding(in v1.RoleBinding, out *v1.RoleBinding, c *conversi
565 565
 	} else {
566 566
 		out.GroupNames = nil
567 567
 	}
568
+	if in.Subjects != nil {
569
+		out.Subjects = make([]pkgapiv1.ObjectReference, len(in.Subjects))
570
+		for i := range in.Subjects {
571
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
572
+				return err
573
+			} else {
574
+				out.Subjects[i] = newVal.(pkgapiv1.ObjectReference)
575
+			}
576
+		}
577
+	} else {
578
+		out.Subjects = nil
579
+	}
568 580
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
569 581
 		return err
570 582
 	} else {
... ...
@@ -194,6 +194,18 @@ func deepCopy_v1beta3_ClusterRoleBinding(in v1beta3.ClusterRoleBinding, out *v1b
194 194
 	} else {
195 195
 		out.GroupNames = nil
196 196
 	}
197
+	if in.Subjects != nil {
198
+		out.Subjects = make([]pkgapiv1beta3.ObjectReference, len(in.Subjects))
199
+		for i := range in.Subjects {
200
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
201
+				return err
202
+			} else {
203
+				out.Subjects[i] = newVal.(pkgapiv1beta3.ObjectReference)
204
+			}
205
+		}
206
+	} else {
207
+		out.Subjects = nil
208
+	}
197 209
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
198 210
 		return err
199 211
 	} else {
... ...
@@ -573,6 +585,18 @@ func deepCopy_v1beta3_RoleBinding(in v1beta3.RoleBinding, out *v1beta3.RoleBindi
573 573
 	} else {
574 574
 		out.GroupNames = nil
575 575
 	}
576
+	if in.Subjects != nil {
577
+		out.Subjects = make([]pkgapiv1beta3.ObjectReference, len(in.Subjects))
578
+		for i := range in.Subjects {
579
+			if newVal, err := c.DeepCopy(in.Subjects[i]); err != nil {
580
+				return err
581
+			} else {
582
+				out.Subjects[i] = newVal.(pkgapiv1beta3.ObjectReference)
583
+			}
584
+		}
585
+	} else {
586
+		out.Subjects = nil
587
+	}
576 588
 	if newVal, err := c.DeepCopy(in.RoleRef); err != nil {
577 589
 		return err
578 590
 	} else {
... ...
@@ -167,8 +167,7 @@ func ToRoleBinding(in *ClusterRoleBinding) *RoleBinding {
167 167
 
168 168
 	ret := &RoleBinding{}
169 169
 	ret.ObjectMeta = in.ObjectMeta
170
-	ret.Users = in.Users
171
-	ret.Groups = in.Groups
170
+	ret.Subjects = in.Subjects
172 171
 	ret.RoleRef = ToRoleRef(in.RoleRef)
173 172
 	return ret
174 173
 }
... ...
@@ -235,8 +234,7 @@ func ToClusterRoleBinding(in *RoleBinding) *ClusterRoleBinding {
235 235
 
236 236
 	ret := &ClusterRoleBinding{}
237 237
 	ret.ObjectMeta = in.ObjectMeta
238
-	ret.Users = in.Users
239
-	ret.Groups = in.Groups
238
+	ret.Subjects = in.Subjects
240 239
 	ret.RoleRef = ToClusterRoleRef(in.RoleRef)
241 240
 
242 241
 	return ret
... ...
@@ -5,7 +5,12 @@ import (
5 5
 	"sort"
6 6
 	"strings"
7 7
 
8
+	kapi "k8s.io/kubernetes/pkg/api"
9
+	"k8s.io/kubernetes/pkg/api/validation"
10
+	"k8s.io/kubernetes/pkg/controller/serviceaccount"
8 11
 	kutil "k8s.io/kubernetes/pkg/util"
12
+
13
+	// uservalidation "github.com/openshift/origin/pkg/user/api/validation"
9 14
 )
10 15
 
11 16
 func ExpandResources(rawResources kutil.StringSet) kutil.StringSet {
... ...
@@ -85,3 +90,91 @@ func GetPolicyBindingName(policyRefNamespace string) string {
85 85
 }
86 86
 
87 87
 var ClusterPolicyBindingName = GetPolicyBindingName("")
88
+
89
+func BuildSubjects(users, groups []string, userNameValidator, groupNameValidator validation.ValidateNameFunc) []kapi.ObjectReference {
90
+	subjects := []kapi.ObjectReference{}
91
+
92
+	for _, user := range users {
93
+		saNamespace, saName, err := serviceaccount.SplitUsername(user)
94
+		if err == nil {
95
+			subjects = append(subjects, kapi.ObjectReference{Kind: ServiceAccountKind, Namespace: saNamespace, Name: saName})
96
+			continue
97
+		}
98
+
99
+		kind := UserKind
100
+		if valid, _ := userNameValidator(user, false); !valid {
101
+			kind = SystemUserKind
102
+		}
103
+
104
+		subjects = append(subjects, kapi.ObjectReference{Kind: kind, Name: user})
105
+	}
106
+
107
+	for _, group := range groups {
108
+		kind := GroupKind
109
+		if valid, _ := groupNameValidator(group, false); !valid {
110
+			kind = SystemGroupKind
111
+		}
112
+
113
+		subjects = append(subjects, kapi.ObjectReference{Kind: kind, Name: group})
114
+	}
115
+
116
+	return subjects
117
+}
118
+
119
+// StringSubjectsFor returns users and groups for comparison against user.Info.  currentNamespace is used to
120
+// to create usernames for service accounts where namespace=="".
121
+func StringSubjectsFor(currentNamespace string, subjects []kapi.ObjectReference) ([]string, []string) {
122
+	users := []string{}
123
+	groups := []string{}
124
+
125
+	for _, subject := range subjects {
126
+		switch subject.Kind {
127
+		case ServiceAccountKind:
128
+			namespace := currentNamespace
129
+			if len(subject.Namespace) > 0 {
130
+				namespace = subject.Namespace
131
+			}
132
+			users = append(users, serviceaccount.MakeUsername(namespace, subject.Name))
133
+
134
+		case UserKind, SystemUserKind:
135
+			users = append(users, subject.Name)
136
+
137
+		case GroupKind, SystemGroupKind:
138
+			groups = append(groups, subject.Name)
139
+		}
140
+	}
141
+
142
+	return users, groups
143
+}
144
+
145
+// SubjectsStrings returns users, groups, serviceaccounts, unknown for display purposes.  currentNamespace is used to
146
+// hide the subject.Namespace for ServiceAccounts in the currentNamespace
147
+func SubjectsStrings(currentNamespace string, subjects []kapi.ObjectReference) ([]string, []string, []string, []string) {
148
+	users := []string{}
149
+	groups := []string{}
150
+	sas := []string{}
151
+	others := []string{}
152
+
153
+	for _, subject := range subjects {
154
+		switch subject.Kind {
155
+		case ServiceAccountKind:
156
+			if len(subject.Namespace) > 0 && currentNamespace != subject.Namespace {
157
+				sas = append(sas, subject.Namespace+"/"+subject.Name)
158
+			} else {
159
+				sas = append(sas, subject.Name)
160
+			}
161
+
162
+		case UserKind, SystemUserKind:
163
+			users = append(users, subject.Name)
164
+
165
+		case GroupKind, SystemGroupKind:
166
+			groups = append(groups, subject.Name)
167
+
168
+		default:
169
+			others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name))
170
+
171
+		}
172
+	}
173
+
174
+	return users, groups, sas, others
175
+}
... ...
@@ -19,6 +19,12 @@ const (
19 19
 	ResourceAll    = "*"
20 20
 	VerbAll        = "*"
21 21
 	NonResourceAll = "*"
22
+
23
+	UserKind           = "User"
24
+	GroupKind          = "Group"
25
+	ServiceAccountKind = "ServiceAccount"
26
+	SystemUserKind     = "SystemUser"
27
+	SystemGroupKind    = "SystemGroup"
22 28
 )
23 29
 
24 30
 const (
... ...
@@ -142,10 +148,8 @@ type RoleBinding struct {
142 142
 	kapi.TypeMeta
143 143
 	kapi.ObjectMeta
144 144
 
145
-	// Users holds all the usernames directly bound to the role
146
-	Users util.StringSet
147
-	// Groups holds all the groups directly bound to the role
148
-	Groups util.StringSet
145
+	// Subjects hold object references of to authorize with this rule
146
+	Subjects []kapi.ObjectReference
149 147
 
150 148
 	// RoleRef can only reference the current namespace and the global namespace
151 149
 	// If the RoleRef cannot be resolved, the Authorizer must return an error.
... ...
@@ -311,10 +315,8 @@ type ClusterRoleBinding struct {
311 311
 	kapi.TypeMeta
312 312
 	kapi.ObjectMeta
313 313
 
314
-	// Users holds all the usernames directly bound to the role
315
-	Users util.StringSet
316
-	// GroupNames holds all the groups directly bound to the role
317
-	Groups util.StringSet
314
+	// Subjects hold object references of to authorize with this rule
315
+	Subjects []kapi.ObjectReference
318 316
 
319 317
 	// RoleRef can only reference the current namespace and the global namespace
320 318
 	// If the ClusterRoleRef cannot be resolved, the Authorizer must return an error.
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"k8s.io/kubernetes/pkg/util"
9 9
 
10 10
 	newer "github.com/openshift/origin/pkg/authorization/api"
11
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
11 12
 )
12 13
 
13 14
 func convert_v1_ResourceAccessReview_To_api_ResourceAccessReview(in *ResourceAccessReview, out *newer.ResourceAccessReview, s conversion.Scope) error {
... ...
@@ -181,8 +182,12 @@ func convert_v1_RoleBinding_To_api_RoleBinding(in *RoleBinding, out *newer.RoleB
181 181
 		return err
182 182
 	}
183 183
 
184
-	out.Users = util.NewStringSet(in.UserNames...)
185
-	out.Groups = util.NewStringSet(in.GroupNames...)
184
+	// if the users and groups fields are cleared, then respect only subjects.  The field was set in the DefaultConvert above
185
+	if in.UserNames == nil && in.GroupNames == nil {
186
+		return nil
187
+	}
188
+
189
+	out.Subjects = newer.BuildSubjects(in.UserNames, in.GroupNames, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
186 190
 
187 191
 	return nil
188 192
 }
... ...
@@ -192,8 +197,7 @@ func convert_api_RoleBinding_To_v1_RoleBinding(in *newer.RoleBinding, out *RoleB
192 192
 		return err
193 193
 	}
194 194
 
195
-	out.UserNames = in.Users.List()
196
-	out.GroupNames = in.Groups.List()
195
+	out.UserNames, out.GroupNames = newer.StringSubjectsFor(in.Namespace, in.Subjects)
197 196
 
198 197
 	return nil
199 198
 }
... ...
@@ -228,8 +232,12 @@ func convert_v1_ClusterRoleBinding_To_api_ClusterRoleBinding(in *ClusterRoleBind
228 228
 		return err
229 229
 	}
230 230
 
231
-	out.Users = util.NewStringSet(in.UserNames...)
232
-	out.Groups = util.NewStringSet(in.GroupNames...)
231
+	// if the users and groups fields are cleared, then respect only subjects.  The field was set in the DefaultConvert above
232
+	if in.UserNames == nil && in.GroupNames == nil {
233
+		return nil
234
+	}
235
+
236
+	out.Subjects = newer.BuildSubjects(in.UserNames, in.GroupNames, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
233 237
 
234 238
 	return nil
235 239
 }
... ...
@@ -239,8 +247,7 @@ func convert_api_ClusterRoleBinding_To_v1_ClusterRoleBinding(in *newer.ClusterRo
239 239
 		return err
240 240
 	}
241 241
 
242
-	out.UserNames = in.Users.List()
243
-	out.GroupNames = in.Groups.List()
242
+	out.UserNames, out.GroupNames = newer.StringSubjectsFor(in.Namespace, in.Subjects)
244 243
 
245 244
 	return nil
246 245
 }
... ...
@@ -55,6 +55,8 @@ type RoleBinding struct {
55 55
 	UserNames []string `json:"userNames" description:"all the usernames directly bound to the role"`
56 56
 	// GroupNames holds all the groups directly bound to the role
57 57
 	GroupNames []string `json:"groupNames" description:"all the groups directly bound to the role"`
58
+	// Subjects hold object references to authorize with this rule
59
+	Subjects []kapi.ObjectReference `json:"subjects" description:"references to subjects bound to the role.  Only User, Group, SystemUser, SystemGroup, and ServiceAccount are allowed."`
58 60
 
59 61
 	// RoleRef can only reference the current namespace and the global namespace
60 62
 	// If the RoleRef cannot be resolved, the Authorizer must return an error.
... ...
@@ -234,6 +236,8 @@ type ClusterRoleBinding struct {
234 234
 	UserNames []string `json:"userNames" description:"all user names directly bound to the role"`
235 235
 	// GroupNames holds all the groups directly bound to the role
236 236
 	GroupNames []string `json:"groupNames" description:"all the groups directly bound to the role"`
237
+	// Subjects hold object references to authorize with this rule
238
+	Subjects []kapi.ObjectReference `json:"subjects" description:"references to subjects bound to the role.  Only User, Group, SystemUser, SystemGroup, and ServiceAccount are allowed."`
237 239
 
238 240
 	// RoleRef can only reference the current namespace and the global namespace
239 241
 	// If the ClusterRoleRef cannot be resolved, the Authorizer must return an error.
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"k8s.io/kubernetes/pkg/util"
9 9
 
10 10
 	newer "github.com/openshift/origin/pkg/authorization/api"
11
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
11 12
 )
12 13
 
13 14
 func convert_v1beta3_ResourceAccessReview_To_api_ResourceAccessReview(in *ResourceAccessReview, out *newer.ResourceAccessReview, s conversion.Scope) error {
... ...
@@ -182,8 +183,12 @@ func convert_v1beta3_RoleBinding_To_api_RoleBinding(in *RoleBinding, out *newer.
182 182
 		return err
183 183
 	}
184 184
 
185
-	out.Users = util.NewStringSet(in.UserNames...)
186
-	out.Groups = util.NewStringSet(in.GroupNames...)
185
+	// if the users and groups fields are cleared, then respect only subjects.  The field was set in the DefaultConvert above
186
+	if in.UserNames == nil && in.GroupNames == nil {
187
+		return nil
188
+	}
189
+
190
+	out.Subjects = newer.BuildSubjects(in.UserNames, in.GroupNames, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
187 191
 
188 192
 	return nil
189 193
 }
... ...
@@ -193,8 +198,7 @@ func convert_api_RoleBinding_To_v1beta3_RoleBinding(in *newer.RoleBinding, out *
193 193
 		return err
194 194
 	}
195 195
 
196
-	out.UserNames = in.Users.List()
197
-	out.GroupNames = in.Groups.List()
196
+	out.UserNames, out.GroupNames = newer.StringSubjectsFor(in.Namespace, in.Subjects)
198 197
 
199 198
 	return nil
200 199
 }
... ...
@@ -229,8 +233,12 @@ func convert_v1beta3_ClusterRoleBinding_To_api_ClusterRoleBinding(in *ClusterRol
229 229
 		return err
230 230
 	}
231 231
 
232
-	out.Users = util.NewStringSet(in.UserNames...)
233
-	out.Groups = util.NewStringSet(in.GroupNames...)
232
+	// if the users and groups fields are cleared, then respect only subjects.  The field was set in the DefaultConvert above
233
+	if in.UserNames == nil && in.GroupNames == nil {
234
+		return nil
235
+	}
236
+
237
+	out.Subjects = newer.BuildSubjects(in.UserNames, in.GroupNames, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
234 238
 
235 239
 	return nil
236 240
 }
... ...
@@ -240,8 +248,7 @@ func convert_api_ClusterRoleBinding_To_v1beta3_ClusterRoleBinding(in *newer.Clus
240 240
 		return err
241 241
 	}
242 242
 
243
-	out.UserNames = in.Users.List()
244
-	out.GroupNames = in.Groups.List()
243
+	out.UserNames, out.GroupNames = newer.StringSubjectsFor(in.Namespace, in.Subjects)
245 244
 
246 245
 	return nil
247 246
 }
... ...
@@ -58,6 +58,8 @@ type RoleBinding struct {
58 58
 	UserNames []string `json:"userNames"`
59 59
 	// GroupNames holds all the groups directly bound to the role
60 60
 	GroupNames []string `json:"groupNames"`
61
+	// Subjects hold object references to authorize with this rule
62
+	Subjects []kapi.ObjectReference `json:"subjects"`
61 63
 
62 64
 	// Since Policy is a singleton, this is sufficient knowledge to locate a role
63 65
 	// RoleRefs can only reference the current namespace and the global namespace
... ...
@@ -229,6 +231,8 @@ type ClusterRoleBinding struct {
229 229
 	UserNames []string `json:"userNames"`
230 230
 	// GroupNames holds all the groups directly bound to the role
231 231
 	GroupNames []string `json:"groupNames"`
232
+	// Subjects hold object references to authorize with this rule
233
+	Subjects []kapi.ObjectReference `json:"subjects"`
232 234
 
233 235
 	// Since Policy is a singleton, this is sufficient knowledge to locate a role
234 236
 	// ClusterRoleRefs can only reference the current namespace and the global namespace
... ...
@@ -1,12 +1,16 @@
1 1
 package validation
2 2
 
3 3
 import (
4
+	"fmt"
5
+
6
+	kapi "k8s.io/kubernetes/pkg/api"
4 7
 	"k8s.io/kubernetes/pkg/api/validation"
5 8
 	"k8s.io/kubernetes/pkg/util"
6 9
 	"k8s.io/kubernetes/pkg/util/fielderrors"
7 10
 
8 11
 	oapi "github.com/openshift/origin/pkg/api"
9 12
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
13
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
10 14
 )
11 15
 
12 16
 func ValidateSubjectAccessReview(review *authorizationapi.SubjectAccessReview) fielderrors.ValidationErrorList {
... ...
@@ -248,6 +252,67 @@ func ValidateRoleBinding(roleBinding *authorizationapi.RoleBinding, isNamespaced
248 248
 		}
249 249
 	}
250 250
 
251
+	for i, subject := range roleBinding.Subjects {
252
+		allErrs = append(allErrs, ValidateRoleBindingSubject(subject, isNamespaced).Prefix(fmt.Sprintf("subjects[%d]", i))...)
253
+	}
254
+
255
+	return allErrs
256
+}
257
+
258
+func ValidateRoleBindingSubject(subject kapi.ObjectReference, isNamespaced bool) fielderrors.ValidationErrorList {
259
+	allErrs := fielderrors.ValidationErrorList{}
260
+
261
+	if len(subject.Name) == 0 {
262
+		allErrs = append(allErrs, fielderrors.NewFieldRequired("name"))
263
+	}
264
+	if len(subject.UID) != 0 {
265
+		allErrs = append(allErrs, fielderrors.NewFieldForbidden("uid", subject.UID))
266
+	}
267
+	if len(subject.APIVersion) != 0 {
268
+		allErrs = append(allErrs, fielderrors.NewFieldForbidden("apiVersion", subject.APIVersion))
269
+	}
270
+	if len(subject.ResourceVersion) != 0 {
271
+		allErrs = append(allErrs, fielderrors.NewFieldForbidden("resourceVersion", subject.ResourceVersion))
272
+	}
273
+	if len(subject.FieldPath) != 0 {
274
+		allErrs = append(allErrs, fielderrors.NewFieldForbidden("fieldPath", subject.FieldPath))
275
+	}
276
+
277
+	switch subject.Kind {
278
+	case authorizationapi.ServiceAccountKind:
279
+		if valid, reason := validation.ValidateServiceAccountName(subject.Name, false); len(subject.Name) > 0 && !valid {
280
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason))
281
+		}
282
+		if !isNamespaced && len(subject.Namespace) == 0 {
283
+			allErrs = append(allErrs, fielderrors.NewFieldRequired("namespace"))
284
+		}
285
+
286
+	case authorizationapi.UserKind:
287
+		if valid, reason := uservalidation.ValidateUserName(subject.Name, false); len(subject.Name) > 0 && !valid {
288
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason))
289
+		}
290
+
291
+	case authorizationapi.GroupKind:
292
+		if valid, reason := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && !valid {
293
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, reason))
294
+		}
295
+
296
+	case authorizationapi.SystemUserKind:
297
+		isValidSAName, _ := validation.ValidateServiceAccountName(subject.Name, false)
298
+		isValidUserName, _ := uservalidation.ValidateUserName(subject.Name, false)
299
+		if isValidSAName || isValidUserName {
300
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, "conforms to User.name or ServiceAccount.name restrictions"))
301
+		}
302
+
303
+	case authorizationapi.SystemGroupKind:
304
+		if valid, _ := uservalidation.ValidateGroupName(subject.Name, false); len(subject.Name) > 0 && valid {
305
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid("name", subject.Name, "conforms to Group.name restrictions"))
306
+		}
307
+
308
+	default:
309
+		allErrs = append(allErrs, fielderrors.NewFieldValueNotSupported("kind", subject.Kind, []string{authorizationapi.ServiceAccountKind, authorizationapi.UserKind, authorizationapi.GroupKind, authorizationapi.SystemGroupKind, authorizationapi.SystemUserKind}))
310
+	}
311
+
251 312
 	return allErrs
252 313
 }
253 314
 
... ...
@@ -168,6 +168,13 @@ func TestValidateRoleBinding(t *testing.T) {
168 168
 		&authorizationapi.RoleBinding{
169 169
 			ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
170 170
 			RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
171
+			Subjects: []kapi.ObjectReference{
172
+				{Name: "validsaname", Kind: authorizationapi.ServiceAccountKind},
173
+				{Name: "valid@username", Kind: authorizationapi.UserKind},
174
+				{Name: "system:admin", Kind: authorizationapi.SystemUserKind},
175
+				{Name: "valid@groupname", Kind: authorizationapi.GroupKind},
176
+				{Name: "system:authenticated", Kind: authorizationapi.SystemGroupKind},
177
+			},
171 178
 		},
172 179
 		true,
173 180
 	)
... ...
@@ -212,6 +219,60 @@ func TestValidateRoleBinding(t *testing.T) {
212 212
 			T: fielderrors.ValidationErrorTypeRequired,
213 213
 			F: "roleRef.name",
214 214
 		},
215
+		"bad subject kind": {
216
+			A: authorizationapi.RoleBinding{
217
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
218
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
219
+				Subjects:   []kapi.ObjectReference{{Name: "subject"}},
220
+			},
221
+			T: fielderrors.ValidationErrorTypeNotSupported,
222
+			F: "subjects[0].kind",
223
+		},
224
+		"bad subject name": {
225
+			A: authorizationapi.RoleBinding{
226
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
227
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
228
+				Subjects:   []kapi.ObjectReference{{Name: "subject:bad", Kind: authorizationapi.ServiceAccountKind}},
229
+			},
230
+			T: fielderrors.ValidationErrorTypeInvalid,
231
+			F: "subjects[0].name",
232
+		},
233
+		"bad system user name": {
234
+			A: authorizationapi.RoleBinding{
235
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
236
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
237
+				Subjects:   []kapi.ObjectReference{{Name: "user", Kind: authorizationapi.SystemUserKind}},
238
+			},
239
+			T: fielderrors.ValidationErrorTypeInvalid,
240
+			F: "subjects[0].name",
241
+		},
242
+		"bad system group name": {
243
+			A: authorizationapi.RoleBinding{
244
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
245
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
246
+				Subjects:   []kapi.ObjectReference{{Name: "valid", Kind: authorizationapi.SystemGroupKind}},
247
+			},
248
+			T: fielderrors.ValidationErrorTypeInvalid,
249
+			F: "subjects[0].name",
250
+		},
251
+		"forbidden fields": {
252
+			A: authorizationapi.RoleBinding{
253
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
254
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
255
+				Subjects:   []kapi.ObjectReference{{Name: "subject", Kind: authorizationapi.ServiceAccountKind, APIVersion: "foo"}},
256
+			},
257
+			T: fielderrors.ValidationErrorTypeForbidden,
258
+			F: "subjects[0].apiVersion",
259
+		},
260
+		"missing subject name": {
261
+			A: authorizationapi.RoleBinding{
262
+				ObjectMeta: kapi.ObjectMeta{Namespace: kapi.NamespaceDefault, Name: "master"},
263
+				RoleRef:    kapi.ObjectReference{Namespace: "master", Name: "valid"},
264
+				Subjects:   []kapi.ObjectReference{{Kind: authorizationapi.ServiceAccountKind}},
265
+			},
266
+			T: fielderrors.ValidationErrorTypeRequired,
267
+			F: "subjects[0].name",
268
+		},
215 269
 	}
216 270
 	for k, v := range errorCases {
217 271
 		errs := ValidateRoleBinding(&v.A, true)
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	kapi "k8s.io/kubernetes/pkg/api"
10 10
 	"k8s.io/kubernetes/pkg/auth/user"
11
+	"k8s.io/kubernetes/pkg/controller/serviceaccount"
11 12
 	"k8s.io/kubernetes/pkg/util"
12 13
 
13 14
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
... ...
@@ -65,6 +66,58 @@ func TestResourceNameAllow(t *testing.T) {
65 65
 	test.test(t)
66 66
 }
67 67
 
68
+func TestClusterBindingServiceAccountSubject(t *testing.T) {
69
+	test := &authorizeTest{
70
+		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceNone), &user.DefaultInfo{Name: serviceaccount.MakeUsername("foo", "default")}),
71
+		attributes: &DefaultAuthorizationAttributes{
72
+			Verb:         "get",
73
+			Resource:     "users",
74
+			ResourceName: "any",
75
+		},
76
+		expectedAllowed: true,
77
+		expectedReason:  "allowed by cluster rule",
78
+	}
79
+	test.clusterPolicies = newDefaultClusterPolicies()
80
+	test.clusterBindings = newDefaultClusterPolicyBindings()
81
+	test.test(t)
82
+}
83
+
84
+func TestLocalBindingServiceAccountSubject(t *testing.T) {
85
+	test := &authorizeTest{
86
+		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: serviceaccount.MakeUsername("adze", "second")}),
87
+		attributes: &DefaultAuthorizationAttributes{
88
+			Verb:     "get",
89
+			Resource: "pods",
90
+		},
91
+		expectedAllowed: true,
92
+		expectedReason:  "allowed by rule in adze",
93
+	}
94
+	test.clusterPolicies = newDefaultClusterPolicies()
95
+	test.policies = append(test.policies, newAdzePolicies()...)
96
+	test.clusterBindings = newDefaultClusterPolicyBindings()
97
+	test.bindings = append(test.bindings, newAdzeBindings()...)
98
+
99
+	test.test(t)
100
+}
101
+
102
+func TestLocalBindingOtherServiceAccountSubject(t *testing.T) {
103
+	test := &authorizeTest{
104
+		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: serviceaccount.MakeUsername("other", "first")}),
105
+		attributes: &DefaultAuthorizationAttributes{
106
+			Verb:     "get",
107
+			Resource: "pods",
108
+		},
109
+		expectedAllowed: true,
110
+		expectedReason:  "allowed by rule in adze",
111
+	}
112
+	test.clusterPolicies = newDefaultClusterPolicies()
113
+	test.policies = append(test.policies, newAdzePolicies()...)
114
+	test.clusterBindings = newDefaultClusterPolicyBindings()
115
+	test.bindings = append(test.bindings, newAdzeBindings()...)
116
+
117
+	test.test(t)
118
+}
119
+
68 120
 func TestDeniedWithError(t *testing.T) {
69 121
 	test := &authorizeTest{
70 122
 		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Anna"}),
... ...
@@ -86,7 +139,7 @@ func TestDeniedWithError(t *testing.T) {
86 86
 		RoleRef: kapi.ObjectReference{
87 87
 			Name: "not-a-real-binding",
88 88
 		},
89
-		Users: util.NewStringSet("Anna"),
89
+		Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Anna"}},
90 90
 	}
91 91
 	test.policyRetrievalError = errors.New("my special error")
92 92
 
... ...
@@ -114,7 +167,7 @@ func TestAllowedWithMissingBinding(t *testing.T) {
114 114
 		RoleRef: kapi.ObjectReference{
115 115
 			Name: "not-a-real-binding",
116 116
 		},
117
-		Users: util.NewStringSet("Anna"),
117
+		Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Anna"}},
118 118
 	}
119 119
 
120 120
 	test.test(t)
... ...
@@ -446,8 +499,7 @@ func newDefaultClusterPolicyBindings() []authorizationapi.ClusterPolicyBinding {
446 446
 				RoleRef: kapi.ObjectReference{
447 447
 					Name: "cluster-admin",
448 448
 				},
449
-				Users:  util.NewStringSet("ClusterAdmin"),
450
-				Groups: util.NewStringSet("RootUsers"),
449
+				Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "ClusterAdmin"}, {Kind: authorizationapi.GroupKind, Name: "RootUsers"}, {Name: "default", Namespace: "foo", Kind: authorizationapi.ServiceAccountKind}},
451 450
 			},
452 451
 			"user-only": {
453 452
 				ObjectMeta: kapi.ObjectMeta{
... ...
@@ -456,7 +508,7 @@ func newDefaultClusterPolicyBindings() []authorizationapi.ClusterPolicyBinding {
456 456
 				RoleRef: kapi.ObjectReference{
457 457
 					Name: "basic-user",
458 458
 				},
459
-				Users: util.NewStringSet("just-a-user"),
459
+				Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "just-a-user"}},
460 460
 			},
461 461
 		},
462 462
 	}
... ...
@@ -504,7 +556,7 @@ func newAdzeBindings() []authorizationapi.PolicyBinding {
504 504
 					RoleRef: kapi.ObjectReference{
505 505
 						Name: bootstrappolicy.AdminRoleName,
506 506
 					},
507
-					Users: util.NewStringSet("Anna"),
507
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Anna"}},
508 508
 				},
509 509
 				"viewers": {
510 510
 					ObjectMeta: kapi.ObjectMeta{
... ...
@@ -514,7 +566,11 @@ func newAdzeBindings() []authorizationapi.PolicyBinding {
514 514
 					RoleRef: kapi.ObjectReference{
515 515
 						Name: bootstrappolicy.ViewRoleName,
516 516
 					},
517
-					Users: util.NewStringSet("Valerie"),
517
+					Subjects: []kapi.ObjectReference{
518
+						{Kind: authorizationapi.UserKind, Name: "Valerie"},
519
+						{Name: "first", Namespace: "other", Kind: authorizationapi.ServiceAccountKind},
520
+						{Name: "second", Kind: authorizationapi.ServiceAccountKind},
521
+					},
518 522
 				},
519 523
 				"editors": {
520 524
 					ObjectMeta: kapi.ObjectMeta{
... ...
@@ -524,7 +580,7 @@ func newAdzeBindings() []authorizationapi.PolicyBinding {
524 524
 					RoleRef: kapi.ObjectReference{
525 525
 						Name: bootstrappolicy.EditRoleName,
526 526
 					},
527
-					Users: util.NewStringSet("Ellen"),
527
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Ellen"}},
528 528
 				},
529 529
 			},
530 530
 		},
... ...
@@ -543,7 +599,7 @@ func newAdzeBindings() []authorizationapi.PolicyBinding {
543 543
 						Name:      "restrictedViewer",
544 544
 						Namespace: "adze",
545 545
 					},
546
-					Users: util.NewStringSet("Rachel"),
546
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Rachel"}},
547 547
 				},
548 548
 			},
549 549
 		},
... ...
@@ -463,7 +463,7 @@ func newMalletBindings() []authorizationapi.PolicyBinding {
463 463
 					RoleRef: kapi.ObjectReference{
464 464
 						Name: bootstrappolicy.AdminRoleName,
465 465
 					},
466
-					Users: util.NewStringSet("Matthew"),
466
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Matthew"}},
467 467
 				},
468 468
 				"viewers": {
469 469
 					ObjectMeta: kapi.ObjectMeta{
... ...
@@ -473,7 +473,7 @@ func newMalletBindings() []authorizationapi.PolicyBinding {
473 473
 					RoleRef: kapi.ObjectReference{
474 474
 						Name: bootstrappolicy.ViewRoleName,
475 475
 					},
476
-					Users: util.NewStringSet("Victor"),
476
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Victor"}},
477 477
 				},
478 478
 				"editors": {
479 479
 					ObjectMeta: kapi.ObjectMeta{
... ...
@@ -483,7 +483,7 @@ func newMalletBindings() []authorizationapi.PolicyBinding {
483 483
 					RoleRef: kapi.ObjectReference{
484 484
 						Name: bootstrappolicy.EditRoleName,
485 485
 					},
486
-					Users: util.NewStringSet("Edgar"),
486
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Edgar"}},
487 487
 				},
488 488
 			},
489 489
 		},
... ...
@@ -534,7 +534,7 @@ func newInvalidExtensionBindings() []authorizationapi.PolicyBinding {
534 534
 						Name:      "badExtension",
535 535
 						Namespace: "mallet",
536 536
 					},
537
-					Users: util.NewStringSet("Brad"),
537
+					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Brad"}},
538 538
 				},
539 539
 			},
540 540
 		},
... ...
@@ -34,7 +34,7 @@ func TestSubjects(t *testing.T) {
34 34
 			Verb:     "get",
35 35
 			Resource: "pods",
36 36
 		},
37
-		expectedUsers:  util.NewStringSet("Anna", "ClusterAdmin", "Ellen", "Valerie"),
37
+		expectedUsers:  util.NewStringSet("Anna", "ClusterAdmin", "Ellen", "Valerie", "system:serviceaccount:adze:second", "system:serviceaccount:foo:default", "system:serviceaccount:other:first"),
38 38
 		expectedGroups: util.NewStringSet("RootUsers", "system:cluster-admins", "system:cluster-readers", "system:masters", "system:nodes"),
39 39
 	}
40 40
 	test.clusterPolicies = newDefaultClusterPolicies()
... ...
@@ -208,10 +208,15 @@ func (a RoleBindingAdapter) RoleRef() kapi.ObjectReference {
208 208
 }
209 209
 
210 210
 func (a RoleBindingAdapter) Users() util.StringSet {
211
-	return a.roleBinding.Users
211
+	users, _ := authorizationapi.StringSubjectsFor(a.roleBinding.Namespace, a.roleBinding.Subjects)
212
+
213
+	return util.NewStringSet(users...)
212 214
 }
215
+
213 216
 func (a RoleBindingAdapter) Groups() util.StringSet {
214
-	return a.roleBinding.Groups
217
+	_, groups := authorizationapi.StringSubjectsFor(a.roleBinding.Namespace, a.roleBinding.Subjects)
218
+
219
+	return util.NewStringSet(groups...)
215 220
 }
216 221
 
217 222
 type ClusterPolicyBindingAdapter struct {
... ...
@@ -260,8 +265,12 @@ func (a ClusterRoleBindingAdapter) RoleRef() kapi.ObjectReference {
260 260
 }
261 261
 
262 262
 func (a ClusterRoleBindingAdapter) Users() util.StringSet {
263
-	return a.roleBinding.Users
263
+	users, _ := authorizationapi.StringSubjectsFor(a.roleBinding.Namespace, a.roleBinding.Subjects)
264
+
265
+	return util.NewStringSet(users...)
264 266
 }
265 267
 func (a ClusterRoleBindingAdapter) Groups() util.StringSet {
266
-	return a.roleBinding.Groups
268
+	_, groups := authorizationapi.StringSubjectsFor(a.roleBinding.Namespace, a.roleBinding.Subjects)
269
+
270
+	return util.NewStringSet(groups...)
267 271
 }
... ...
@@ -44,7 +44,7 @@ func testNewClusterBindings() []authorizationapi.ClusterPolicyBinding {
44 44
 				"cluster-admins": {
45 45
 					ObjectMeta: kapi.ObjectMeta{Name: "cluster-admins"},
46 46
 					RoleRef:    kapi.ObjectReference{Name: "cluster-admin"},
47
-					Users:      util.NewStringSet("system:admin"),
47
+					Subjects:   []kapi.ObjectReference{{Kind: authorizationapi.SystemUserKind, Name: "system:admin"}},
48 48
 				},
49 49
 			},
50 50
 		},
... ...
@@ -7,12 +7,13 @@ import (
7 7
 
8 8
 	"github.com/spf13/cobra"
9 9
 
10
-	"k8s.io/kubernetes/pkg/controller/serviceaccount"
10
+	kapi "k8s.io/kubernetes/pkg/api"
11 11
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12 12
 	"k8s.io/kubernetes/pkg/util"
13 13
 
14 14
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
15 15
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
16
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
16 17
 )
17 18
 
18 19
 const (
... ...
@@ -32,8 +33,9 @@ type RoleModificationOptions struct {
32 32
 	RoleName            string
33 33
 	RoleBindingAccessor RoleBindingAccessor
34 34
 
35
-	Users  []string
36
-	Groups []string
35
+	Users    []string
36
+	Groups   []string
37
+	Subjects []kapi.ObjectReference
37 38
 }
38 39
 
39 40
 // NewCmdAddRoleToGroup implements the OpenShift cli add-role-to-group command
... ...
@@ -246,7 +248,7 @@ func (o *RoleModificationOptions) CompleteUserWithSA(f *clientcmd.Factory, args
246 246
 	o.RoleBindingAccessor = NewLocalRoleBindingAccessor(roleBindingNamespace, osClient)
247 247
 
248 248
 	for _, sa := range saNames {
249
-		o.Users = append(o.Users, serviceaccount.MakeUsername(roleBindingNamespace, sa))
249
+		o.Subjects = append(o.Subjects, kapi.ObjectReference{Name: sa, Kind: "ServiceAccount"})
250 250
 	}
251 251
 
252 252
 	return nil
... ...
@@ -293,7 +295,7 @@ func (o *RoleModificationOptions) AddRole() error {
293 293
 	var roleBinding *authorizationapi.RoleBinding
294 294
 	isUpdate := true
295 295
 	if len(roleBindings) == 0 {
296
-		roleBinding = &authorizationapi.RoleBinding{Users: util.NewStringSet(), Groups: util.NewStringSet()}
296
+		roleBinding = &authorizationapi.RoleBinding{}
297 297
 		isUpdate = false
298 298
 	} else {
299 299
 		// only need to add the user or group to a single roleBinding on the role.  Just choose the first one
... ...
@@ -303,8 +305,21 @@ func (o *RoleModificationOptions) AddRole() error {
303 303
 	roleBinding.RoleRef.Namespace = o.RoleNamespace
304 304
 	roleBinding.RoleRef.Name = o.RoleName
305 305
 
306
-	roleBinding.Users.Insert(o.Users...)
307
-	roleBinding.Groups.Insert(o.Groups...)
306
+	newSubjects := authorizationapi.BuildSubjects(o.Users, o.Groups, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
307
+	newSubjects = append(newSubjects, o.Subjects...)
308
+
309
+subjectCheck:
310
+	for _, newSubject := range newSubjects {
311
+		for _, existingSubject := range roleBinding.Subjects {
312
+			if existingSubject.Kind == newSubject.Kind &&
313
+				existingSubject.Name == newSubject.Name &&
314
+				existingSubject.Namespace == newSubject.Namespace {
315
+				continue subjectCheck
316
+			}
317
+		}
318
+
319
+		roleBinding.Subjects = append(roleBinding.Subjects, newSubject)
320
+	}
308 321
 
309 322
 	if isUpdate {
310 323
 		err = o.RoleBindingAccessor.UpdateRoleBinding(roleBinding)
... ...
@@ -328,9 +343,11 @@ func (o *RoleModificationOptions) RemoveRole() error {
328 328
 		return fmt.Errorf("unable to locate RoleBinding for %v/%v", o.RoleNamespace, o.RoleName)
329 329
 	}
330 330
 
331
+	subjectsToRemove := authorizationapi.BuildSubjects(o.Users, o.Groups, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
332
+	subjectsToRemove = append(subjectsToRemove, o.Subjects...)
333
+
331 334
 	for _, roleBinding := range roleBindings {
332
-		roleBinding.Groups.Delete(o.Groups...)
333
-		roleBinding.Users.Delete(o.Users...)
335
+		roleBinding.Subjects = removeSubjects(roleBinding.Subjects, subjectsToRemove)
334 336
 
335 337
 		err = o.RoleBindingAccessor.UpdateRoleBinding(roleBinding)
336 338
 		if err != nil {
... ...
@@ -340,3 +357,23 @@ func (o *RoleModificationOptions) RemoveRole() error {
340 340
 
341 341
 	return nil
342 342
 }
343
+
344
+func removeSubjects(haystack, needles []kapi.ObjectReference) []kapi.ObjectReference {
345
+	newSubjects := []kapi.ObjectReference{}
346
+
347
+existingLoop:
348
+	for _, existingSubject := range haystack {
349
+		for _, toRemove := range needles {
350
+			if existingSubject.Kind == toRemove.Kind &&
351
+				existingSubject.Name == toRemove.Name &&
352
+				existingSubject.Namespace == toRemove.Namespace {
353
+				continue existingLoop
354
+
355
+			}
356
+		}
357
+
358
+		newSubjects = append(newSubjects, existingSubject)
359
+	}
360
+
361
+	return newSubjects
362
+}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/spf13/cobra"
9 9
 
10
+	kapi "k8s.io/kubernetes/pkg/api"
10 11
 	"k8s.io/kubernetes/pkg/fields"
11 12
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12 13
 	"k8s.io/kubernetes/pkg/labels"
... ...
@@ -15,6 +16,7 @@ import (
15 15
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
16 16
 	"github.com/openshift/origin/pkg/client"
17 17
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
18
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
18 19
 )
19 20
 
20 21
 const (
... ...
@@ -103,20 +105,25 @@ func (o *RemoveFromProjectOptions) Run() error {
103 103
 
104 104
 	usersRemoved := util.StringSet{}
105 105
 	groupsRemoved := util.StringSet{}
106
+	sasRemoved := util.StringSet{}
107
+	othersRemoved := util.StringSet{}
108
+
109
+	subjectsToRemove := authorizationapi.BuildSubjects(o.Users, o.Groups, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
106 110
 
107 111
 	for _, currPolicyBinding := range bindingList.Items {
108 112
 		for _, currBinding := range authorizationapi.SortRoleBindings(currPolicyBinding.RoleBindings, true) {
109
-			bindingHasGroups := len(o.Groups) > 0 && currBinding.Groups.HasAny(o.Groups...)
110
-			bindingHasUsers := len(o.Users) > 0 && currBinding.Users.HasAny(o.Users...)
111
-			if !bindingHasGroups && !bindingHasUsers {
112
-				continue
113
-			}
113
+			originalSubjects := make([]kapi.ObjectReference, len(currBinding.Subjects))
114
+			copy(originalSubjects, currBinding.Subjects)
115
+			oldUsers, oldGroups, oldSAs, oldOthers := authorizationapi.SubjectsStrings(currBinding.Namespace, originalSubjects)
116
+			oldUsersSet, oldGroupsSet, oldSAsSet, oldOtherSet := util.NewStringSet(oldUsers...), util.NewStringSet(oldGroups...), util.NewStringSet(oldSAs...), util.NewStringSet(oldOthers...)
114 117
 
115
-			existingGroups := util.NewStringSet(currBinding.Groups.List()...)
116
-			existingUsers := util.NewStringSet(currBinding.Users.List()...)
118
+			currBinding.Subjects = removeSubjects(currBinding.Subjects, subjectsToRemove)
119
+			newUsers, newGroups, newSAs, newOthers := authorizationapi.SubjectsStrings(currBinding.Namespace, currBinding.Subjects)
120
+			newUsersSet, newGroupsSet, newSAsSet, newOtherSet := util.NewStringSet(newUsers...), util.NewStringSet(newGroups...), util.NewStringSet(newSAs...), util.NewStringSet(newOthers...)
117 121
 
118
-			currBinding.Groups.Delete(o.Groups...)
119
-			currBinding.Users.Delete(o.Users...)
122
+			if len(currBinding.Subjects) == len(originalSubjects) {
123
+				continue
124
+			}
120 125
 
121 126
 			_, err = o.Client.RoleBindings(o.BindingNamespace).Update(currBinding)
122 127
 			if err != nil {
... ...
@@ -127,23 +134,32 @@ func (o *RemoveFromProjectOptions) Run() error {
127 127
 			if len(currBinding.RoleRef.Namespace) == 0 {
128 128
 				roleDisplayName = currBinding.RoleRef.Name
129 129
 			}
130
-			if diff := existingGroups.Difference(currBinding.Groups); len(diff) != 0 {
130
+
131
+			if diff := oldUsersSet.Difference(newUsersSet); len(diff) != 0 {
132
+				fmt.Fprintf(o.Out, "Removing %s from users %v in project %s.\n", roleDisplayName, diff.List(), o.BindingNamespace)
133
+				usersRemoved.Insert(diff.List()...)
134
+			}
135
+			if diff := oldGroupsSet.Difference(newGroupsSet); len(diff) != 0 {
131 136
 				fmt.Fprintf(o.Out, "Removing %s from groups %v in project %s.\n", roleDisplayName, diff.List(), o.BindingNamespace)
132 137
 				groupsRemoved.Insert(diff.List()...)
133 138
 			}
134
-			if diff := existingUsers.Difference(currBinding.Users); len(diff) != 0 {
135
-				fmt.Fprintf(o.Out, "Removing %s from users %v in project %s.\n", roleDisplayName, diff.List(), o.BindingNamespace)
136
-				usersRemoved.Insert(diff.List()...)
139
+			if diff := oldSAsSet.Difference(newSAsSet); len(diff) != 0 {
140
+				fmt.Fprintf(o.Out, "Removing %s from serviceaccounts %v in project %s.\n", roleDisplayName, diff.List(), o.BindingNamespace)
141
+				sasRemoved.Insert(diff.List()...)
142
+			}
143
+			if diff := oldOtherSet.Difference(newOtherSet); len(diff) != 0 {
144
+				fmt.Fprintf(o.Out, "Removing %s from subjects %v in project %s.\n", roleDisplayName, diff.List(), o.BindingNamespace)
145
+				othersRemoved.Insert(diff.List()...)
137 146
 			}
138 147
 		}
139 148
 	}
140 149
 
141
-	if diff := util.NewStringSet(o.Groups...).Difference(groupsRemoved); len(diff) != 0 {
142
-		fmt.Fprintf(o.Out, "Groups %v were not bound to roles in project %s.\n", diff.List(), o.BindingNamespace)
143
-	}
144 150
 	if diff := util.NewStringSet(o.Users...).Difference(usersRemoved); len(diff) != 0 {
145 151
 		fmt.Fprintf(o.Out, "Users %v were not bound to roles in project %s.\n", diff.List(), o.BindingNamespace)
146 152
 	}
153
+	if diff := util.NewStringSet(o.Groups...).Difference(groupsRemoved); len(diff) != 0 {
154
+		fmt.Fprintf(o.Out, "Groups %v were not bound to roles in project %s.\n", diff.List(), o.BindingNamespace)
155
+	}
147 156
 
148 157
 	return nil
149 158
 }
... ...
@@ -128,8 +128,7 @@ func (o *NewProjectOptions) Run(useNodeSelector bool) error {
128 128
 			RoleName:            binding.RoleRef.Name,
129 129
 			RoleNamespace:       binding.RoleRef.Namespace,
130 130
 			RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(o.ProjectName, o.Client),
131
-			Users:               binding.Users.List(),
132
-			Groups:              binding.Groups.List(),
131
+			Subjects:            binding.Subjects,
133 132
 		}
134 133
 		if err := addRole.AddRole(); err != nil {
135 134
 			fmt.Printf("Could not add service accounts to the %v role: %v\n", binding.RoleRef.Name, err)
... ...
@@ -1003,6 +1003,7 @@ func (d *PolicyBindingDescriber) Describe(namespace, name string) (string, error
1003 1003
 }
1004 1004
 
1005 1005
 func DescribePolicyBinding(policyBinding *authorizationapi.PolicyBinding) (string, error) {
1006
+
1006 1007
 	return tabbedString(func(out *tabwriter.Writer) error {
1007 1008
 		formatMeta(out, policyBinding.ObjectMeta)
1008 1009
 		formatString(out, "Last Modified", policyBinding.LastModified)
... ...
@@ -1011,10 +1012,14 @@ func DescribePolicyBinding(policyBinding *authorizationapi.PolicyBinding) (strin
1011 1011
 		// using .List() here because I always want the sorted order that it provides
1012 1012
 		for _, key := range util.KeySet(reflect.ValueOf(policyBinding.RoleBindings)).List() {
1013 1013
 			roleBinding := policyBinding.RoleBindings[key]
1014
+			users, groups, sas, others := authorizationapi.SubjectsStrings(roleBinding.Namespace, roleBinding.Subjects)
1015
+
1014 1016
 			formatString(out, "RoleBinding["+key+"]", " ")
1015 1017
 			formatString(out, "\tRole", roleBinding.RoleRef.Name)
1016
-			formatString(out, "\tUsers", roleBinding.Users.List())
1017
-			formatString(out, "\tGroups", roleBinding.Groups.List())
1018
+			formatString(out, "\tUsers", strings.Join(users, ", "))
1019
+			formatString(out, "\tGroups", strings.Join(groups, ", "))
1020
+			formatString(out, "\tServiceAccounts", strings.Join(sas, ", "))
1021
+			formatString(out, "\tSubjects", strings.Join(others, ", "))
1018 1022
 		}
1019 1023
 
1020 1024
 		return nil
... ...
@@ -1048,12 +1053,16 @@ func (d *RoleBindingDescriber) Describe(namespace, name string) (string, error)
1048 1048
 
1049 1049
 // DescribeRoleBinding prints out information about a role binding and its associated role
1050 1050
 func DescribeRoleBinding(roleBinding *authorizationapi.RoleBinding, role *authorizationapi.Role, err error) (string, error) {
1051
+	users, groups, sas, others := authorizationapi.SubjectsStrings(roleBinding.Namespace, roleBinding.Subjects)
1052
+
1051 1053
 	return tabbedString(func(out *tabwriter.Writer) error {
1052 1054
 		formatMeta(out, roleBinding.ObjectMeta)
1053 1055
 
1054 1056
 		formatString(out, "Role", roleBinding.RoleRef.Namespace+"/"+roleBinding.RoleRef.Name)
1055
-		formatString(out, "Users", roleBinding.Users.List())
1056
-		formatString(out, "Groups", roleBinding.Groups.List())
1057
+		formatString(out, "Users", strings.Join(users, ", "))
1058
+		formatString(out, "Groups", strings.Join(groups, ", "))
1059
+		formatString(out, "ServiceAccounts", strings.Join(sas, ", "))
1060
+		formatString(out, "Subjects", strings.Join(others, ", "))
1057 1061
 
1058 1062
 		switch {
1059 1063
 		case err != nil:
... ...
@@ -39,7 +39,7 @@ var (
39 39
 	templateColumns         = []string{"NAME", "DESCRIPTION", "PARAMETERS", "OBJECTS"}
40 40
 	policyColumns           = []string{"NAME", "ROLES", "LAST MODIFIED"}
41 41
 	policyBindingColumns    = []string{"NAME", "ROLE BINDINGS", "LAST MODIFIED"}
42
-	roleBindingColumns      = []string{"NAME", "ROLE", "USERS", "GROUPS"}
42
+	roleBindingColumns      = []string{"NAME", "ROLE", "USERS", "GROUPS", "SERVICE ACCOUNTS", "SUBJECTS"}
43 43
 	roleColumns             = []string{"NAME"}
44 44
 
45 45
 	oauthClientColumns              = []string{"NAME", "SECRET", "WWW-CHALLENGE", "REDIRECT URIS"}
... ...
@@ -512,7 +512,9 @@ func printRoleBinding(roleBinding *authorizationapi.RoleBinding, w io.Writer, wi
512 512
 			return err
513 513
 		}
514 514
 	}
515
-	_, err := fmt.Fprintf(w, "%s\t%s\t%v\t%v\n", roleBinding.Name, roleBinding.RoleRef.Namespace+"/"+roleBinding.RoleRef.Name, roleBinding.Users.List(), roleBinding.Groups.List())
515
+	users, groups, sas, others := authorizationapi.SubjectsStrings(roleBinding.Namespace, roleBinding.Subjects)
516
+
517
+	_, err := fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t%v\n", roleBinding.Name, roleBinding.RoleRef.Namespace+"/"+roleBinding.RoleRef.Name, strings.Join(users, ", "), strings.Join(groups, ", "), strings.Join(sas, ", "), strings.Join(others, ", "))
516 518
 	return err
517 519
 }
518 520
 
... ...
@@ -518,7 +518,7 @@ func GetBootstrapOpenshiftRoleBindings(openshiftNamespace string) []authorizatio
518 518
 				Name:      OpenshiftSharedResourceViewRoleName,
519 519
 				Namespace: openshiftNamespace,
520 520
 			},
521
-			Groups: util.NewStringSet(AuthenticatedGroup),
521
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}},
522 522
 		},
523 523
 	}
524 524
 }
... ...
@@ -532,7 +532,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
532 532
 			RoleRef: kapi.ObjectReference{
533 533
 				Name: MasterRoleName,
534 534
 			},
535
-			Groups: util.NewStringSet(MastersGroup),
535
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: MastersGroup}},
536 536
 		},
537 537
 		{
538 538
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -541,7 +541,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
541 541
 			RoleRef: kapi.ObjectReference{
542 542
 				Name: ClusterAdminRoleName,
543 543
 			},
544
-			Groups: util.NewStringSet(ClusterAdminGroup),
544
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: ClusterAdminGroup}},
545 545
 		},
546 546
 		{
547 547
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -550,7 +550,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
550 550
 			RoleRef: kapi.ObjectReference{
551 551
 				Name: ClusterReaderRoleName,
552 552
 			},
553
-			Groups: util.NewStringSet(ClusterReaderGroup),
553
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: ClusterReaderGroup}},
554 554
 		},
555 555
 		{
556 556
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -559,7 +559,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
559 559
 			RoleRef: kapi.ObjectReference{
560 560
 				Name: BasicUserRoleName,
561 561
 			},
562
-			Groups: util.NewStringSet(AuthenticatedGroup),
562
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}},
563 563
 		},
564 564
 		{
565 565
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -568,7 +568,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
568 568
 			RoleRef: kapi.ObjectReference{
569 569
 				Name: SelfProvisionerRoleName,
570 570
 			},
571
-			Groups: util.NewStringSet(AuthenticatedGroup),
571
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}},
572 572
 		},
573 573
 		{
574 574
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -577,7 +577,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
577 577
 			RoleRef: kapi.ObjectReference{
578 578
 				Name: OAuthTokenDeleterRoleName,
579 579
 			},
580
-			Groups: util.NewStringSet(AuthenticatedGroup, UnauthenticatedGroup),
580
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}, {Kind: authorizationapi.SystemGroupKind, Name: UnauthenticatedGroup}},
581 581
 		},
582 582
 		{
583 583
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -586,7 +586,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
586 586
 			RoleRef: kapi.ObjectReference{
587 587
 				Name: StatusCheckerRoleName,
588 588
 			},
589
-			Groups: util.NewStringSet(AuthenticatedGroup, UnauthenticatedGroup),
589
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}, {Kind: authorizationapi.SystemGroupKind, Name: UnauthenticatedGroup}},
590 590
 		},
591 591
 		{
592 592
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -595,7 +595,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
595 595
 			RoleRef: kapi.ObjectReference{
596 596
 				Name: RouterRoleName,
597 597
 			},
598
-			Groups: util.NewStringSet(RouterGroup),
598
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: RouterGroup}},
599 599
 		},
600 600
 		{
601 601
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -604,7 +604,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
604 604
 			RoleRef: kapi.ObjectReference{
605 605
 				Name: RegistryRoleName,
606 606
 			},
607
-			Groups: util.NewStringSet(RegistryGroup),
607
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: RegistryGroup}},
608 608
 		},
609 609
 		{
610 610
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -613,7 +613,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
613 613
 			RoleRef: kapi.ObjectReference{
614 614
 				Name: NodeRoleName,
615 615
 			},
616
-			Groups: util.NewStringSet(NodesGroup),
616
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: NodesGroup}},
617 617
 		},
618 618
 		{
619 619
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -623,7 +623,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
623 623
 				Name: NodeProxierRoleName,
624 624
 			},
625 625
 			// Allow node identities to run node proxies
626
-			Groups: util.NewStringSet(NodesGroup),
626
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: NodesGroup}},
627 627
 		},
628 628
 		{
629 629
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -633,7 +633,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
633 633
 				Name: SDNReaderRoleName,
634 634
 			},
635 635
 			// Allow node identities to run SDN plugins
636
-			Groups: util.NewStringSet(NodesGroup),
636
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: NodesGroup}},
637 637
 		},
638 638
 		{
639 639
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -642,7 +642,7 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
642 642
 			RoleRef: kapi.ObjectReference{
643 643
 				Name: WebHooksRoleName,
644 644
 			},
645
-			Groups: util.NewStringSet(AuthenticatedGroup, UnauthenticatedGroup),
645
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup}, {Kind: authorizationapi.SystemGroupKind, Name: UnauthenticatedGroup}},
646 646
 		},
647 647
 	}
648 648
 }
... ...
@@ -3,7 +3,6 @@ package bootstrappolicy
3 3
 import (
4 4
 	kapi "k8s.io/kubernetes/pkg/api"
5 5
 	"k8s.io/kubernetes/pkg/controller/serviceaccount"
6
-	"k8s.io/kubernetes/pkg/util"
7 6
 
8 7
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
9 8
 )
... ...
@@ -18,7 +17,7 @@ func GetBootstrapServiceAccountProjectRoleBindings(namespace string) []authoriza
18 18
 			RoleRef: kapi.ObjectReference{
19 19
 				Name: ImagePullerRoleName,
20 20
 			},
21
-			Groups: util.NewStringSet(serviceaccount.MakeNamespaceGroupName(namespace)),
21
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.SystemGroupKind, Name: serviceaccount.MakeNamespaceGroupName(namespace)}},
22 22
 		},
23 23
 		{
24 24
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -28,7 +27,7 @@ func GetBootstrapServiceAccountProjectRoleBindings(namespace string) []authoriza
28 28
 			RoleRef: kapi.ObjectReference{
29 29
 				Name: ImageBuilderRoleName,
30 30
 			},
31
-			Users: util.NewStringSet(serviceaccount.MakeUsername(namespace, BuilderServiceAccountName)),
31
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.ServiceAccountKind, Name: BuilderServiceAccountName}},
32 32
 		},
33 33
 		{
34 34
 			ObjectMeta: kapi.ObjectMeta{
... ...
@@ -38,7 +37,7 @@ func GetBootstrapServiceAccountProjectRoleBindings(namespace string) []authoriza
38 38
 			RoleRef: kapi.ObjectReference{
39 39
 				Name: DeployerRoleName,
40 40
 			},
41
-			Users: util.NewStringSet(serviceaccount.MakeUsername(namespace, DeployerServiceAccountName)),
41
+			Subjects: []kapi.ObjectReference{{Kind: authorizationapi.ServiceAccountKind, Name: DeployerServiceAccountName}},
42 42
 		},
43 43
 	}
44 44
 }
... ...
@@ -56,22 +56,22 @@ func (c *MasterConfig) ensureOpenShiftInfraNamespace() {
56 56
 	}
57 57
 
58 58
 	// Ensure service account cluster role bindings exist
59
-	clusterRolesToUsernames := map[string][]string{
60
-		bootstrappolicy.BuildControllerRoleName:       {serviceaccount.MakeUsername(ns, c.BuildControllerServiceAccount)},
61
-		bootstrappolicy.DeploymentControllerRoleName:  {serviceaccount.MakeUsername(ns, c.DeploymentControllerServiceAccount)},
62
-		bootstrappolicy.ReplicationControllerRoleName: {serviceaccount.MakeUsername(ns, c.ReplicationControllerServiceAccount)},
59
+	clusterRolesToSubjects := map[string][]kapi.ObjectReference{
60
+		bootstrappolicy.BuildControllerRoleName:       {{Namespace: ns, Name: c.BuildControllerServiceAccount, Kind: "ServiceAccount"}},
61
+		bootstrappolicy.DeploymentControllerRoleName:  {{Namespace: ns, Name: c.DeploymentControllerServiceAccount, Kind: "ServiceAccount"}},
62
+		bootstrappolicy.ReplicationControllerRoleName: {{Namespace: ns, Name: c.ReplicationControllerServiceAccount, Kind: "ServiceAccount"}},
63 63
 	}
64 64
 	roleAccessor := policy.NewClusterRoleBindingAccessor(c.ServiceAccountRoleBindingClient())
65
-	for clusterRole, usernames := range clusterRolesToUsernames {
65
+	for clusterRole, subjects := range clusterRolesToSubjects {
66 66
 		addRole := &policy.RoleModificationOptions{
67 67
 			RoleName:            clusterRole,
68 68
 			RoleBindingAccessor: roleAccessor,
69
-			Users:               usernames,
69
+			Subjects:            subjects,
70 70
 		}
71 71
 		if err := addRole.AddRole(); err != nil {
72
-			glog.Errorf("Could not add %v users to the %v cluster role: %v\n", usernames, clusterRole, err)
72
+			glog.Errorf("Could not add %v subjects to the %v cluster role: %v\n", subjects, clusterRole, err)
73 73
 		} else {
74
-			glog.V(2).Infof("Added %v users to the %v cluster role: %v\n", usernames, clusterRole, err)
74
+			glog.V(2).Infof("Added %v subjects to the %v cluster role: %v\n", subjects, clusterRole, err)
75 75
 		}
76 76
 	}
77 77
 }
... ...
@@ -128,8 +128,7 @@ func (c *MasterConfig) ensureDefaultNamespaceServiceAccountRoles() {
128 128
 			RoleName:            binding.RoleRef.Name,
129 129
 			RoleNamespace:       binding.RoleRef.Namespace,
130 130
 			RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(kapi.NamespaceDefault, c.ServiceAccountRoleBindingClient()),
131
-			Users:               binding.Users.List(),
132
-			Groups:              binding.Groups.List(),
131
+			Subjects:            binding.Subjects,
133 132
 		}
134 133
 		if err := addRole.AddRole(); err != nil {
135 134
 			glog.Errorf("Could not add service accounts to the %v role in the %v namespace: %v\n", binding.RoleRef.Name, kapi.NamespaceDefault, err)
... ...
@@ -1,7 +1,7 @@
1 1
 package delegated
2 2
 
3 3
 import (
4
-	"k8s.io/kubernetes/pkg/util"
4
+	kapi "k8s.io/kubernetes/pkg/api"
5 5
 
6 6
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
7 7
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
... ...
@@ -39,7 +39,7 @@ func DefaultTemplate() *templateapi.Template {
39 39
 	binding := &authorizationapi.RoleBinding{}
40 40
 	binding.Name = "admins"
41 41
 	binding.Namespace = ns
42
-	binding.Users = util.NewStringSet("${" + ProjectAdminUserParam + "}")
42
+	binding.Subjects = []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "${" + ProjectAdminUserParam + "}"}}
43 43
 	binding.RoleRef.Name = bootstrappolicy.AdminRoleName
44 44
 	ret.Objects = append(ret.Objects, binding)
45 45
 
... ...
@@ -8,17 +8,20 @@ set -o pipefail
8 8
 
9 9
 oc policy add-role-to-group cluster-admin system:unauthenticated
10 10
 oc policy add-role-to-user cluster-admin system:no-user
11
+oc get rolebinding/cluster-admin --no-headers
11 12
 oc get rolebinding/cluster-admin --no-headers | grep -q "system:no-user"
12 13
 
13 14
 oc policy add-role-to-user cluster-admin -z=one,two --serviceaccount=three,four
14
-oc get rolebinding/cluster-admin --no-headers | grep -q "system:serviceaccount:cmd-policy:one"
15
-oc get rolebinding/cluster-admin --no-headers | grep -q "system:serviceaccount:cmd-policy:four"
15
+oc get rolebinding/cluster-admin --no-headers
16
+oc get rolebinding/cluster-admin --no-headers | grep -q "one"
17
+oc get rolebinding/cluster-admin --no-headers | grep -q "four"
16 18
 
17 19
 oc policy remove-role-from-group cluster-admin system:unauthenticated
18 20
 
19 21
 oc policy remove-role-from-user cluster-admin system:no-user
20 22
 oc policy remove-role-from-user cluster-admin -z=one,two --serviceaccount=three,four
21
-[ ! "$(oc get rolebinding/cluster-admin --no-headers | grep -q "system:serviceaccount:cmd-policy:four")" ]
23
+oc get rolebinding/cluster-admin --no-headers
24
+[ ! "$(oc get rolebinding/cluster-admin --no-headers | grep -q "four")" ]
22 25
 
23 26
 oc policy remove-group system:unauthenticated
24 27
 oc policy remove-user system:no-user
... ...
@@ -6,12 +6,11 @@ import (
6 6
 	"reflect"
7 7
 	"testing"
8 8
 
9
-	"k8s.io/kubernetes/pkg/util"
10
-
11 9
 	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
12 10
 	groupscmd "github.com/openshift/origin/pkg/cmd/admin/groups"
13 11
 	projectapi "github.com/openshift/origin/pkg/project/api"
14 12
 	userapi "github.com/openshift/origin/pkg/user/api"
13
+	uservalidation "github.com/openshift/origin/pkg/user/api/validation"
15 14
 	testutil "github.com/openshift/origin/test/util"
16 15
 )
17 16
 
... ...
@@ -85,7 +84,7 @@ func TestBasicUserBasedGroupManipulation(t *testing.T) {
85 85
 	roleBinding := &authorizationapi.RoleBinding{}
86 86
 	roleBinding.Name = "admins"
87 87
 	roleBinding.RoleRef.Name = "admin"
88
-	roleBinding.Groups = util.NewStringSet(valerieGroups...)
88
+	roleBinding.Subjects = authorizationapi.BuildSubjects([]string{}, valerieGroups, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
89 89
 	_, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding)
90 90
 	if err != nil {
91 91
 		t.Fatalf("unexpected error: %v", err)
... ...
@@ -145,7 +144,7 @@ func TestBasicGroupManipulation(t *testing.T) {
145 145
 	roleBinding := &authorizationapi.RoleBinding{}
146 146
 	roleBinding.Name = "admins"
147 147
 	roleBinding.RoleRef.Name = "admin"
148
-	roleBinding.Groups = util.NewStringSet(theGroup.Name)
148
+	roleBinding.Subjects = authorizationapi.BuildSubjects([]string{}, []string{theGroup.Name}, uservalidation.ValidateUserName, uservalidation.ValidateGroupName)
149 149
 	_, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding)
150 150
 	if err != nil {
151 151
 		t.Fatalf("unexpected error: %v", err)
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	testutil "github.com/openshift/origin/test/util"
10 10
 
11
+	authorizationinterfaces "github.com/openshift/origin/pkg/authorization/interfaces"
11 12
 	policy "github.com/openshift/origin/pkg/cmd/admin/policy"
12 13
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
13 14
 )
... ...
@@ -50,11 +51,12 @@ func TestPolicyCommands(t *testing.T) {
50 50
 	if err != nil {
51 51
 		t.Fatalf("unexpected error: %v", err)
52 52
 	}
53
-	if !viewers.Users.Has("valerie") {
54
-		t.Errorf("expected valerie in users: %v", viewers.Users)
53
+	binding := authorizationinterfaces.NewLocalRoleBindingAdapter(viewers)
54
+	if !binding.Users().Has("valerie") {
55
+		t.Errorf("expected valerie in users: %v", binding.Users())
55 56
 	}
56
-	if !viewers.Groups.Has("my-group") {
57
-		t.Errorf("expected my-group in groups: %v", viewers.Groups)
57
+	if !binding.Groups().Has("my-group") {
58
+		t.Errorf("expected my-group in groups: %v", binding.Groups())
58 59
 	}
59 60
 
60 61
 	removeValerie := policy.RemoveFromProjectOptions{
... ...
@@ -71,11 +73,12 @@ func TestPolicyCommands(t *testing.T) {
71 71
 	if err != nil {
72 72
 		t.Fatalf("unexpected error: %v", err)
73 73
 	}
74
-	if viewers.Users.Has("valerie") {
75
-		t.Errorf("unexpected valerie in users: %v", viewers.Users)
74
+	binding = authorizationinterfaces.NewLocalRoleBindingAdapter(viewers)
75
+	if binding.Users().Has("valerie") {
76
+		t.Errorf("unexpected valerie in users: %v", binding.Users())
76 77
 	}
77
-	if !viewers.Groups.Has("my-group") {
78
-		t.Errorf("expected my-group in groups: %v", viewers.Groups)
78
+	if !binding.Groups().Has("my-group") {
79
+		t.Errorf("expected my-group in groups: %v", binding.Groups())
79 80
 	}
80 81
 
81 82
 	removeMyGroup := policy.RemoveFromProjectOptions{
... ...
@@ -92,11 +95,12 @@ func TestPolicyCommands(t *testing.T) {
92 92
 	if err != nil {
93 93
 		t.Fatalf("unexpected error: %v", err)
94 94
 	}
95
-	if viewers.Users.Has("valerie") {
96
-		t.Errorf("unexpected valerie in users: %v", viewers.Users)
95
+	binding = authorizationinterfaces.NewLocalRoleBindingAdapter(viewers)
96
+	if binding.Users().Has("valerie") {
97
+		t.Errorf("unexpected valerie in users: %v", binding.Users())
97 98
 	}
98
-	if viewers.Groups.Has("my-group") {
99
-		t.Errorf("unexpected my-group in groups: %v", viewers.Groups)
99
+	if binding.Groups().Has("my-group") {
100
+		t.Errorf("unexpected my-group in groups: %v", binding.Groups())
100 101
 	}
101 102
 
102 103
 }