- Introduced CRUD operations for user management, including create, invite, update, disable, and reactivate functionalities. - Enhanced user listing with filtering options based on status and search queries. - Implemented multi-service quota management for users, allowing specification of mail, drive, and photos storage limits. - Added audit log export functionality with validation for format and limit parameters. - Established strict RBAC for admin routes, ensuring proper permission checks for read and write operations. - Updated validation logic for user-related requests and improved error handling across the user management API. - Revised database schema to support new user status and quota fields, along with necessary migrations. - Updated project checklist to reflect the completion of user management and admin RBAC enhancements.
201 lines
5.0 KiB
Go
201 lines
5.0 KiB
Go
package permission
|
|
|
|
import "testing"
|
|
|
|
func TestHasRole(t *testing.T) {
|
|
tests := []struct {
|
|
groups []string
|
|
role Role
|
|
want bool
|
|
}{
|
|
{[]string{"role:admin"}, RoleAdmin, true},
|
|
{[]string{"admin"}, RoleAdmin, true},
|
|
{[]string{"role:user"}, RoleAdmin, false},
|
|
{[]string{" role:service "}, RoleService, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if got := HasRole(tt.groups, tt.role); got != tt.want {
|
|
t.Fatalf("HasRole(%v, %q) = %v, want %v", tt.groups, tt.role, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHasPermissionHierarchy(t *testing.T) {
|
|
groups := []string{"drive:write"}
|
|
|
|
if !HasPermission(groups, ResourceDrive, LevelRead) {
|
|
t.Fatal("write should satisfy read")
|
|
}
|
|
if !HasPermission(groups, ResourceDrive, LevelWrite) {
|
|
t.Fatal("write should satisfy write")
|
|
}
|
|
if HasPermission(groups, ResourceDrive, LevelAdmin) {
|
|
t.Fatal("write should not satisfy admin")
|
|
}
|
|
}
|
|
|
|
func TestHasPermissionAdminBypass(t *testing.T) {
|
|
groups := []string{"role:admin"}
|
|
|
|
if !HasPermission(groups, ResourcePhotos, LevelAdmin) {
|
|
t.Fatal("platform admin should bypass resource checks")
|
|
}
|
|
}
|
|
|
|
func TestHasPermissionResourceAdmin(t *testing.T) {
|
|
groups := []string{"calendar:admin"}
|
|
|
|
if !HasPermission(groups, ResourceCalendar, LevelRead) {
|
|
t.Fatal("resource admin should satisfy read")
|
|
}
|
|
if !HasPermission(groups, ResourceCalendar, LevelWrite) {
|
|
t.Fatal("resource admin should satisfy write")
|
|
}
|
|
if !HasPermission(groups, ResourceCalendar, LevelAdmin) {
|
|
t.Fatal("resource admin should satisfy admin")
|
|
}
|
|
}
|
|
|
|
func TestHasPermissionIsolation(t *testing.T) {
|
|
groups := []string{"contacts:read"}
|
|
|
|
if !HasPermission(groups, ResourceContacts, LevelRead) {
|
|
t.Fatal("expected contacts read")
|
|
}
|
|
if HasPermission(groups, ResourceDrive, LevelRead) {
|
|
t.Fatal("contacts permission must not grant drive access")
|
|
}
|
|
}
|
|
|
|
func TestAdminScopeString(t *testing.T) {
|
|
tests := []struct {
|
|
scope AdminScope
|
|
want string
|
|
}{
|
|
{AdminScopeRead, "read"},
|
|
{AdminScopeWrite, "write"},
|
|
{AdminScope(0), "unknown"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if got := tt.scope.String(); got != tt.want {
|
|
t.Fatalf("AdminScope(%d).String() = %q, want %q", tt.scope, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseAdminScope(t *testing.T) {
|
|
tests := []struct {
|
|
in string
|
|
want AdminScope
|
|
ok bool
|
|
}{
|
|
{"read", AdminScopeRead, true},
|
|
{"write", AdminScopeWrite, true},
|
|
{" READ ", AdminScopeRead, true},
|
|
{"Write", AdminScopeWrite, true},
|
|
{"admin", 0, false},
|
|
{"", 0, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got, ok := ParseAdminScope(tt.in)
|
|
if ok != tt.ok || got != tt.want {
|
|
t.Fatalf("ParseAdminScope(%q) = (%v, %v), want (%v, %v)", tt.in, got, ok, tt.want, tt.ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopeReadOnly(t *testing.T) {
|
|
groups := []string{GroupAdminRead}
|
|
|
|
if !HasAdminScope(groups, AdminScopeRead) {
|
|
t.Fatal("admin:read should satisfy read scope")
|
|
}
|
|
if HasAdminScope(groups, AdminScopeWrite) {
|
|
t.Fatal("admin:read should not satisfy write scope")
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopeWriteImpliesRead(t *testing.T) {
|
|
groups := []string{GroupAdminWrite}
|
|
|
|
if !HasAdminScope(groups, AdminScopeRead) {
|
|
t.Fatal("admin:write should satisfy read scope")
|
|
}
|
|
if !HasAdminScope(groups, AdminScopeWrite) {
|
|
t.Fatal("admin:write should satisfy write scope")
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopePlatformAdminBypass(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
groups []string
|
|
}{
|
|
{"bare admin role", []string{"admin"}},
|
|
{"prefixed admin role", []string{"role:admin"}},
|
|
{"trimmed prefixed admin role", []string{" role:admin "}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if !HasAdminScope(tt.groups, AdminScopeRead) {
|
|
t.Fatal("platform admin should satisfy read scope")
|
|
}
|
|
if !HasAdminScope(tt.groups, AdminScopeWrite) {
|
|
t.Fatal("platform admin should satisfy write scope")
|
|
}
|
|
if !HasAdminScope(tt.groups, DefaultAdminScope) {
|
|
t.Fatal("platform admin should satisfy default admin scope")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopeNoAccess(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
groups []string
|
|
}{
|
|
{"empty groups", nil},
|
|
{"unrelated user role", []string{"role:user"}},
|
|
{"resource permission only", []string{"drive:admin"}},
|
|
{"unknown admin suffix", []string{"admin:unknown"}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if HasAdminScope(tt.groups, AdminScopeRead) {
|
|
t.Fatal("expected no admin read scope")
|
|
}
|
|
if HasAdminScope(tt.groups, AdminScopeWrite) {
|
|
t.Fatal("expected no admin write scope")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopeMaxWins(t *testing.T) {
|
|
groups := []string{GroupAdminRead, GroupAdminWrite}
|
|
|
|
if !HasAdminScope(groups, AdminScopeRead) {
|
|
t.Fatal("combined scopes should satisfy read")
|
|
}
|
|
if !HasAdminScope(groups, AdminScopeWrite) {
|
|
t.Fatal("combined scopes should satisfy write")
|
|
}
|
|
}
|
|
|
|
func TestHasAdminScopeNormalization(t *testing.T) {
|
|
groups := []string{" Admin:Write "}
|
|
|
|
if !HasAdminScope(groups, AdminScopeWrite) {
|
|
t.Fatal("admin scope groups should be case- and whitespace-normalized")
|
|
}
|
|
if !HasAdminScope(groups, AdminScopeRead) {
|
|
t.Fatal("normalized admin:write should still imply read")
|
|
}
|
|
}
|