feat: /v1/role list, get, create, update and delete. Reassign members on delete. Write-time validation (ValidateGrants, duplicates, typed keys against real content types). Name uniqueness (role_name_taken), role_locked, role_has_members, role_conflict on version mismatch. No-escalation: nobody grants a permission they don't hold, and roles.edit_owner_only guards the Owner role. Activity events for role changes. Typed-key exposure in /auth/me. Cleanup of typed rows when a content type is deleted. | Backend only. Will carry the deferred ListGrants decision.
This commit is contained in:
1 parent
a043f7f41a
commit
094df63a7f
51 files changed
+4822
-115
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -86,14 +87,14 @@ func TestContentScopeCheckPermissionUseCase(t *testing.T) {
|
||||
if _, err := check.Require(writer, "content.read"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if counting.gets != 1 || counting.getByNames != 0 || counting.listGrantsN != 0 {
|
||||
if counting.gets != 1 || counting.getByNames != 0 || counting.lists != 0 {
|
||||
t.Errorf("unexpected loads %+v", counting)
|
||||
}
|
||||
counting.gets = 0
|
||||
if _, err := check.Require(user.User{ID: 4, Role: user.RoleAdmin}, "content.read"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if counting.gets != 0 || counting.getByNames != 1 || counting.listGrantsN != 0 {
|
||||
if counting.gets != 0 || counting.getByNames != 1 || counting.lists != 0 {
|
||||
t.Errorf("unexpected legacy loads %+v", counting)
|
||||
}
|
||||
})
|
||||
@@ -120,10 +121,10 @@ func TestContentScopeGetMyAccessUseCase(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(access.Permissions) != 1 || access.Permissions[0].Key != "content.update" || access.Permissions[0].Scope != role.ScopeOwn {
|
||||
t.Errorf("unexpected permissions %+v", access.Permissions)
|
||||
if !reflect.DeepEqual(access.Permissions, grantRows("content.update=own", "content.update:blog=all")) {
|
||||
t.Errorf("unexpected permissions %+v (typed entries superseded by roles_permissions_roles_api)", access.Permissions)
|
||||
}
|
||||
if counting.gets != 1 || counting.listGrantsN != 0 {
|
||||
if counting.gets != 1 || counting.lists != 0 {
|
||||
t.Errorf("unexpected loads %+v", counting)
|
||||
}
|
||||
})
|
||||
@@ -478,7 +479,7 @@ func TestContentScopeCreateContentTypeReservesBuiltInKeys(t *testing.T) {
|
||||
for _, key := range []string{"article", "author", "site", "media", "user"} {
|
||||
t.Run(key, func(t *testing.T) {
|
||||
types := newMockContentTypeRepo()
|
||||
create := useCases.NewCreateContentTypeUseCase(types, time.Now)
|
||||
create := useCases.NewCreateContentTypeUseCase(types, newMockRoleRepo(), time.Now)
|
||||
_, err := create.Create(useCases.CreateContentTypeCommand{Key: key, Name: "Reserved", Fields: []contenttype.Field{{Key: "title", Label: "Title", Type: contenttype.TypeText}}})
|
||||
if !errors.Is(err, contenttype.ErrKeyTaken) || types.createCalls != 0 {
|
||||
t.Errorf("expected ErrKeyTaken and nothing stored, got %v (%d)", err, types.createCalls)
|
||||
|
||||
Reference in new issue
Block a user