change rbac roleref type

This commit is contained in:
deads2k
2016-08-24 14:23:27 -04:00
parent 80f56d831c
commit 8c788233e7
12 changed files with 281 additions and 61 deletions

View File

@@ -32,7 +32,7 @@ import (
type AuthorizationRuleResolver interface {
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace
// of the role binding, the empty string if a cluster role binding.
GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, namespace string) ([]rbac.PolicyRule, error)
GetRoleReferenceRules(ctx api.Context, roleRef rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error)
// GetEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations
@@ -101,31 +101,24 @@ type ClusterRoleBindingLister interface {
}
// GetRoleReferenceRules attempts resolve the RoleBinding or ClusterRoleBinding.
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, bindingNamespace string) ([]rbac.PolicyRule, error) {
switch roleRef.Kind {
case "Role":
// Roles can only be referenced by RoleBindings within the same namespace.
if len(bindingNamespace) == 0 {
return nil, fmt.Errorf("cluster role binding references role %q in namespace %q", roleRef.Name, roleRef.Namespace)
} else {
if bindingNamespace != roleRef.Namespace {
return nil, fmt.Errorf("role binding in namespace %q references role %q in namespace %q", bindingNamespace, roleRef.Name, roleRef.Namespace)
}
}
role, err := r.roleGetter.GetRole(api.WithNamespace(ctx, roleRef.Namespace), roleRef.Name)
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx api.Context, roleRef rbac.RoleRef, bindingNamespace string) ([]rbac.PolicyRule, error) {
switch kind := rbac.RoleRefGroupKind(roleRef); kind {
case rbac.Kind("Role"):
role, err := r.roleGetter.GetRole(api.WithNamespace(ctx, bindingNamespace), roleRef.Name)
if err != nil {
return nil, err
}
return role.Rules, nil
case "ClusterRole":
case rbac.Kind("ClusterRole"):
clusterRole, err := r.clusterRoleGetter.GetClusterRole(api.WithNamespace(ctx, ""), roleRef.Name)
if err != nil {
return nil, err
}
return clusterRole.Rules, nil
default:
return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
return nil, fmt.Errorf("unsupported role reference kind: %q", kind)
}
}

View File

@@ -96,7 +96,7 @@ func TestDefaultRuleResolver(t *testing.T) {
{Kind: rbac.UserKind, Name: "foobar"},
{Kind: rbac.GroupKind, Name: "group1"},
},
RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "readthings"},
},
},
clusterRoleBindings: []rbac.ClusterRoleBinding{
@@ -105,7 +105,7 @@ func TestDefaultRuleResolver(t *testing.T) {
{Kind: rbac.UserKind, Name: "admin"},
{Kind: rbac.GroupKind, Name: "admin"},
},
RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "cluster-admin"},
},
},
}

View File

@@ -103,11 +103,17 @@ func ValidateRoleBinding(roleBinding *rbac.RoleBinding) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, true, minimalNameRequirements, field.NewPath("metadata"))...)
// roleRef namespace is empty when referring to global policy.
if len(roleBinding.RoleRef.Namespace) > 0 {
for _, msg := range validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false) {
allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg))
}
// TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking
// advantage of the binding infrastructure
if roleBinding.RoleRef.APIGroup != rbac.GroupName {
allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
}
switch roleBinding.RoleRef.Kind {
case "Role", "ClusterRole":
default:
allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"Role", "ClusterRole"}))
}
if len(roleBinding.RoleRef.Name) == 0 {
@@ -141,11 +147,17 @@ func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.Erro
allErrs := field.ErrorList{}
allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, false, minimalNameRequirements, field.NewPath("metadata"))...)
// roleRef namespace is empty when referring to global policy.
if len(roleBinding.RoleRef.Namespace) > 0 {
for _, msg := range validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false) {
allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg))
}
// TODO allow multiple API groups. For now, restrict to one, but I can envision other experimental roles in other groups taking
// advantage of the binding infrastructure
if roleBinding.RoleRef.APIGroup != rbac.GroupName {
allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "apiGroup"), roleBinding.RoleRef.APIGroup, []string{rbac.GroupName}))
}
switch roleBinding.RoleRef.Kind {
case "ClusterRole":
default:
allErrs = append(allErrs, field.NotSupported(field.NewPath("roleRef", "kind"), roleBinding.RoleRef.Kind, []string{"ClusterRole"}))
}
if len(roleBinding.RoleRef.Name) == 0 {

View File

@@ -24,11 +24,126 @@ import (
"k8s.io/kubernetes/pkg/util/validation/field"
)
func TestValidateClusterRoleBinding(t *testing.T) {
errs := ValidateClusterRoleBinding(
&rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "master"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
Subjects: []rbac.Subject{
{Name: "validsaname", Namespace: "foo", Kind: rbac.ServiceAccountKind},
{Name: "valid@username", Kind: rbac.UserKind},
{Name: "valid@groupname", Kind: rbac.GroupKind},
},
},
)
if len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
errorCases := map[string]struct {
A rbac.ClusterRoleBinding
T field.ErrorType
F string
}{
"bad group": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"},
},
T: field.ErrorTypeNotSupported,
F: "roleRef.apiGroup",
},
"bad kind": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"},
},
T: field.ErrorTypeNotSupported,
F: "roleRef.kind",
},
"reference role": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
},
T: field.ErrorTypeNotSupported,
F: "roleRef.kind",
},
"zero-length name": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
},
T: field.ErrorTypeRequired,
F: "metadata.name",
},
"bad role": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole"},
},
T: field.ErrorTypeRequired,
F: "roleRef.name",
},
"bad subject kind": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "master"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
Subjects: []rbac.Subject{{Name: "subject"}},
},
T: field.ErrorTypeNotSupported,
F: "subjects[0].kind",
},
"bad subject name": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "master"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
Subjects: []rbac.Subject{{Namespace: "foo", Name: "subject:bad", Kind: rbac.ServiceAccountKind}},
},
T: field.ErrorTypeInvalid,
F: "subjects[0].name",
},
"missing SA namespace": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "master"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
Subjects: []rbac.Subject{{Name: "good", Kind: rbac.ServiceAccountKind}},
},
T: field.ErrorTypeRequired,
F: "subjects[0].namespace",
},
"missing subject name": {
A: rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{Name: "master"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "ClusterRole", Name: "valid"},
Subjects: []rbac.Subject{{Namespace: "foo", Kind: rbac.ServiceAccountKind}},
},
T: field.ErrorTypeRequired,
F: "subjects[0].name",
},
}
for k, v := range errorCases {
errs := ValidateClusterRoleBinding(&v.A)
if len(errs) == 0 {
t.Errorf("expected failure %s for %v", k, v.A)
continue
}
for i := range errs {
if errs[i].Type != v.T {
t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
}
if errs[i].Field != v.F {
t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
}
}
}
}
func TestValidateRoleBinding(t *testing.T) {
errs := ValidateRoleBinding(
&rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
Subjects: []rbac.Subject{
{Name: "validsaname", Kind: rbac.ServiceAccountKind},
{Name: "valid@username", Kind: rbac.UserKind},
@@ -45,10 +160,26 @@ func TestValidateRoleBinding(t *testing.T) {
T field.ErrorType
F string
}{
"bad group": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.GroupName", Kind: "ClusterRole", Name: "valid"},
},
T: field.ErrorTypeNotSupported,
F: "roleRef.apiGroup",
},
"bad kind": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "default"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Type", Name: "valid"},
},
T: field.ErrorTypeNotSupported,
F: "roleRef.kind",
},
"zero-length namespace": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Name: "default"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
},
T: field.ErrorTypeRequired,
F: "metadata.namespace",
@@ -56,23 +187,15 @@ func TestValidateRoleBinding(t *testing.T) {
"zero-length name": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
},
T: field.ErrorTypeRequired,
F: "metadata.name",
},
"invalid ref": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "name"},
RoleRef: api.ObjectReference{Namespace: "-192083", Name: "valid"},
},
T: field.ErrorTypeInvalid,
F: "roleRef.namespace",
},
"bad role": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "default"},
RoleRef: api.ObjectReference{Namespace: "default"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role"},
},
T: field.ErrorTypeRequired,
F: "roleRef.name",
@@ -80,7 +203,7 @@ func TestValidateRoleBinding(t *testing.T) {
"bad subject kind": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
Subjects: []rbac.Subject{{Name: "subject"}},
},
T: field.ErrorTypeNotSupported,
@@ -89,7 +212,7 @@ func TestValidateRoleBinding(t *testing.T) {
"bad subject name": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
Subjects: []rbac.Subject{{Name: "subject:bad", Kind: rbac.ServiceAccountKind}},
},
T: field.ErrorTypeInvalid,
@@ -98,7 +221,7 @@ func TestValidateRoleBinding(t *testing.T) {
"missing subject name": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
Subjects: []rbac.Subject{{Kind: rbac.ServiceAccountKind}},
},
T: field.ErrorTypeRequired,
@@ -125,13 +248,13 @@ func TestValidateRoleBinding(t *testing.T) {
func TestValidateRoleBindingUpdate(t *testing.T) {
old := &rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
}
errs := ValidateRoleBindingUpdate(
&rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "valid"},
},
old,
)
@@ -147,7 +270,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
"changedRef": {
A: rbac.RoleBinding{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"},
RoleRef: api.ObjectReference{Namespace: "master", Name: "changed"},
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "changed"},
},
T: field.ErrorTypeInvalid,
F: "roleRef",