1
0
mirror of https://github.com/rancher/steve.git synced 2025-07-04 18:46:41 +00:00
steve/pkg/accesscontrol/policy_rule_index_test.go

389 lines
9.5 KiB
Go
Raw Normal View History

package accesscontrol
import (
"reflect"
"slices"
"testing"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func Test_policyRuleIndex_roleBindingBySubject(t *testing.T) {
roleRef := rbacv1.RoleRef{Kind: "Role", Name: "testrole"}
tests := []struct {
name string
kind string
rb *rbacv1.RoleBinding
want []string
}{
{
name: "indexes users",
kind: "User",
rb: makeRB("testns", "testrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "User",
Name: "myuser",
},
}),
want: []string{"myuser"},
},
{
name: "indexes multiple subjects",
kind: "Group",
rb: makeRB("testns", "testrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup1",
},
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup2",
},
}),
want: []string{"mygroup1", "mygroup2"},
},
{
name: "indexes svcaccounts in user mode",
kind: "User",
rb: makeRB("testns", "testrb", roleRef, []rbacv1.Subject{
{
APIGroup: "",
Kind: "ServiceAccount",
Name: "mysvcaccount",
Namespace: "testns",
},
}),
want: []string{"system:serviceaccount:testns:mysvcaccount"},
},
{
name: "ignores svcaccounts in group mode",
kind: "Group",
rb: makeRB("testns", "testrb", roleRef, []rbacv1.Subject{
{
APIGroup: "",
Kind: "ServiceAccount",
Name: "mysvcaccount",
Namespace: "testns",
},
}),
want: []string{},
},
{
name: "ignores unknown subjects",
kind: "Group",
rb: makeRB("testns", "testrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "User",
Name: "myuser",
},
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup1",
},
{
APIGroup: "custom.api.group",
Kind: "CustomGroup",
Name: "mygroup2",
},
}),
want: []string{"mygroup1"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
indexFunc := roleBindingBySubjectIndexer(tt.kind)
if got, err := indexFunc(tt.rb); err != nil {
t.Error(err)
} else if !slices.Equal(got, tt.want) {
t.Errorf("roleBindingBySubjectIndexer() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_policyRuleIndex_clusterRoleBindingBySubject(t *testing.T) {
roleRef := rbacv1.RoleRef{Kind: "ClusterRole", Name: "testclusterrole"}
tests := []struct {
name string
kind string
crb *rbacv1.ClusterRoleBinding
want []string
}{
{
name: "ignores if RoleRef is a Role",
kind: "User",
crb: makeCRB("testcrb", rbacv1.RoleRef{Kind: "Role", Name: "testrole"}, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "User",
Name: "myuser",
},
}),
want: []string{},
},
{
name: "indexes users",
kind: "User",
crb: makeCRB("testcrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "User",
Name: "myuser",
},
}),
want: []string{"myuser"},
},
{
name: "indexes multiple subjects",
kind: "Group",
crb: makeCRB("testcrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup1",
},
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup2",
},
}),
want: []string{"mygroup1", "mygroup2"},
},
{
name: "indexes svcaccounts in user mode",
kind: "User",
crb: makeCRB("testcrb", roleRef, []rbacv1.Subject{
{
APIGroup: "",
Kind: "ServiceAccount",
Name: "mysvcaccount",
Namespace: "testns",
},
}),
want: []string{"system:serviceaccount:testns:mysvcaccount"},
},
{
name: "ignores svcaccounts in group mode",
kind: "Group",
crb: makeCRB("testcrb", roleRef, []rbacv1.Subject{
{
APIGroup: "",
Kind: "ServiceAccount",
Name: "mysvcaccount",
Namespace: "testns",
},
}),
want: []string{},
},
{
name: "ignores unknown subjects",
kind: "Group",
crb: makeCRB("testcrb", roleRef, []rbacv1.Subject{
{
APIGroup: rbacGroup,
Kind: "User",
Name: "myuser",
},
{
APIGroup: rbacGroup,
Kind: "Group",
Name: "mygroup1",
},
{
APIGroup: "custom.api.group",
Kind: "CustomGroup",
Name: "mygroup2",
},
}),
want: []string{"mygroup1"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
indexFunc := clusterRoleBindingBySubjectIndexer(tt.kind)
if got, err := indexFunc(tt.crb); err != nil {
t.Error(err)
} else if !slices.Equal(got, tt.want) {
t.Errorf("clusterRoleBindingBySubjectIndexer() got = %v, want %v", got, tt.want)
}
})
}
}
func makeRB(namespace, name string, roleRef rbacv1.RoleRef, subjects []rbacv1.Subject) *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
},
RoleRef: roleRef,
Subjects: subjects,
}
}
func makeCRB(name string, roleRef rbacv1.RoleRef, subjects []rbacv1.Subject) *rbacv1.ClusterRoleBinding {
return &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
RoleRef: roleRef,
Subjects: subjects,
}
}
func Test_addResourceAccess(t *testing.T) {
tests := []struct {
name string
namespace string
rule rbacv1.PolicyRule
want AccessSet
}{
{
name: "RoleBinding namespaces resource with empty names",
namespace: "test-ns",
rule: rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"namespaces", "deployments"},
ResourceNames: []string{},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{
verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
Access{Namespace: "*", ResourceName: "test-ns"}: true,
},
{
verb: "get", gr: schema.GroupResource{Group: "", Resource: "deployments"}}: {
Access{Namespace: "test-ns", ResourceName: "*"}: true,
},
},
},
},
{
name: "ClusterRoleBinding namespaces resource with empty names",
namespace: "*",
rule: rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"namespaces", "deployments"},
ResourceNames: []string{},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
Access{Namespace: "*", ResourceName: "*"}: true,
},
{
verb: "get", gr: schema.GroupResource{Group: "", Resource: "deployments"}}: {
Access{Namespace: "*", ResourceName: "*"}: true,
},
},
},
},
{
name: "RoleBinding namespaces resource with specific names",
namespace: "test-ns",
rule: rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"namespaces"},
ResourceNames: []string{"specific-ns"},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
Access{Namespace: "test-ns", ResourceName: "specific-ns"}: true,
},
},
},
},
{
name: "RoleBinding namespaces resource with its own namespace",
namespace: "test-ns",
rule: rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"namespaces"},
ResourceNames: []string{"test-ns"},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
Access{Namespace: "*", ResourceName: "test-ns"}: true,
},
},
},
},
{
name: "RoleBinding other resource with empty names",
namespace: "test-ns",
rule: rbacv1.PolicyRule{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
ResourceNames: []string{},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
Access{Namespace: "test-ns", ResourceName: "*"}: true,
},
},
},
},
{
name: "ClusterRoleBinding other resource with empty names",
namespace: "*",
rule: rbacv1.PolicyRule{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
ResourceNames: []string{},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
Access{Namespace: "*", ResourceName: "*"}: true,
},
},
},
},
{
name: "RoleBinding other resource with specific names",
namespace: "test-ns",
rule: rbacv1.PolicyRule{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
ResourceNames: []string{"my-deploy"},
Verbs: []string{"get"},
},
want: AccessSet{
set: map[key]resourceAccessSet{
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
Access{Namespace: "test-ns", ResourceName: "my-deploy"}: true,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
accessSet := &AccessSet{}
addResourceAccess(accessSet, tt.namespace, tt.rule)
if !reflect.DeepEqual(*accessSet, tt.want) {
t.Errorf("addResourceAccess() got = %v, want %v", *accessSet, tt.want)
}
})
}
}