Persisted Actor Role Operating Model - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki
- Confirm the bootstrap administrator still works
Start the application and open:
http://localhost:5173/admin/authorization
Confirm:
The page loads without an authorization error. bootstrap-admin appears as an active persisted actor. It has the APPLICATION_ADMIN role with tenant scope *. The page identifies it as the currently operating actor. Effective permissions include *. The current actor cannot deactivate itself.
Also call:
curl -sS
-H "X-Actor-ID: bootstrap-admin"
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/authz/current-actor | python3 -m json.tool
Expected:
HTTP 200 actor key bootstrap-admin active actor role APPLICATION_ADMIN effective scope * 2. Confirm header-supplied wildcard permissions no longer bypass persisted actors
Use an actor that does not exist:
curl -sS -i
-H "X-Actor-ID: bite27b-fake-admin"
-H "X-Tenant-ID: default"
-H "X-Actor-Permissions: *"
http://localhost:8080/api/v1/authz/current-actor
Expected:
HTTP 401
The response should identify the actor as missing, unknown, or inactive.
This is the most important Bite 27B test. X-Actor-Permissions: * must not turn an unknown actor into an administrator.
- Confirm the frontend no longer sends wildcard permissions
Open the browser developer tools:
Select the Network tab. Reload the Authorization page. Inspect requests to /api/v1/....
Expected request headers:
X-Actor-ID: bootstrap-admin X-Tenant-ID: default
The request must not contain:
X-Actor-Permissions: *
Repeat this check on:
/admin/reference-data /expenses /work-periods /receipts/outstanding 4. Create a temporary tenant administrator
In the Authorization page, create an actor such as:
Actor key: bite27b-tenant-admin Display name: Bite 27B Tenant Admin Active: Yes
Grant:
Role: TENANT_ADMIN Tenant: default
Confirm the actor appears active and its effective role and tenant scope are shown correctly.
Switch the operating actor to bite27b-tenant-admin.
Expected:
/api/v1/authz/current-actor identifies the new actor. Tenant scope is default. It can open tenant administrative screens. It does not gain global * scope. It cannot administer another tenant through a forged X-Tenant-ID.
Return to bootstrap-admin after this test.
- Test an operational role
Create:
Actor key: bite27b-expense-operator Display name: Bite 27B Expense Operator
Grant:
Role: EXPENSE_OPERATOR Tenant: default
Switch to that actor.
Expected allowed behavior:
Open the New Expense page. Read collaborators needed for expense preparation. Read Reference Data required by the expense form. Read the Current Account information allowed to the Expenses role. Create an expense, using disposable local test data.
Expected denied behavior:
Cannot open or operate the Authorization administration page. Cannot grant or revoke roles. Cannot update second-person approval policy. Cannot close a Journey. Cannot perform unrestricted sensitive settlement operations.
A denied action should return 403, not 401, because this is a valid persisted actor with insufficient permission.
- Test actor deactivation
Return to bootstrap-admin.
Deactivate bite27b-expense-operator.
Then call:
curl -sS -i
-H "X-Actor-ID: bite27b-expense-operator"
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/authz/current-actor
Expected:
HTTP 401
Also confirm that switching to the inactive actor is no longer offered in the UI.
Reactivate the actor and repeat the request.
Expected after reactivation:
HTTP 200
Its original persisted role grant should remain effective unless you explicitly removed the grant.
- Test self-lockout protection
Switch to bite27b-tenant-admin.
Attempt to:
deactivate bite27b-tenant-admin; revoke its own active role grant.
Expected:
The operation is rejected. The actor remains active. Its role grant remains present. The response is a validation or conflict error, normally 400 or 409, rather than an unexpected 500.
Return to bootstrap-admin.
- Test last Application Administrator protection
Create a second application administrator:
Actor key: bite27b-backup-app-admin Role: APPLICATION_ADMIN Tenant scope: *
Confirm it can be selected as the operating actor and has global scope.
While operating as bootstrap-admin, test that the backup administrator can be deactivated and reactivated because another active Application Administrator remains.
Then ensure there is only one active Application Administrator and attempt to:
deactivate that final active Application Administrator; revoke its global APPLICATION_ADMIN grant.
Expected:
Both operations are rejected. ERS retains at least one active persisted Application Administrator. No administrator lockout occurs.
Leave both administrators active when the test is complete.
- Test role-scope validation
Attempt these invalid grants through the Authorization UI.
Invalid Application Administrator scope Role: APPLICATION_ADMIN Tenant: default
Expected: rejected because Application Administrator requires scope *.
Invalid tenant role scope Role: TENANT_ADMIN Tenant: *
Expected: rejected because tenant roles require a specific tenant.
Repeat with an operational tenant role, such as:
EXPENSE_OPERATOR + tenant *
Expected: rejected.
Then verify these valid grants:
APPLICATION_ADMIN + * TENANT_ADMIN + default EXPENSE_OPERATOR + default PERSON + default 10. Confirm current-actor permission reporting
For each test actor, call:
curl -sS
-H "X-Actor-ID: "
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/authz/current-actor | python3 -m json.tool
Confirm the response accurately reports:
actor key; persisted actor ID; active roles; selected tenant; effective scope; effective permissions; linked Person or Collaborator IDs, when present.
Permissions should come from persisted grants, not from caller-provided permission headers.
- Confirm malformed and missing identities fail safely
Missing actor:
curl -sS -i
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/tenants/current
Expected: 401.
Unknown actor:
curl -sS -i
-H "X-Actor-ID: does-not-exist"
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/tenants/current
Expected: 401.
Active actor without permission:
curl -sS -i
-H "X-Actor-ID: bite27b-expense-operator"
-H "X-Tenant-ID: default"
http://localhost:8080/api/v1/authz/actors
Expected: 403.
- Confirm health checks remain public curl -sS -i http://localhost:8080/api/v1/healthz curl -sS -i http://localhost:8080/healthz
Expected: 200 without authorization headers.
- Final browser regression pass
As bootstrap-admin, confirm these pages still load and function:
/admin/authorization /admin/reference-data /admin/current-account-settings /admin/audit-logs /people /collaborators /work-periods /expenses /receipts/outstanding
Also complete one harmless normal workflow:
Open a collaborator. Open Current Account. Open an existing receipt. Open Work Period planning. Open New Expense without submitting a permanent test expense.