Bite 32.4 — Cross‐Tenant Delegated Role Isolation - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki
Bite 32.4 — Cross-Tenant Delegated Role Isolation
Refined Manual Promotion Test Suite
Branch: 891-bite-32-journey-history-work-credit-evidence-and-cross-tenant-role-isolation
Committed HEAD SHA: 055ec8cb535238c328b1dc5e957219ac780b4334
1. Requirement under test
The refined Bite 32.4 rule is:
A Person may have Membership and Collaborator relationships in multiple Tenants.
If that Person holds any other Tenant Role in one Tenant, they are not eligible to receive any other Tenant Role in another Tenant.
Before another Tenant may provision such a Role, the Person must work with the Tenant where the existing Roles are held and have every Role other than Membership and Collaborator explicitly removed.
ERS may inform the provisioning Tenant Administrator that the Person is not eligible, but ERS must not reveal the other Tenant or the Roles held there.
The current UI exposes these non-baseline Tenant Roles:
EARNINGS_OPERATOR
EXPENSE_OPERATOR
TENANT_ADMIN
The automated suite additionally proves that an unknown/future TENANT-scoped Role is governed by the same rule.
2. Promotion rule
Promote Bite 32.4 only if all three manual tests PASS.
Promotion is blocked if any of the following occurs:
- Membership or Collaborator participation in multiple Tenants prevents legitimate Role provisioning;
- a Person holding a non-baseline Role in Tenant A can receive a non-baseline Role in Tenant B;
- removing only some of the Roles held in Tenant A makes the Person eligible in Tenant B;
- Actor deactivation or other lifecycle state is treated as equivalent to Role removal;
- removing every non-baseline Role in Tenant A fails to make the Person eligible in Tenant B;
- the provisioning Tenant learns the other Tenant's name, ID, code, or Role;
TENANT_ADMINbehaves differently from operator Roles with respect to the cross-Tenant boundary;- multiple non-baseline Roles in the same Tenant are incorrectly prohibited;
- the refined conflict explanation does not tell the administrator that the Person must work with the other Tenant to have the Roles removed;
- the new 32.4 conflict text is missing in either supported locale;
- an unexpected 401, 403, 404, 500, JavaScript exception, or application error occurs.
3. Deterministic LOCAL prerequisites
These tests use the existing deterministic Bite 30G fixture.
The fixture gives us:
Tenant A:
Byte 28A Manual Test
Tenant B:
Default Tenant
and one global Person who already participates normally in both Tenants:
Person:
Ana FinancialContinuity
Login:
[email protected]
Password:
Manual-30C-Password!
Ana has:
Tenant A:
Membership
Collaborator
Tenant Actor
Tenant B:
Membership
Collaborator
Tenant Actor
Initial non-baseline Roles:
none
That makes Ana an appropriate test subject for the refined Bite 32.4 rule.
3.1 Confirm exact HEAD
From the repository root:
git rev-parse HEAD
Expected:
055ec8cb535238c328b1dc5e957219ac780b4334
PASS
The SHA matches exactly.
BLOCK PROMOTION
Do not continue if it differs.
3.2 Create an isolated manual-test database
Stop any running LOCAL backend.
From the repository root:
rm -f \
backend/data/manual-32-4.db \
backend/data/manual-32-4.db-wal \
backend/data/manual-32-4.db-shm \
backend/data/manual-32-4.db-journal
DB_PATH=backend/data/manual-32-4.db \
./scripts/db-migrate.sh
Confirm migration completes through:
000071_cross_tenant_delegated_role_isolation.up.sql
Seed the fixture:
python3 scripts/seed-bite30g-final-manual-testdata.py \
--db-path backend/data/manual-32-4.db \
--no-backup
Do not continue if the seed reports an error.
3.3 Identities used
Tenant A Administrator
Person:
Daniela TenantOperator
Login:
[email protected]
Password:
Manual-30C-Password!
Required Tenant:
Byte 28A Manual Test
Tenant B Administrator
Person:
Elisa TenantAdministrator
Login:
[email protected]
Password:
Manual-30C-Password!
Required Tenant:
Default Tenant
Cross-Tenant Person
Person:
Ana FinancialContinuity
Login:
[email protected]
Password:
Manual-30C-Password!
Her Actor displays approximately as:
30G Identity A · Tenant A
30G Identity A · Tenant B
Application Administrator
Login:
[email protected]
Password:
admin123
Required context:
Global administration
3.4 Start LOCAL
Terminal 1:
ERS_DATABASE_PATH=data/manual-32-4.db \
make local-backend
Terminal 2:
make local-frontend
Open the normal Vite LOCAL URL.
Do not navigate by manually typing ERS application routes.
Test 01 — Membership and Collaborator are baseline; multiple Roles in one Tenant remain valid
Purpose
Prove that the refined invariant does not prohibit either:
Membership / Collaborator in multiple Tenants
or:
multiple non-baseline Roles inside one Tenant
Identity
Sign in as:
[email protected]
Manual-30C-Password!
Confirm selected Tenant:
√ Byte 28A Manual Test
Use:
English (United States)
Part A — Find Ana
-
In the normal ERS navigation, click Authorization.
-
√ Confirm the page heading is:
Tenant Authorization -
Locate:
Filter People / Actors -
Enter:
[email protected] -
Locate:
30G Identity A · Tenant A -
√ Confirm Ana shows:
Actor ACTIVE Membership ACTIVE Collaborator -
√ Confirm Ana does not display:
Role in another Tenant -
√ Confirm the Role selector is enabled.
Part B — Verify the Role selector interaction
-
√ Before clicking the Role selector, confirm no Role choice list is permanently visible.
-
Click the Role selector.
-
√ Confirm the full list opens and contains both:
EARNINGS_OPERATOR
Earnings Operator
EXPENSE_OPERATOR
Expenses Operator
- In:
Filter roles
type:
earn
-
√ Confirm the visible list filters to the Earnings Operator choice.
-
Press Escape.
-
√ Confirm the list closes.
-
Reopen the Role selector.
-
√ Confirm the complete unfiltered Role list appears again.
This verifies the established ERS combobox behavior.
Part C — Grant Earnings Operator
- Select:
EARNINGS_OPERATOR
-
Confirm the Role list closes.
-
Click:
Grant Role
- Confirm the success message:
Earnings Operator granted.
- Under:
Current Role Grants
confirm:
EARNINGS_OPERATOR
Earnings Operator
is present.
Part D — Grant Expenses Operator in the same Tenant
-
Reopen the Role selector.
-
Select:
EXPENSE_OPERATOR
- Click:
Grant Role
- √ Confirm:
Expenses Operator granted.
- √ Under Current Role Grants, confirm both are present simultaneously:
EARNINGS_OPERATOR
EXPENSE_OPERATOR
- √ Confirm the page does not show:
Role in another Tenant
Expected result
The following state is legal:
Ana
Tenant A
├── Membership
├── Collaborator
├── EARNINGS_OPERATOR
└── EXPENSE_OPERATOR
Tenant B
├── Membership
└── Collaborator
Part E — Confirm Tenant B baseline participation remains intact
-
Sign out.
-
Sign in as Ana:
[email protected]
Manual-30C-Password!
-
Open the Tenant/context selector.
-
√ Confirm both Tenants are available:
Byte 28A Manual Test
Default Tenant
- Select:
Default Tenant
-
√ Confirm the context changes successfully.
-
Navigate to People through the normal UI.
-
√ Confirm Ana can still access her own Person context.
-
Navigate to Collaborators.
-
√ Confirm Ana can still access her own Collaborator/Journey context.
-
Open the Tenant selector again.
-
Select:
Byte 28A Manual Test
- √ Confirm Ana's normal Person/Collaborator participation remains available there too.
PASS
PASS if:
- Ana starts eligible despite having Membership and Collaborator relationships in both Tenants;
- both operator Roles can coexist in Tenant A;
- Ana continues participating normally in Tenant B;
- no cross-Tenant warning appears merely because Ana belongs to or collaborates with Tenant B.
BLOCK PROMOTION
Block promotion if:
- Ana is initially ineligible simply because she has Tenant B Membership/Collaborator participation;
- the second Role in Tenant A is rejected;
- Tenant B disappears from Ana's normal context selection;
- Ana loses her Tenant B Membership/Collaborator behavior;
- ordinary baseline participation is treated as a conflicting Role.
Test 02 — Another Tenant must refuse provisioning until every non-baseline Role is removed
Dependency
Test 01 must have passed.
At the beginning of this test Ana must have:
Tenant A:
EARNINGS_OPERATOR
EXPENSE_OPERATOR
Tenant B:
no non-baseline Role
Part A — Tenant B attempts to provision Ana
-
Sign out.
-
Sign in as the Tenant B Administrator:
[email protected]
Manual-30C-Password!
- √ Confirm selected Tenant:
Default Tenant
- Select:
English (United States)
-
Navigate to Authorization.
-
In Filter People / Actors, enter:
[email protected]
- Locate:
30G Identity A · Tenant B
- √ Confirm Ana still shows normal baseline status:
Actor ACTIVE
Membership ACTIVE
Collaborator
- √ Confirm she additionally displays the badge:
Role in another Tenant
-
√ Confirm the Role selector is disabled.
-
√ Confirm it displays:
Role selection unavailable
- √ Confirm Grant Role is disabled.
Part B — Verify the refined client-facing explanation
-
Read the warning beneath the Role selector.
-
√ Confirm it says:
This Person has one or more Roles in another Tenant. They must work with that Tenant to have every Role other than Membership and Collaborator removed before a Role can be assigned here.
- √ Confirm the message clearly communicates all three concepts:
a Role exists elsewhere;
the Person must work with that Tenant;
every Role other than Membership and Collaborator must be removed.
Part C — Verify confidentiality
-
Inspect the entire Ana card.
-
Confirm it does not reveal:
Byte 28A Manual Test
-
√ Confirm it does not reveal Tenant A's internal Tenant ID or code.
-
√ Confirm it does not reveal either:
EARNINGS_OPERATOR
EXPENSE_OPERATOR
as Roles Ana holds elsewhere.
- √ Confirm it does not reveal how many other Roles she holds.
√ Expected result
Tenant B is told only:
Ana is currently ineligible because one or more non-baseline Roles exist elsewhere.
Tenant B is not told where or what those Roles are.
Part D — Verify the same explanation in Brazilian Portuguese
- Switch the locale to:
Português (Brasil)
- √ Confirm the conflict badge becomes:
Função em outro Locatário
- √ Confirm the explanatory text becomes:
Esta Pessoa possui uma ou mais Funções em outro Locatário. Ela deve trabalhar com esse Locatário para que todas as Funções, exceto Vínculo e Colaborador, sejam removidas antes que uma Função possa ser atribuída aqui.
-
√ Confirm this Portuguese message also does not reveal the other Locatário or any Role held there.
-
Return to:
English (United States)
Part E — Remove only one Role from Tenant A
-
Sign out.
-
Sign in as the Tenant A Administrator:
[email protected]
Manual-30C-Password!
- Confirm:
Byte 28A Manual Test
-
Open Authorization.
-
Search for:
[email protected]
- Under Current Role Grants, locate:
EARNINGS_OPERATOR
- Click its:
Remove
button.
- √ Confirm:
Earnings Operator revoked.
-
√ Confirm
EARNINGS_OPERATORdisappears. -
√ Confirm this Role remains:
EXPENSE_OPERATOR
Part F — Verify that partial removal is insufficient
-
Sign out.
-
Sign in again as the Tenant B Administrator.
-
Confirm:
Default Tenant
-
Open Authorization.
-
Search for Ana.
-
√ Confirm the badge still says:
Role in another Tenant
- √ Confirm the Role selector remains disabled.
43.√ Confirm Grant Role remains disabled.
- √ Confirm the same non-disclosing explanatory message remains.
√ Expected result
Removing one Role is not sufficient because Ana still holds EXPENSE_OPERATOR in Tenant A.
Part G — Remove the final non-baseline Role from Tenant A
-
Sign out.
-
Sign in as the Tenant A Administrator.
-
√ Confirm:
Byte 28A Manual Test
-
Open Authorization.
-
Search for Ana.
-
Under Current Role Grants, remove:
EXPENSE_OPERATOR
- √ Confirm:
Expenses Operator revoked.
- √ Confirm the page now says:
No current operator Role Grants.
Part H — Tenant B eligibility is now restored
-
Sign out.
-
Sign in as the Tenant B Administrator.
-
Confirm:
Default Tenant
-
Open Authorization.
-
Search for Ana.
-
√ Confirm:
Role in another Tenant
is no longer shown.
-
√ Confirm the Role selector is enabled.
-
Open the selector.
-
Select:
EXPENSE_OPERATOR
- Click:
Grant Role
- Confirm:
Expenses Operator granted.
- √ Confirm Tenant B now shows:
EXPENSE_OPERATOR
under Ana's Current Role Grants.
Part I — Confirm the boundary works in the reverse direction
-
Sign out.
-
Sign in as the Tenant A Administrator.
-
√ Confirm:
Byte 28A Manual Test
-
Open Authorization.
-
Search for Ana.
-
√ Confirm Ana is now blocked in Tenant A.
-
√ Confirm the badge reads:
Role in another Tenant
- √ Confirm neither:
Default Tenant
nor:
EXPENSE_OPERATOR
is disclosed.
Cleanup
-
Sign out.
-
Sign in as the Tenant B Administrator.
-
Open Authorization.
-
Search for Ana.
-
Remove her:
EXPENSE_OPERATOR
- √ Confirm:
No current operator Role Grants.
This cleanup is required before Test 03.
PASS
PASS if the exact sequence is:
2 Roles in Tenant A
→ Tenant B blocked
1 Role in Tenant A
→ Tenant B still blocked
0 Roles in Tenant A
→ Tenant B eligible
1 Role in Tenant B
→ Tenant A blocked
Role removed from Tenant B
→ boundary released
BLOCK PROMOTION
Block promotion if:
- Tenant B can provision Ana while either Tenant A Role remains;
- removing just one of several Roles releases Ana;
- removing all Roles does not restore eligibility;
- the warning omits the requirement to work with the other Tenant;
- the warning implies Membership or Collaborator must be removed;
- ERS exposes the other Tenant;
- ERS exposes the other Role;
- EN-US and pt-BR conflict semantics differ.
Test 03 — TENANT_ADMIN participates in exactly the same cross-Tenant Role boundary
Purpose
Prove that TENANT_ADMIN is not a special exception.
The same rule must apply:
operator Role in Tenant A
→ cannot provision TENANT_ADMIN in Tenant B
TENANT_ADMIN in Tenant B
→ cannot provision operator Role in Tenant A
Dependency
Test 02 cleanup must have completed.
Ana must have no active non-baseline Tenant Role.
Part A — Give Ana a Role in Tenant A
- Sign in as:
[email protected]
Manual-30C-Password!
- √ Confirm selected Tenant:
Byte 28A Manual Test
-
Open Authorization.
-
Search for Ana.
-
Open the Role selector.
-
Select:
EARNINGS_OPERATOR
- Click:
Grant Role
- Confirm:
Earnings Operator granted.
Part B — Inspect Ana as a Tenant Administrator candidate in Tenant B
-
Sign out.
-
Sign in as the Application Administrator:
[email protected]
admin123
- √ Confirm the current context is:
Global administration
- Through the normal administration navigation, open:
Tenants
- Locate:
Default Tenant
-
Open the Default Tenant detail page.
-
Scroll to:
Tenant Administrators
- √ Confirm the section explains:
A Person may participate through Membership and Collaborator relationships in multiple Tenants, but may hold any other Tenant Role in only one Tenant at a time.
- Confirm it also explains:
Before assigning a Role here, the Person must work with the Tenant where those Roles are held to have them removed.
- √ Confirm Default Tenant currently has one administrator slot occupied:
1 of 2 Tenant Administrator slots is occupied.
- Locate:
Unavailable administrator candidates
- √ Locate Ana's Tenant B Actor.
√ Expected display:
30G Identity A · Tenant B
-
√ Confirm Ana is unavailable.
-
√ Confirm the reason is:
This Person has one or more Roles in another Tenant. They must work with that Tenant to have every Role other than Membership and Collaborator removed before a Role can be assigned here.
- Open:
Select an eligible active actor
- √ Confirm Ana is not offered as an eligible candidate.
Part C — Verify Tenant Administrator candidate confidentiality
-
Inspect the unavailable-candidate message.
-
√ Confirm it does not identify:
Byte 28A Manual Test
-
√ Confirm it does not identify Tenant A's ID or code.
-
√ Confirm it does not identify:
EARNINGS_OPERATOR
- √ Confirm the Application Administrator learns only that Ana currently has one or more Roles elsewhere and must arrange their removal.
Part D — Remove Tenant A Role
-
Sign out.
-
Sign in as the Tenant A Administrator.
-
Open Authorization.
-
Search for Ana.
-
Remove:
EARNINGS_OPERATOR
- √ Confirm no current operator Role Grant remains.
Part E — Ana becomes eligible for TENANT_ADMIN in Tenant B
-
Sign out.
-
Sign in as the Application Administrator.
-
Return through the normal UI to:
Tenants
→ Default Tenant
→ Tenant Administrators
-
√ Confirm Ana is no longer listed as unavailable because of a Role in another Tenant.
-
Open:
Select an eligible active actor
-
√ Confirm Ana is now selectable.
-
Select Ana.
-
Click:
Assign Admin
-
Confirm the assignment succeeds.
-
If a success dialog appears, click:
Continue
- √ Confirm Default Tenant now has:
2 of 2 assignments
- √ Confirm Ana appears as an assigned Tenant Administrator.
Part F — Tenant B TENANT_ADMIN must now block Tenant A provisioning
-
Sign out.
-
Sign in as the Tenant A Administrator.
-
√ Confirm:
Byte 28A Manual Test
-
Open Authorization.
-
Search for Ana.
-
√ Confirm:
Role in another Tenant
is displayed.
-
√ Confirm the Role selector is disabled.
-
√ Confirm Grant Role is disabled.
-
√ Confirm the warning does not reveal:
Default Tenant
- √ Confirm it does not reveal:
TENANT_ADMIN
√ Expected result
√ TENANT_ADMIN behaves exactly like every other non-baseline Tenant Role for cross-Tenant eligibility purposes.
Cleanup
-
Sign out.
-
Sign in as the Application Administrator.
-
Navigate to:
Tenants
→ Default Tenant
→ Tenant Administrators
-
Locate Ana.
-
Click:
Revoke
-
Complete the success dialog if displayed.
-
√ Confirm Default Tenant returns to:
1 of 2 assignments
- √ Confirm Ana is no longer assigned as a Tenant Administrator.
PASS
PASS if:
Tenant A EARNINGS_OPERATOR
→ Tenant B TENANT_ADMIN unavailable
Tenant A Role removed
→ Tenant B TENANT_ADMIN available
Tenant B TENANT_ADMIN assigned
→ Tenant A operator Role unavailable
Tenant B TENANT_ADMIN revoked
→ cross-Tenant Role boundary released
BLOCK PROMOTION
Block promotion if:
TENANT_ADMINbypasses the rule;- an operator Role bypasses the rule when
TENANT_ADMINalready exists elsewhere; - removing the original Role fails to restore eligibility;
- the candidate explanation reveals the other Tenant or Role;
- Membership or Collaborator relationships are treated as conflicting Roles.
4. Cases that do not require additional manual testing
Future/unlisted Tenant Roles
No manual synthetic Role test is required.
The current UI exposes only:
EARNINGS_OPERATOR
EXPENSE_OPERATOR
TENANT_ADMIN
The committed automated backend and migration tests create an unenumerated future TENANT-scoped Role and prove that it is automatically governed by the same invariant.
This is stronger than adding test-only Role data manually.
Lifecycle suspension
No manual lifecycle-suspension test is required.
Automated coverage proves:
active Role Grant
+
lifecycle_suspended = true
still reserves the Person to that Tenant.
The Person becomes cross-Tenant eligible only after explicit Role Grant revocation.
Direct SQL bypass
No manual SQL test is required.
Migration 000071 already has deterministic coverage for:
cross-Tenant INSERT
cross-Tenant UPDATE/reactivation
existing conflicting data during migration
5. DevTools / API / SQL
None required.
All promotion acceptance steps above are performed through the normal ERS UI.
The only command-line operations are:
fixture setup
migration
backend startup
frontend startup
6. Final promotion checklist
[x] Exact ZIP/SHA verified
[x] make local-check passes — reported by tester
[x] Feature-branch CI passes — reported by tester
[ ] Test 01 — baseline multi-Tenant participation + same-Tenant multi-role — PASS
[ ] Test 02 — all Roles must be removed before another Tenant can provision — PASS
[ ] Test 03 — TENANT_ADMIN follows the same cross-Tenant boundary — PASS
If all three tests PASS and no new authorization, confidentiality, I18N, or usability defect is observed, Bite 32.4 — Cross-Tenant Delegated Role Isolation is ready for promotion.
7. Promotion-critical business statement
Before promotion, the observed application behavior should be explainable to a prospective client in exactly these terms:
A Person can be a Member and Collaborator of multiple Tenants. However, once a Tenant gives that Person any additional Role, another Tenant cannot give the Person another Role until the Person works with the first Tenant to have all of those additional Roles removed. ERS tells the second Tenant that the Person is not currently eligible, but ERS does not reveal which other Tenant the Person works with or which Roles they hold there.