Files
capsule/api/v1beta2/tenant_func_test.go

239 lines
6.0 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2_test
import (
"reflect"
"testing"
rbacv1 "k8s.io/api/rbac/v1"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
capsulerbac "github.com/projectcapsule/capsule/pkg/api/rbac"
)
func testTenant() *capsulev1beta2.Tenant {
return &capsulev1beta2.Tenant{
Spec: capsulev1beta2.TenantSpec{
AdditionalRoleBindings: []capsulerbac.AdditionalRoleBindingsSpec{
{
ClusterRoleName: "developer",
Subjects: []rbacv1.Subject{
{Kind: "User", Name: "user2"},
{Kind: "Group", Name: "group1"},
},
},
{
ClusterRoleName: "cluster-admin",
Subjects: []rbacv1.Subject{
{Kind: "User", Name: "user3"},
{Kind: "Group", Name: "group1"},
},
},
{
ClusterRoleName: "deployer",
Subjects: []rbacv1.Subject{
{Kind: "ServiceAccount", Name: "system:serviceaccount:argocd:argo-operator"},
},
},
},
},
Status: capsulev1beta2.TenantStatus{
Owners: capsulerbac.OwnerStatusListSpec{
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.UserOwner,
Name: "user1",
},
ClusterRoles: []string{"cluster-admin", "read-only"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.GroupOwner,
Name: "group1",
},
ClusterRoles: []string{"edit"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.ServiceAccountOwner,
Name: "service",
},
ClusterRoles: []string{"read-only"},
},
},
},
}
}
func TestGetSubjectsByClusterRoles(t *testing.T) {
t.Parallel()
tenant := testTenant()
expected := map[string][]rbacv1.Subject{
"cluster-admin": {
{Kind: "User", Name: "user1"},
{Kind: "User", Name: "user3"},
{Kind: "Group", Name: "group1"},
},
"read-only": {
{Kind: "User", Name: "user1"},
{Kind: "ServiceAccount", Name: "service"},
},
"edit": {
{Kind: "Group", Name: "group1"},
},
"developer": {
{Kind: "User", Name: "user2"},
{Kind: "Group", Name: "group1"},
},
"deployer": {
{Kind: "ServiceAccount", Name: "system:serviceaccount:argocd:argo-operator"},
},
}
got := tenant.GetSubjectsByClusterRoles(nil)
if !reflect.DeepEqual(got, expected) {
t.Fatalf("expected %#v\n got %#v", expected, got)
}
expectedIgnored := map[string][]rbacv1.Subject{
"cluster-admin": {
{Kind: "User", Name: "user1"},
{Kind: "User", Name: "user3"},
{Kind: "Group", Name: "group1"},
},
"read-only": {
{Kind: "User", Name: "user1"},
},
"edit": {
{Kind: "Group", Name: "group1"},
},
"developer": {
{Kind: "User", Name: "user2"},
{Kind: "Group", Name: "group1"},
},
}
gotIgnored := tenant.GetSubjectsByClusterRoles([]capsulerbac.OwnerKind{capsulerbac.ServiceAccountOwner})
if !reflect.DeepEqual(gotIgnored, expectedIgnored) {
t.Fatalf("expected %#v\n got %#v", expectedIgnored, gotIgnored)
}
}
func TestGetClusterRolesBySubjectSorted(t *testing.T) {
t.Parallel()
tenant := &capsulev1beta2.Tenant{
Spec: capsulev1beta2.TenantSpec{
AdditionalRoleBindings: []capsulerbac.AdditionalRoleBindingsSpec{
{
ClusterRoleName: "deployer",
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "system:serviceaccount:argocd:argo-operator",
},
},
},
{
ClusterRoleName: "developer",
Subjects: []rbacv1.Subject{
{
Kind: "Group",
Name: "group1",
},
},
},
},
},
Status: capsulev1beta2.TenantStatus{
Owners: capsulerbac.OwnerStatusListSpec{
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.UserOwner,
Name: "user1",
},
ClusterRoles: []string{"cluster-admin", "read-only"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.UserOwner,
Name: "user2",
},
ClusterRoles: []string{"developer"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.UserOwner,
Name: "user3",
},
ClusterRoles: []string{"cluster-admin"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.GroupOwner,
Name: "group1",
},
ClusterRoles: []string{"edit", "developer", "cluster-admin"},
},
{
UserSpec: capsulerbac.UserSpec{
Kind: capsulerbac.ServiceAccountOwner,
Name: "service",
},
ClusterRoles: []string{"read-only"},
},
},
},
}
expected := []capsulerbac.SubjectRoles{
{Kind: "Group", Name: "group1", Roles: []string{"cluster-admin", "developer", "edit"}},
{Kind: "ServiceAccount", Name: "service", Roles: []string{"read-only"}},
{Kind: "ServiceAccount", Name: "system:serviceaccount:argocd:argo-operator", Roles: []string{"deployer"}},
{Kind: "User", Name: "user1", Roles: []string{"cluster-admin", "read-only"}},
{Kind: "User", Name: "user2", Roles: []string{"developer"}},
{Kind: "User", Name: "user3", Roles: []string{"cluster-admin"}},
}
t.Run("includes all kinds and is deterministic, deduped and sorted", func(t *testing.T) {
t.Parallel()
got := tenant.GetClusterRolesBySubject(nil)
if !reflect.DeepEqual(got, expected) {
t.Fatalf("expected %#v\n got %#v", expected, got)
}
})
t.Run("ignores ServiceAccount kind", func(t *testing.T) {
t.Parallel()
got := tenant.GetClusterRolesBySubject([]capsulerbac.OwnerKind{capsulerbac.ServiceAccountOwner})
expectedIgnored := []capsulerbac.SubjectRoles{
{Kind: "Group", Name: "group1", Roles: []string{"cluster-admin", "developer", "edit"}},
{Kind: "User", Name: "user1", Roles: []string{"cluster-admin", "read-only"}},
{Kind: "User", Name: "user2", Roles: []string{"developer"}},
{Kind: "User", Name: "user3", Roles: []string{"cluster-admin"}},
}
if !reflect.DeepEqual(got, expectedIgnored) {
t.Fatalf("expected %#v\n got %#v", expectedIgnored, got)
}
})
t.Run("empty tenant yields empty slice", func(t *testing.T) {
t.Parallel()
empty := &capsulev1beta2.Tenant{}
got := empty.GetClusterRolesBySubject(nil)
if len(got) != 0 {
t.Fatalf("expected empty, got %#v", got)
}
})
}