1212// - the name is checked against the catalog the security plugin really binds
1313// (a declared position, a declared permission set, a name nobody declares);
1414// - the write goes through the engine, so the last-admin guard's ledger hook
15- // really refuses switching `admin_full_access` off;
15+ // really judges switching `admin_full_access` off: refused when no
16+ // administrator would be left, landing when one stands;
1617// - the resolver over the booted engine stops granting through a position
1718// the door switched off, for a holder of that position.
1819
@@ -36,6 +37,7 @@ const HOLDER_EMAIL = 'exec.holder@example.com';
3637interface Engine {
3738 find ( object : string , options ?: unknown ) : Promise < Array < Record < string , unknown > > > ;
3839 insert ( object : string , data : Record < string , unknown > , options ?: unknown ) : Promise < unknown > ;
40+ update ( object : string , data : Record < string , unknown > , options ?: unknown ) : Promise < unknown > ;
3941}
4042
4143describe ( 'the catalog activation door, on a booted showcase' , ( ) => {
@@ -113,13 +115,52 @@ describe('the catalog activation door, on a booted showcase', () => {
113115 expect ( rows [ 0 ] . active === true || rows [ 0 ] . active === 1 ) . toBe ( true ) ;
114116 } ) ;
115117
116- it ( 'switching `admin_full_access` off is refused 403 by the last-admin guard, and no row lands' , async ( ) => {
117- const res = await flip ( 'permission' , 'admin_full_access' , { enabled : false } ) ;
118- const body = await res . json ( ) as { error ?: { code ?: string } } ;
118+ // [ADR-0135 D5.2] The last-administrator guard refuses a ledger switch-off
119+ // of `admin_full_access` only when it would leave nobody who can sign in as
120+ // an administrator. Standing here is the seeded admin's unscoped
121+ // `admin_full_access` grant (the `single` posture declares no owner email)
122+ // plus the default organization's owner membership, which the same admin
123+ // holds. So the pair: with the owner membership demoted the grant is the
124+ // last standing and the switch-off is refused; with it standing the
125+ // switch-off lands. Every change is undone in `finally`, so a failed
126+ // assertion leaves the shared boot as it found it.
127+ it ( 'switching `admin_full_access` off with no administrator left standing is refused 403, and no row lands' , async ( ) => {
128+ const owners = await ql . find ( 'sys_member' , { where : { role : 'owner' } , context : SYSTEM_CTX } ) ;
129+ // Anti-vacuity: the boot really has an owner to demote.
130+ expect ( owners . length , 'no owner membership on this boot' ) . toBeGreaterThan ( 0 ) ;
131+ try {
132+ for ( const m of owners ) await ql . update ( 'sys_member' , { id : m . id , role : 'member' } , { context : SYSTEM_CTX } ) ;
133+
134+ const res = await flip ( 'permission' , 'admin_full_access' , { enabled : false } ) ;
135+ const body = await res . json ( ) as { error ?: { code ?: string } } ;
136+
137+ expect ( res . status ) . toBe ( 403 ) ;
138+ expect ( body . error ?. code ) . toBe ( 'PERMISSION_DENIED' ) ;
139+ expect ( await rowsOf ( 'permission' , 'admin_full_access' ) ) . toEqual ( [ ] ) ;
140+ } finally {
141+ for ( const m of owners ) await ql . update ( 'sys_member' , { id : m . id , role : 'owner' } , { context : SYSTEM_CTX } ) ;
142+ }
143+ } ) ;
119144
120- expect ( res . status ) . toBe ( 403 ) ;
121- expect ( body . error ?. code ) . toBe ( 'PERMISSION_DENIED' ) ;
122- expect ( await rowsOf ( 'permission' , 'admin_full_access' ) ) . toEqual ( [ ] ) ;
145+ it ( 'CONTROL: switching `admin_full_access` off with the organization owner still standing lands 200' , async ( ) => {
146+ try {
147+ const res = await flip ( 'permission' , 'admin_full_access' , { enabled : false } ) ;
148+ const text = await res . clone ( ) . text ( ) ;
149+
150+ expect ( res . status , text ) . toBe ( 200 ) ;
151+ const rows = await rowsOf ( 'permission' , 'admin_full_access' ) ;
152+ expect ( rows ) . toHaveLength ( 1 ) ;
153+ expect ( rows [ 0 ] . active === false || rows [ 0 ] . active === 0 ) . toBe ( true ) ;
154+ } finally {
155+ // Restored through the engine, not the door: with the set off the
156+ // caller may no longer hold `manage_metadata`. Switching it back on
157+ // is never refused by the guard.
158+ for ( const row of await rowsOf ( 'permission' , 'admin_full_access' ) ) {
159+ await ql . update ( LEDGER , { id : row . id , active : true } , { context : SYSTEM_CTX } ) ;
160+ }
161+ }
162+ const restored = await rowsOf ( 'permission' , 'admin_full_access' ) ;
163+ expect ( restored . every ( ( r ) => r . active === true || r . active === 1 ) ) . toBe ( true ) ;
123164 } ) ;
124165
125166 it ( 'a name nobody declares is 404 and writes nothing' , async ( ) => {
0 commit comments