mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Switch RBAC subject apiVersion to apiGroup in v1beta1
This commit is contained in:
		| @@ -9,6 +9,6 @@ roleRef: | |||||||
|   kind: ClusterRole |   kind: ClusterRole | ||||||
|   name: node-proxy |   name: node-proxy | ||||||
| subjects: | subjects: | ||||||
| - apiVersion: rbac/v1beta1 | - apiGroup: rbac.authorization.k8s.io | ||||||
|   kind: User |   kind: User | ||||||
|   name: kube-apiserver |   name: kube-apiserver | ||||||
|   | |||||||
| @@ -719,7 +719,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na | |||||||
| 		Subjects: []rbacv1beta1.Subject{ | 		Subjects: []rbacv1beta1.Subject{ | ||||||
| 			{ | 			{ | ||||||
| 				Kind:      "ServiceAccount", | 				Kind:      "ServiceAccount", | ||||||
| 				APIVersion: "", | 				APIGroup:  "", | ||||||
| 				Name:      "federation-controller-manager", | 				Name:      "federation-controller-manager", | ||||||
| 				Namespace: "federation-system", | 				Namespace: "federation-system", | ||||||
| 			}, | 			}, | ||||||
|   | |||||||
| @@ -590,6 +590,23 @@ func rbacFuncs(t apitesting.TestingCommon) []interface{} { | |||||||
| 				r.APIGroup = rbac.GroupName | 				r.APIGroup = rbac.GroupName | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  | 		func(r *rbac.Subject, c fuzz.Continue) { | ||||||
|  | 			switch c.Int31n(3) { | ||||||
|  | 			case 0: | ||||||
|  | 				r.Kind = rbac.ServiceAccountKind | ||||||
|  | 				r.APIGroup = "" | ||||||
|  | 				c.FuzzNoCustom(&r.Name) | ||||||
|  | 				c.FuzzNoCustom(&r.Namespace) | ||||||
|  | 			case 1: | ||||||
|  | 				r.Kind = rbac.UserKind | ||||||
|  | 				r.APIGroup = rbac.GroupName | ||||||
|  | 				c.FuzzNoCustom(&r.Name) | ||||||
|  | 			case 2: | ||||||
|  | 				r.Kind = rbac.GroupKind | ||||||
|  | 				r.APIGroup = rbac.GroupName | ||||||
|  | 				c.FuzzNoCustom(&r.Name) | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -220,14 +220,14 @@ func NewClusterBinding(clusterRoleName string) *ClusterRoleBindingBuilder { | |||||||
|  |  | ||||||
| func (r *ClusterRoleBindingBuilder) Groups(groups ...string) *ClusterRoleBindingBuilder { | func (r *ClusterRoleBindingBuilder) Groups(groups ...string) *ClusterRoleBindingBuilder { | ||||||
| 	for _, group := range groups { | 	for _, group := range groups { | ||||||
| 		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: GroupKind, Name: group}) | 		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: GroupKind, APIGroup: GroupName, Name: group}) | ||||||
| 	} | 	} | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *ClusterRoleBindingBuilder) Users(users ...string) *ClusterRoleBindingBuilder { | func (r *ClusterRoleBindingBuilder) Users(users ...string) *ClusterRoleBindingBuilder { | ||||||
| 	for _, user := range users { | 	for _, user := range users { | ||||||
| 		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: UserKind, Name: user}) | 		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: UserKind, APIGroup: GroupName, Name: user}) | ||||||
| 	} | 	} | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|   | |||||||
| @@ -63,9 +63,10 @@ type Subject struct { | |||||||
| 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | ||||||
| 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | ||||||
| 	Kind string | 	Kind string | ||||||
| 	// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is | 	// APIGroup holds the API group of the referenced subject. | ||||||
| 	// expected to be API version of this API group. For example, "rbac/v1alpha1". | 	// Defaults to "" for ServiceAccount subjects. | ||||||
| 	APIVersion string | 	// Defaults to "rbac.authorization.k8s.io" for User and Group subjects. | ||||||
|  | 	APIGroup string | ||||||
| 	// Name of the object being referenced. | 	// Name of the object being referenced. | ||||||
| 	Name string | 	Name string | ||||||
| 	// Namespace of the referenced object.  If the object kind is non-namespace, such as "User" or "Group", and this value is not empty | 	// Namespace of the referenced object.  If the object kind is non-namespace, such as "User" or "Group", and this value is not empty | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ package v1alpha1 | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"k8s.io/apimachinery/pkg/conversion" | 	"k8s.io/apimachinery/pkg/conversion" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
| 	api "k8s.io/kubernetes/pkg/apis/rbac" | 	api "k8s.io/kubernetes/pkg/apis/rbac" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -30,13 +31,51 @@ func Convert_v1alpha1_Subject_To_rbac_Subject(in *Subject, out *api.Subject, s c | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// specifically set the APIGroup for the three subjects recognized in v1alpha1 | ||||||
|  | 	switch { | ||||||
|  | 	case in.Kind == ServiceAccountKind: | ||||||
|  | 		out.APIGroup = "" | ||||||
|  | 	case in.Kind == UserKind: | ||||||
|  | 		out.APIGroup = GroupName | ||||||
|  | 	case in.Kind == GroupKind: | ||||||
|  | 		out.APIGroup = GroupName | ||||||
|  | 	default: | ||||||
|  | 		// For unrecognized kinds, use the group portion of the APIVersion if we can get it | ||||||
|  | 		if gv, err := schema.ParseGroupVersion(in.APIVersion); err == nil { | ||||||
|  | 			out.APIGroup = gv.Group | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// User * in v1alpha1 will only match all authenticated users | 	// User * in v1alpha1 will only match all authenticated users | ||||||
| 	// This is only for compatibility with old RBAC bindings | 	// This is only for compatibility with old RBAC bindings | ||||||
| 	// Special treatment for * should not be included in v1beta1 | 	// Special treatment for * should not be included in v1beta1 | ||||||
| 	if out.Kind == UserKind && out.Name == "*" { | 	if out.Kind == UserKind && out.APIGroup == GroupName && out.Name == "*" { | ||||||
| 		out.Kind = GroupKind | 		out.Kind = GroupKind | ||||||
| 		out.Name = allAuthenticated | 		out.Name = allAuthenticated | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func Convert_rbac_Subject_To_v1alpha1_Subject(in *api.Subject, out *Subject, s conversion.Scope) error { | ||||||
|  | 	if err := autoConvert_rbac_Subject_To_v1alpha1_Subject(in, out, s); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	case in.Kind == ServiceAccountKind && in.APIGroup == "": | ||||||
|  | 		// Make service accounts v1 | ||||||
|  | 		out.APIVersion = "v1" | ||||||
|  | 	case in.Kind == UserKind && in.APIGroup == GroupName: | ||||||
|  | 		// users in the rbac API group get v1alpha | ||||||
|  | 		out.APIVersion = SchemeGroupVersion.String() | ||||||
|  | 	case in.Kind == GroupKind && in.APIGroup == GroupName: | ||||||
|  | 		// groups in the rbac API group get v1alpha | ||||||
|  | 		out.APIVersion = SchemeGroupVersion.String() | ||||||
|  | 	default: | ||||||
|  | 		// otherwise, they get an unspecified version of a group | ||||||
|  | 		out.APIVersion = schema.GroupVersion{Group: in.APIGroup}.String() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -34,21 +34,63 @@ func TestConversion(t *testing.T) { | |||||||
| 		"specific user": { | 		"specific user": { | ||||||
| 			old: &v1alpha1.RoleBinding{ | 			old: &v1alpha1.RoleBinding{ | ||||||
| 				RoleRef:  v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | 				RoleRef:  v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
| 				Subjects: []v1alpha1.Subject{{Kind: "User", Name: "bob"}}, | 				Subjects: []v1alpha1.Subject{{Kind: "User", APIVersion: v1alpha1.SchemeGroupVersion.String(), Name: "bob"}}, | ||||||
| 			}, | 			}, | ||||||
| 			expected: &rbacapi.RoleBinding{ | 			expected: &rbacapi.RoleBinding{ | ||||||
| 				RoleRef:  rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | 				RoleRef:  rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
| 				Subjects: []rbacapi.Subject{{Kind: "User", Name: "bob"}}, | 				Subjects: []rbacapi.Subject{{Kind: "User", APIGroup: v1alpha1.GroupName, Name: "bob"}}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		"wildcard user matches authenticated": { | 		"wildcard user matches authenticated": { | ||||||
| 			old: &v1alpha1.RoleBinding{ | 			old: &v1alpha1.RoleBinding{ | ||||||
| 				RoleRef:  v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | 				RoleRef:  v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
| 				Subjects: []v1alpha1.Subject{{Kind: "User", Name: "*"}}, | 				Subjects: []v1alpha1.Subject{{Kind: "User", APIVersion: v1alpha1.SchemeGroupVersion.String(), Name: "*"}}, | ||||||
| 			}, | 			}, | ||||||
| 			expected: &rbacapi.RoleBinding{ | 			expected: &rbacapi.RoleBinding{ | ||||||
| 				RoleRef:  rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | 				RoleRef:  rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
| 				Subjects: []rbacapi.Subject{{Kind: "Group", Name: "system:authenticated"}}, | 				Subjects: []rbacapi.Subject{{Kind: "Group", APIGroup: v1alpha1.GroupName, Name: "system:authenticated"}}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"missing api group gets defaulted": { | ||||||
|  | 			old: &v1alpha1.RoleBinding{ | ||||||
|  | 				RoleRef: v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
|  | 				Subjects: []v1alpha1.Subject{ | ||||||
|  | 					{Kind: "User", Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &rbacapi.RoleBinding{ | ||||||
|  | 				RoleRef: rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
|  | 				Subjects: []rbacapi.Subject{ | ||||||
|  | 					{Kind: "User", APIGroup: v1alpha1.GroupName, Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", APIGroup: v1alpha1.GroupName, Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", APIGroup: "", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"bad api group gets defaulted": { | ||||||
|  | 			old: &v1alpha1.RoleBinding{ | ||||||
|  | 				RoleRef: v1alpha1.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
|  | 				Subjects: []v1alpha1.Subject{ | ||||||
|  | 					{Kind: "User", APIVersion: "rbac", Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", APIVersion: "rbac", Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", APIVersion: "rbac", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 					{Kind: "User", APIVersion: "rbac/v8", Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", APIVersion: "rbac/v8", Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", APIVersion: "rbac/v8", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &rbacapi.RoleBinding{ | ||||||
|  | 				RoleRef: rbacapi.RoleRef{Name: "foo", APIGroup: v1alpha1.GroupName}, | ||||||
|  | 				Subjects: []rbacapi.Subject{ | ||||||
|  | 					{Kind: "User", APIGroup: v1alpha1.GroupName, Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", APIGroup: v1alpha1.GroupName, Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", APIGroup: "", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 					{Kind: "User", APIGroup: v1alpha1.GroupName, Name: "myuser"}, | ||||||
|  | 					{Kind: "Group", APIGroup: v1alpha1.GroupName, Name: "mygroup"}, | ||||||
|  | 					{Kind: "ServiceAccount", APIGroup: "", Name: "mysa", Namespace: "myns"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { | |||||||
| 	return scheme.AddDefaultingFuncs( | 	return scheme.AddDefaultingFuncs( | ||||||
| 		SetDefaults_ClusterRoleBinding, | 		SetDefaults_ClusterRoleBinding, | ||||||
| 		SetDefaults_RoleBinding, | 		SetDefaults_RoleBinding, | ||||||
|  | 		SetDefaults_Subject, | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -38,3 +39,15 @@ func SetDefaults_RoleBinding(obj *RoleBinding) { | |||||||
| 		obj.RoleRef.APIGroup = GroupName | 		obj.RoleRef.APIGroup = GroupName | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | func SetDefaults_Subject(obj *Subject) { | ||||||
|  | 	if len(obj.APIVersion) == 0 { | ||||||
|  | 		switch obj.Kind { | ||||||
|  | 		case ServiceAccountKind: | ||||||
|  | 			obj.APIVersion = "v1" | ||||||
|  | 		case UserKind: | ||||||
|  | 			obj.APIVersion = SchemeGroupVersion.String() | ||||||
|  | 		case GroupKind: | ||||||
|  | 			obj.APIVersion = SchemeGroupVersion.String() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -72,7 +72,10 @@ type Subject struct { | |||||||
| 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | ||||||
| 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | ||||||
| 	Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` | 	Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` | ||||||
| 	// APIVersion holds the API group and version of the referenced object. | 	// APIVersion holds the API group and version of the referenced subject. | ||||||
|  | 	// Defaults to "v1" for ServiceAccount subjects. | ||||||
|  | 	// Defaults to "rbac.authorization.k8s.io/v1alpha1" for User and Group subjects. | ||||||
|  | 	// +k8s:conversion-gen=false | ||||||
| 	// +optional | 	// +optional | ||||||
| 	APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt.name=apiVersion"` | 	APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt.name=apiVersion"` | ||||||
| 	// Name of the object being referenced. | 	// Name of the object being referenced. | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { | |||||||
| 	return scheme.AddDefaultingFuncs( | 	return scheme.AddDefaultingFuncs( | ||||||
| 		SetDefaults_ClusterRoleBinding, | 		SetDefaults_ClusterRoleBinding, | ||||||
| 		SetDefaults_RoleBinding, | 		SetDefaults_RoleBinding, | ||||||
|  | 		SetDefaults_Subject, | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -38,3 +39,15 @@ func SetDefaults_RoleBinding(obj *RoleBinding) { | |||||||
| 		obj.RoleRef.APIGroup = GroupName | 		obj.RoleRef.APIGroup = GroupName | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | func SetDefaults_Subject(obj *Subject) { | ||||||
|  | 	if len(obj.APIGroup) == 0 { | ||||||
|  | 		switch obj.Kind { | ||||||
|  | 		case ServiceAccountKind: | ||||||
|  | 			obj.APIGroup = "" | ||||||
|  | 		case UserKind: | ||||||
|  | 			obj.APIGroup = GroupName | ||||||
|  | 		case GroupKind: | ||||||
|  | 			obj.APIGroup = GroupName | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -71,9 +71,11 @@ type Subject struct { | |||||||
| 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | 	// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". | ||||||
| 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | 	// If the Authorizer does not recognized the kind value, the Authorizer should report an error. | ||||||
| 	Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` | 	Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` | ||||||
| 	// APIVersion holds the API group and version of the referenced object. | 	// APIGroup holds the API group of the referenced subject. | ||||||
|  | 	// Defaults to "" for ServiceAccount subjects. | ||||||
|  | 	// Defaults to "rbac.authorization.k8s.io" for User and Group subjects. | ||||||
| 	// +optional | 	// +optional | ||||||
| 	APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt.name=apiVersion"` | 	APIGroup string `json:"apiGroup,omitempty" protobuf:"bytes,2,opt.name=apiGroup"` | ||||||
| 	// Name of the object being referenced. | 	// Name of the object being referenced. | ||||||
| 	Name string `json:"name" protobuf:"bytes,3,opt,name=name"` | 	Name string `json:"name" protobuf:"bytes,3,opt,name=name"` | ||||||
| 	// Namespace of the referenced object.  If the object kind is non-namespace, such as "User" or "Group", and this value is not empty | 	// Namespace of the referenced object.  If the object kind is non-namespace, such as "User" or "Group", and this value is not empty | ||||||
|   | |||||||
| @@ -201,6 +201,9 @@ func validateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath | |||||||
| 				allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg)) | 				allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg)) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		if len(subject.APIGroup) > 0 { | ||||||
|  | 			allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{""})) | ||||||
|  | 		} | ||||||
| 		if !isNamespaced && len(subject.Namespace) == 0 { | 		if !isNamespaced && len(subject.Namespace) == 0 { | ||||||
| 			allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "")) | 			allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "")) | ||||||
| 		} | 		} | ||||||
| @@ -210,12 +213,18 @@ func validateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath | |||||||
| 		if len(subject.Name) == 0 { | 		if len(subject.Name) == 0 { | ||||||
| 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "user name cannot be empty")) | 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "user name cannot be empty")) | ||||||
| 		} | 		} | ||||||
|  | 		if subject.APIGroup != rbac.GroupName { | ||||||
|  | 			allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName})) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	case rbac.GroupKind: | 	case rbac.GroupKind: | ||||||
| 		// TODO(ericchiang): What other restrictions on group name are there? | 		// TODO(ericchiang): What other restrictions on group name are there? | ||||||
| 		if len(subject.Name) == 0 { | 		if len(subject.Name) == 0 { | ||||||
| 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "group name cannot be empty")) | 			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, "group name cannot be empty")) | ||||||
| 		} | 		} | ||||||
|  | 		if subject.APIGroup != rbac.GroupName { | ||||||
|  | 			allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{rbac.GroupName})) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| 		allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{rbac.ServiceAccountKind, rbac.UserKind, rbac.GroupKind})) | 		allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, []string{rbac.ServiceAccountKind, rbac.UserKind, rbac.GroupKind})) | ||||||
|   | |||||||
| @@ -30,9 +30,9 @@ func TestValidateClusterRoleBinding(t *testing.T) { | |||||||
| 			ObjectMeta: metav1.ObjectMeta{Name: "master"}, | 			ObjectMeta: metav1.ObjectMeta{Name: "master"}, | ||||||
| 			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, | 			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"}, | ||||||
| 			Subjects: []rbac.Subject{ | 			Subjects: []rbac.Subject{ | ||||||
| 				{Name: "validsaname", Namespace: "foo", Kind: rbac.ServiceAccountKind}, | 				{Name: "validsaname", APIGroup: "", Namespace: "foo", Kind: rbac.ServiceAccountKind}, | ||||||
| 				{Name: "valid@username", Kind: rbac.UserKind}, | 				{Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind}, | ||||||
| 				{Name: "valid@groupname", Kind: rbac.GroupKind}, | 				{Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	) | 	) | ||||||
| @@ -145,9 +145,9 @@ func TestValidateRoleBinding(t *testing.T) { | |||||||
| 			ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, | 			ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "master"}, | ||||||
| 			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, | 			RoleRef:    rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"}, | ||||||
| 			Subjects: []rbac.Subject{ | 			Subjects: []rbac.Subject{ | ||||||
| 				{Name: "validsaname", Kind: rbac.ServiceAccountKind}, | 				{Name: "validsaname", APIGroup: "", Kind: rbac.ServiceAccountKind}, | ||||||
| 				{Name: "valid@username", Kind: rbac.UserKind}, | 				{Name: "valid@username", APIGroup: rbac.GroupName, Kind: rbac.UserKind}, | ||||||
| 				{Name: "valid@groupname", Kind: rbac.GroupKind}, | 				{Name: "valid@groupname", APIGroup: rbac.GroupName, Kind: rbac.GroupKind}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	) | 	) | ||||||
|   | |||||||
| @@ -118,14 +118,14 @@ func (s ClusterRoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, err | |||||||
| 	for _, user := range s.Users { | 	for _, user := range s.Users { | ||||||
| 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:     rbac.UserKind, | 			Kind:     rbac.UserKind, | ||||||
| 			APIVersion: "rbac.authorization.k8s.io/v1beta1", | 			APIGroup: rbac.GroupName, | ||||||
| 			Name:     user, | 			Name:     user, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 	for _, group := range s.Groups { | 	for _, group := range s.Groups { | ||||||
| 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:     rbac.GroupKind, | 			Kind:     rbac.GroupKind, | ||||||
| 			APIVersion: "rbac.authorization.k8s.io/v1beta1", | 			APIGroup: rbac.GroupName, | ||||||
| 			Name:     group, | 			Name:     group, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @@ -136,6 +136,7 @@ func (s ClusterRoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, err | |||||||
| 		} | 		} | ||||||
| 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | 		clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:      rbac.ServiceAccountKind, | 			Kind:      rbac.ServiceAccountKind, | ||||||
|  | 			APIGroup:  "", | ||||||
| 			Namespace: tokens[0], | 			Namespace: tokens[0], | ||||||
| 			Name:      tokens[1], | 			Name:      tokens[1], | ||||||
| 		}) | 		}) | ||||||
|   | |||||||
| @@ -133,14 +133,14 @@ func (s RoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, error) { | |||||||
| 	for _, user := range s.Users { | 	for _, user := range s.Users { | ||||||
| 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:     rbac.UserKind, | 			Kind:     rbac.UserKind, | ||||||
| 			APIVersion: "rbac.authorization.k8s.io/v1beta1", | 			APIGroup: rbac.GroupName, | ||||||
| 			Name:     user, | 			Name:     user, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 	for _, group := range s.Groups { | 	for _, group := range s.Groups { | ||||||
| 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:     rbac.GroupKind, | 			Kind:     rbac.GroupKind, | ||||||
| 			APIVersion: "rbac.authorization.k8s.io/v1beta1", | 			APIGroup: rbac.GroupName, | ||||||
| 			Name:     group, | 			Name:     group, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @@ -151,6 +151,7 @@ func (s RoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, error) { | |||||||
| 		} | 		} | ||||||
| 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | 		roleBinding.Subjects = append(roleBinding.Subjects, rbac.Subject{ | ||||||
| 			Kind:      rbac.ServiceAccountKind, | 			Kind:      rbac.ServiceAccountKind, | ||||||
|  | 			APIGroup:  "", | ||||||
| 			Namespace: tokens[0], | 			Namespace: tokens[0], | ||||||
| 			Name:      tokens[1], | 			Name:      tokens[1], | ||||||
| 		}) | 		}) | ||||||
|   | |||||||
| @@ -12,7 +12,8 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: cluster-admin |     name: cluster-admin | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:masters |     name: system:masters | ||||||
| - apiVersion: rbac.authorization.k8s.io/v1beta1 | - apiVersion: rbac.authorization.k8s.io/v1beta1 | ||||||
|   kind: ClusterRoleBinding |   kind: ClusterRoleBinding | ||||||
| @@ -26,9 +27,11 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: system:basic-user |     name: system:basic-user | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:authenticated |     name: system:authenticated | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:unauthenticated |     name: system:unauthenticated | ||||||
| - apiVersion: rbac.authorization.k8s.io/v1beta1 | - apiVersion: rbac.authorization.k8s.io/v1beta1 | ||||||
|   kind: ClusterRoleBinding |   kind: ClusterRoleBinding | ||||||
| @@ -42,9 +45,11 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: system:discovery |     name: system:discovery | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:authenticated |     name: system:authenticated | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:unauthenticated |     name: system:unauthenticated | ||||||
| - apiVersion: rbac.authorization.k8s.io/v1beta1 | - apiVersion: rbac.authorization.k8s.io/v1beta1 | ||||||
|   kind: ClusterRoleBinding |   kind: ClusterRoleBinding | ||||||
| @@ -58,7 +63,8 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: system:kube-controller-manager |     name: system:kube-controller-manager | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: User |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: User | ||||||
|     name: system:kube-controller-manager |     name: system:kube-controller-manager | ||||||
| - apiVersion: rbac.authorization.k8s.io/v1beta1 | - apiVersion: rbac.authorization.k8s.io/v1beta1 | ||||||
|   kind: ClusterRoleBinding |   kind: ClusterRoleBinding | ||||||
| @@ -72,7 +78,8 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: system:node |     name: system:node | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: Group |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: Group | ||||||
|     name: system:nodes |     name: system:nodes | ||||||
| - apiVersion: rbac.authorization.k8s.io/v1beta1 | - apiVersion: rbac.authorization.k8s.io/v1beta1 | ||||||
|   kind: ClusterRoleBinding |   kind: ClusterRoleBinding | ||||||
| @@ -86,7 +93,8 @@ items: | |||||||
|     kind: ClusterRole |     kind: ClusterRole | ||||||
|     name: system:node-proxier |     name: system:node-proxier | ||||||
|   subjects: |   subjects: | ||||||
|   - kind: User |   - apiGroup: rbac.authorization.k8s.io | ||||||
|  |     kind: User | ||||||
|     name: system:kube-proxy |     name: system:kube-proxy | ||||||
| kind: List | kind: List | ||||||
| metadata: {} | metadata: {} | ||||||
|   | |||||||
| @@ -64,6 +64,15 @@ func newClusterRoleBinding(roleName string, subjects ...string) *rbac.ClusterRol | |||||||
| 	for i, subject := range subjects { | 	for i, subject := range subjects { | ||||||
| 		split := strings.SplitN(subject, ":", 2) | 		split := strings.SplitN(subject, ":", 2) | ||||||
| 		r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1] | 		r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1] | ||||||
|  |  | ||||||
|  | 		switch r.Subjects[i].Kind { | ||||||
|  | 		case rbac.ServiceAccountKind: | ||||||
|  | 			r.Subjects[i].APIGroup = "" | ||||||
|  | 		case rbac.UserKind, rbac.GroupKind: | ||||||
|  | 			r.Subjects[i].APIGroup = rbac.GroupName | ||||||
|  | 		default: | ||||||
|  | 			panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
| @@ -82,6 +91,15 @@ func newRoleBinding(namespace, roleName string, bindType uint16, subjects ...str | |||||||
| 	for i, subject := range subjects { | 	for i, subject := range subjects { | ||||||
| 		split := strings.SplitN(subject, ":", 2) | 		split := strings.SplitN(subject, ":", 2) | ||||||
| 		r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1] | 		r.Subjects[i].Kind, r.Subjects[i].Name = split[0], split[1] | ||||||
|  |  | ||||||
|  | 		switch r.Subjects[i].Kind { | ||||||
|  | 		case rbac.ServiceAccountKind: | ||||||
|  | 			r.Subjects[i].APIGroup = "" | ||||||
|  | 		case rbac.UserKind, rbac.GroupKind: | ||||||
|  | 			r.Subjects[i].APIGroup = rbac.GroupName | ||||||
|  | 		default: | ||||||
|  | 			panic(fmt.Errorf("invalid kind %s", r.Subjects[i].Kind)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|   | |||||||
| @@ -54,9 +54,9 @@ func NewSubjectAccessEvaluator(roles rbacregistryvalidation.RoleGetter, roleBind | |||||||
| // AllowedSubjects returns the subjects that can perform an action and any errors encountered while computing the list. | // AllowedSubjects returns the subjects that can perform an action and any errors encountered while computing the list. | ||||||
| // It is possible to have both subjects and errors returned if some rolebindings couldn't be resolved, but others could be. | // It is possible to have both subjects and errors returned if some rolebindings couldn't be resolved, but others could be. | ||||||
| func (r *SubjectAccessEvaluator) AllowedSubjects(requestAttributes authorizer.Attributes) ([]rbac.Subject, error) { | func (r *SubjectAccessEvaluator) AllowedSubjects(requestAttributes authorizer.Attributes) ([]rbac.Subject, error) { | ||||||
| 	subjects := []rbac.Subject{{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}} | 	subjects := []rbac.Subject{{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}} | ||||||
| 	if len(r.superUser) > 0 { | 	if len(r.superUser) > 0 { | ||||||
| 		subjects = append(subjects, rbac.Subject{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: r.superUser}) | 		subjects = append(subjects, rbac.Subject{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: r.superUser}) | ||||||
| 	} | 	} | ||||||
| 	errorlist := []error{} | 	errorlist := []error{} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -58,29 +58,29 @@ func TestSubjectLocator(t *testing.T) { | |||||||
| 				{ | 				{ | ||||||
| 					&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""}, | 					&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 						{Kind: rbac.UserKind, Name: "admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					// cluster role matches star in namespace | 					// cluster role matches star in namespace | ||||||
| 					&defaultAttributes{"", "", "*", "Pods", "", "*", ""}, | 					&defaultAttributes{"", "", "*", "Pods", "", "*", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					// empty ns | 					// empty ns | ||||||
| 					&defaultAttributes{"", "", "*", "Pods", "", "", ""}, | 					&defaultAttributes{"", "", "*", "Pods", "", "", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| @@ -104,32 +104,32 @@ func TestSubjectLocator(t *testing.T) { | |||||||
| 				{ | 				{ | ||||||
| 					&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""}, | 					&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 						{Kind: rbac.UserKind, Name: "admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					// verb matchies correctly | 					// verb matchies correctly | ||||||
| 					&defaultAttributes{"", "", "create", "Pods", "", "ns1", ""}, | 					&defaultAttributes{"", "", "create", "Pods", "", "ns1", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					// binding only works in correct ns | 					// binding only works in correct ns | ||||||
| 					&defaultAttributes{"", "", "get", "Pods", "", "ns2", ""}, | 					&defaultAttributes{"", "", "get", "Pods", "", "ns2", ""}, | ||||||
| 					[]rbac.Subject{ | 					[]rbac.Subject{ | ||||||
| 						{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: user.SystemPrivilegedGroup}, | ||||||
| 						{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "foo"}, | ||||||
| 						{Kind: rbac.UserKind, Name: "super-admin"}, | 						{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "super-admin"}, | ||||||
| 						{Kind: rbac.GroupKind, Name: "super-admins"}, | 						{Kind: rbac.GroupKind, APIGroup: rbac.GroupName, Name: "super-admins"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| @@ -144,7 +144,7 @@ func TestSubjectLocator(t *testing.T) { | |||||||
| 				t.Errorf("case %q %d: error %v", tt.name, i, err) | 				t.Errorf("case %q %d: error %v", tt.name, i, err) | ||||||
| 			} | 			} | ||||||
| 			if !reflect.DeepEqual(actualSubjects, action.subjects) { | 			if !reflect.DeepEqual(actualSubjects, action.subjects) { | ||||||
| 				t.Errorf("case %q %d: expected %v actual %v", tt.name, i, action.subjects, actualSubjects) | 				t.Errorf("case %q %d: expected\n%v\nactual\n%v", tt.name, i, action.subjects, actualSubjects) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -181,7 +181,7 @@ var ( | |||||||
|     "name": "write-jobs" |     "name": "write-jobs" | ||||||
|   }, |   }, | ||||||
|   "subjects": [{ |   "subjects": [{ | ||||||
|     "apiVersion": "rbac/v1alpha1", |     "apiGroup": "rbac.authorization.k8s.io", | ||||||
|     "kind": "User", |     "kind": "User", | ||||||
|     "name": "admin" |     "name": "admin" | ||||||
|   }] |   }] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jordan Liggitt
					Jordan Liggitt