Files
kubesphere/pkg/apiserver/authorization/rbac/rbac_test.go
KubeSphere CI Bot 447a51f08b feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

---------

Signed-off-by: ci-bot <ci-bot@kubesphere.io>
Co-authored-by: ks-ci-bot <ks-ci-bot@example.com>
Co-authored-by: joyceliu <joyceliu@yunify.com>
2024-09-06 11:05:52 +08:00

1028 lines
27 KiB
Go

package rbac
import (
"context"
"errors"
"hash/fnv"
"io"
"sort"
"testing"
"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/user"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
runtimefakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
"kubesphere.io/kubesphere/pkg/scheme"
)
// StaticRoles is a rule resolver that resolves from lists of role objects.
type StaticRoles struct {
roles []*iamv1beta1.Role
roleBindings []*iamv1beta1.RoleBinding
clusterRoles []*iamv1beta1.ClusterRole
clusterRoleBindings []*iamv1beta1.ClusterRoleBinding
workspaceRoles []*iamv1beta1.WorkspaceRole
workspaceRoleBindings []*iamv1beta1.WorkspaceRoleBinding
globalRoles []*iamv1beta1.GlobalRole
globalRoleBindings []*iamv1beta1.GlobalRoleBinding
namespaces []*corev1.Namespace
}
func (r *StaticRoles) GetRole(namespace, name string) (*iamv1beta1.Role, error) {
if len(namespace) == 0 {
return nil, errors.New("must provide namespace when getting role")
}
for _, role := range r.roles {
if role.Namespace == namespace && role.Name == name {
return role, nil
}
}
return nil, errors.New("role not found")
}
func (r *StaticRoles) GetClusterRole(name string) (*iamv1beta1.ClusterRole, error) {
for _, clusterRole := range r.clusterRoles {
if clusterRole.Name == name {
return clusterRole, nil
}
}
return nil, errors.New("cluster role not found")
}
func (r *StaticRoles) ListRoleBindings(namespace string) ([]*iamv1beta1.RoleBinding, error) {
if len(namespace) == 0 {
return nil, errors.New("must provide namespace when listing role bindings")
}
var roleBindingList []*iamv1beta1.RoleBinding
for _, roleBinding := range r.roleBindings {
if roleBinding.Namespace != namespace {
continue
}
roleBindingList = append(roleBindingList, roleBinding)
}
return roleBindingList, nil
}
func (r *StaticRoles) ListClusterRoleBindings() ([]*iamv1beta1.ClusterRoleBinding, error) {
return r.clusterRoleBindings, nil
}
// compute a hash of a policy rule, so we can sort in a deterministic order
func hashOf(p rbacv1.PolicyRule) string {
hash := fnv.New32()
writeStrings := func(slis ...[]string) {
for _, sli := range slis {
for _, s := range sli {
_, _ = io.WriteString(hash, s)
}
}
}
writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs)
return string(hash.Sum(nil))
}
// byHash sorts a set of policy rules by a hash of its fields
type byHash []rbacv1.PolicyRule
func (b byHash) Len() int { return len(b) }
func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) }
func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func TestRBACAuthorizer(t *testing.T) {
ruleReadPods := rbacv1.PolicyRule{
Verbs: []string{"GET", "WATCH"},
APIGroups: []string{"v1"},
Resources: []string{"pods"},
}
ruleReadServices := rbacv1.PolicyRule{
Verbs: []string{"GET", "WATCH"},
APIGroups: []string{"v1"},
Resources: []string{"services"},
}
ruleWriteNodes := rbacv1.PolicyRule{
Verbs: []string{"PUT", "CREATE", "UPDATE"},
APIGroups: []string{"v1"},
Resources: []string{"nodes"},
}
ruleAdmin := rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
}
staticRoles := StaticRoles{
roles: []*iamv1beta1.Role{
{
ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
Rules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices},
},
},
clusterRoles: []*iamv1beta1.ClusterRole{
{
ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"},
Rules: []rbacv1.PolicyRule{ruleAdmin},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "write-nodes"},
Rules: []rbacv1.PolicyRule{ruleWriteNodes},
},
},
workspaceRoles: []*iamv1beta1.WorkspaceRole{
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-workspace-manager",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
Rules: []rbacv1.PolicyRule{ruleAdmin},
},
},
globalRoles: []*iamv1beta1.GlobalRole{
{
ObjectMeta: metav1.ObjectMeta{
Name: "global-admin",
},
Rules: []rbacv1.PolicyRule{ruleAdmin},
},
},
roleBindings: []*iamv1beta1.RoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace1",
Name: "readthings",
},
Subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "foobar"},
{Kind: rbacv1.GroupKind, Name: "group1"},
},
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "readthings"},
},
},
workspaceRoleBindings: []*iamv1beta1.WorkspaceRoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-workspace-manager-tester",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindWorkspaceRole,
Name: "system-workspace-workspace-manager",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Name: "tester",
},
},
},
},
globalRoleBindings: []*iamv1beta1.GlobalRoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Name: "admin",
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindGlobalRole,
Name: "global-admin",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Name: "admin",
},
},
},
},
}
tests := []struct {
StaticRoles
// For a given context, what are the rules that apply?
user user.Info
namespace string
workspace string
effectiveRules []rbacv1.PolicyRule
}{
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "admin"},
workspace: "system-workspace",
effectiveRules: []rbacv1.PolicyRule{ruleAdmin},
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "admin"},
namespace: "namespace1",
effectiveRules: []rbacv1.PolicyRule{ruleAdmin},
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "tester"},
workspace: "system-workspace",
effectiveRules: []rbacv1.PolicyRule{ruleAdmin},
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "tester"},
workspace: "not-exists-workspace",
effectiveRules: nil,
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "foobar"},
namespace: "namespace1",
effectiveRules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices},
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{Name: "foobar"},
namespace: "namespace2",
effectiveRules: nil,
},
{
StaticRoles: staticRoles,
// Same as above but without a namespace. Only global rules should apply.
user: &user.DefaultInfo{Name: "foobar"},
effectiveRules: nil,
},
{
StaticRoles: staticRoles,
user: &user.DefaultInfo{},
effectiveRules: nil,
},
}
for i, tc := range tests {
ruleResolver, err := newMockRBACAuthorizer(&tc.StaticRoles)
if err != nil {
t.Fatal(err)
}
scope := request.ClusterScope
if tc.workspace != "" {
scope = request.WorkspaceScope
}
if tc.namespace != "" {
scope = request.NamespaceScope
}
rules, err := ruleResolver.rulesFor(authorizer.AttributesRecord{
User: tc.user,
Namespace: tc.namespace,
Workspace: tc.workspace,
ResourceScope: scope,
ResourceRequest: true,
})
if err != nil {
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
continue
}
// Sort for deep equals
sort.Sort(byHash(rules))
sort.Sort(byHash(tc.effectiveRules))
if diff := cmp.Diff(rules, tc.effectiveRules); diff != "" {
t.Errorf("case %d: %s", i, diff)
}
}
}
func TestRBACAuthorizerMakeDecision(t *testing.T) {
t.Skipf("TODO: refactor this test case")
staticRoles := StaticRoles{
roles: []*iamv1beta1.Role{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kubesphere-system",
Name: "kubesphere-system-admin",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kubesphere-system",
Name: "kubesphere-system-viewer",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
},
clusterRoles: []*iamv1beta1.ClusterRole{
{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-viewer",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-admin",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
},
workspaceRoles: []*iamv1beta1.WorkspaceRole{
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-admin",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-viewer",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
},
globalRoles: []*iamv1beta1.GlobalRole{
{
ObjectMeta: metav1.ObjectMeta{
Name: "global-admin",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
NonResourceURLs: []string{"*"},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "global-viewer",
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
},
},
},
roleBindings: []*iamv1beta1.RoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kubesphere-system",
Name: "kubesphere-system-admin",
},
Subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "kubesphere-system-admin"},
},
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "kubesphere-system-admin"},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kubesphere-system",
Name: "kubesphere-system-viewer",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
Name: "kubesphere-system-viewer",
},
},
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "kubesphere-system-viewer"},
},
},
workspaceRoleBindings: []*iamv1beta1.WorkspaceRoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-admin",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindWorkspaceRole,
Name: "system-workspace-admin",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
Name: "system-workspace-admin",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "system-workspace-viewer",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindWorkspaceRole,
Name: "system-workspace-viewer",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
Name: "system-workspace-viewer",
},
},
},
},
clusterRoleBindings: []*iamv1beta1.ClusterRoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-admin",
},
Subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "cluster-admin"},
},
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "cluster-admin"},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-viewer",
},
Subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "cluster-viewer"},
},
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "cluster-viewer"},
},
},
globalRoleBindings: []*iamv1beta1.GlobalRoleBinding{
{
ObjectMeta: metav1.ObjectMeta{
Name: "admin",
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindGlobalRole,
Name: "global-admin",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Name: "admin",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "viewer",
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Kind: iamv1beta1.ResourceKindGlobalRole,
Name: "global-viewer",
},
Subjects: []rbacv1.Subject{
{
Kind: iamv1beta1.ResourceKindUser,
APIGroup: iamv1beta1.SchemeGroupVersion.Group,
Name: "viewer",
},
},
},
},
namespaces: []*corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "kubesphere-system",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "kube-system",
Labels: map[string]string{tenantv1beta1.WorkspaceLabel: "system-workspace"},
},
},
},
}
tests := []struct {
StaticRoles
Request authorizer.AttributesRecord
ExpectedDecision authorizer.Decision
}{
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "admin",
},
Verb: "create",
APIGroup: "",
APIVersion: "v1",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.ClusterScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "viewer",
},
Verb: "create",
APIGroup: "",
APIVersion: "v1",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.ClusterScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "viewer",
},
Verb: "list",
APIGroup: "",
APIVersion: "v1",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.ClusterScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "admin",
},
Verb: "list",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-admin",
},
Verb: "list",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-viewer",
},
Verb: "list",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "admin",
},
Verb: "create",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: iamv1beta1.ScopeWorkspace,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-admin",
},
Verb: "create",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-viewer",
},
Verb: "create",
Workspace: "system-workspace",
APIGroup: "tenant.kubesphere.io",
APIVersion: "v1alpha2",
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "admin",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "viewer",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-admin",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-viewer",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "kubesphere-system-admin",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "kubesphere-system-viewer",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kubesphere-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "system-workspace-admin",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kube-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionAllow,
},
{
StaticRoles: staticRoles,
Request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "kubesphere-system-admin",
},
Verb: "create",
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
Namespace: "kube-system",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
},
ExpectedDecision: authorizer.DecisionNoOpinion,
},
}
for i, tc := range tests {
ruleResolver, err := newMockRBACAuthorizer(&tc.StaticRoles)
if err != nil {
t.Fatal(err)
}
decision, message, err := ruleResolver.Authorize(&tc.Request)
if err != nil {
t.Errorf("case %d: %v: %s", i, err, message)
continue
}
if decision != tc.ExpectedDecision {
t.Errorf("case %d: %d != %d", i, decision, tc.ExpectedDecision)
}
}
}
func newMockRBACAuthorizer(staticRoles *StaticRoles) (*Authorizer, error) {
client := runtimefakeclient.NewClientBuilder().
WithScheme(scheme.Scheme).Build()
for _, role := range staticRoles.roles {
if err := client.Create(context.Background(), role.DeepCopy()); err != nil {
return nil, err
}
}
for _, roleBinding := range staticRoles.roleBindings {
if err := client.Create(context.Background(), roleBinding.DeepCopy()); err != nil {
return nil, err
}
}
for _, clusterRole := range staticRoles.clusterRoles {
if err := client.Create(context.Background(), clusterRole.DeepCopy()); err != nil {
return nil, err
}
}
for _, clusterRoleBinding := range staticRoles.clusterRoleBindings {
if err := client.Create(context.Background(), clusterRoleBinding.DeepCopy()); err != nil {
return nil, err
}
}
for _, workspaceRole := range staticRoles.workspaceRoles {
if err := client.Create(context.Background(), workspaceRole.DeepCopy()); err != nil {
return nil, err
}
}
for _, workspaceRoleBinding := range staticRoles.workspaceRoleBindings {
if err := client.Create(context.Background(), workspaceRoleBinding.DeepCopy()); err != nil {
return nil, err
}
}
for _, globalRole := range staticRoles.globalRoles {
if err := client.Create(context.Background(), globalRole.DeepCopy()); err != nil {
return nil, err
}
}
for _, globalRoleBinding := range staticRoles.globalRoleBindings {
if err := client.Create(context.Background(), globalRoleBinding.DeepCopy()); err != nil {
return nil, err
}
}
fakeCache := &informertest.FakeInformers{Scheme: scheme.Scheme}
resourceManager, err := v1beta1.New(context.Background(), client, fakeCache)
if err != nil {
return nil, err
}
return NewRBACAuthorizer(am.NewReadOnlyOperator(resourceManager)), nil
}
func TestAppliesTo(t *testing.T) {
tests := []struct {
subjects []rbacv1.Subject
user user.Info
namespace string
appliesTo bool
index int
testCase string
}{
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "foobar"},
},
user: &user.DefaultInfo{Name: "foobar"},
appliesTo: true,
index: 0,
testCase: "single subject that matches username",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "barfoo"},
{Kind: rbacv1.UserKind, Name: "foobar"},
},
user: &user.DefaultInfo{Name: "foobar"},
appliesTo: true,
index: 1,
testCase: "multiple subjects, one that matches username",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "barfoo"},
{Kind: rbacv1.UserKind, Name: "foobar"},
},
user: &user.DefaultInfo{Name: "zimzam"},
appliesTo: false,
testCase: "multiple subjects, none that match username",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "barfoo"},
{Kind: rbacv1.GroupKind, Name: "foobar"},
},
user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}},
appliesTo: true,
index: 1,
testCase: "multiple subjects, one that match group",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "barfoo"},
{Kind: rbacv1.GroupKind, Name: "foobar"},
},
user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}},
namespace: "namespace1",
appliesTo: true,
index: 1,
testCase: "multiple subjects, one that match group, should ignore namespace",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "barfoo"},
{Kind: rbacv1.GroupKind, Name: "foobar"},
{Kind: rbacv1.ServiceAccountKind, APIGroup: rbacv1.GroupName, Namespace: "kube-system", Name: "default"},
},
user: &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"},
namespace: "default",
appliesTo: true,
index: 2,
testCase: "multiple subjects with a service account that matches",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.UserKind, Name: "*"},
},
user: &user.DefaultInfo{Name: "foobar"},
namespace: "default",
appliesTo: false,
testCase: "* user subject name doesn't match all users",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.GroupKind, Name: user.AllAuthenticated},
{Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated},
},
user: &user.DefaultInfo{Name: "foobar", Groups: []string{user.AllAuthenticated}},
namespace: "default",
appliesTo: true,
index: 0,
testCase: "binding to all authenticated and unauthenticated subjects matches authenticated user",
},
{
subjects: []rbacv1.Subject{
{Kind: rbacv1.GroupKind, Name: user.AllAuthenticated},
{Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated},
},
user: &user.DefaultInfo{Name: "system:anonymous", Groups: []string{user.AllUnauthenticated}},
namespace: "default",
appliesTo: true,
index: 1,
testCase: "binding to all authenticated and unauthenticated subjects matches anonymous user",
},
}
for _, tc := range tests {
gotIndex, got := appliesTo(tc.user, tc.subjects, tc.namespace)
if got != tc.appliesTo {
t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
}
if gotIndex != tc.index {
t.Errorf("case %q want index %d, got %d", tc.testCase, tc.index, gotIndex)
}
}
}