1515 * let a write take the deployment's last platform administrator — the
1616 * fail-open this file pins shut.
1717 *
18- * Every case runs a REAL {@link ObjectQL} engine over a real driver
19- * (better-sqlite3 `:memory:`, or driver-memory), registers the real guard, and
20- * asks the real resolver. Three things are pinned:
18+ * Every case runs a REAL {@link ObjectQL} engine over better-sqlite3
19+ * `:memory:`, registers the real guard, and asks the real resolver. Three
20+ * things are pinned:
2121 *
2222 * 1. **Where the row can exist.** Through the engine, an organization-less
2323 * grant is derived or refused wherever the organization object is
2424 * registered (ADR-0131 D9), and refused by the NOT NULL column a fresh SQL
25- * table carries (D1) where it is not. Two compositions can still hold one:
26- * a lean embedding on driver-memory, and a store built before 18.x.
27- * 2. **The negative pin** — in both, with a verified `OS_PLATFORM_OWNER_EMAIL`
25+ * table carries (D1) where it is not. What can still hold one is a column
26+ * that is NOT NULL nowhere: a store built before 18.x, or a driver that
27+ * enforces no NOT NULL (driver-memory, measured on #24883), in a lean
28+ * embedding that registers no organization object or beneath the engine.
29+ * The nullable column stands in for both here.
30+ * 2. **The negative pin** — in both shapes, with a verified `OS_PLATFORM_OWNER_EMAIL`
2831 * account as the resolver's only platform administrator and an
2932 * organization-less grant holder beside it, banning that administrator is
3033 * REFUSED.
3639import { describe , it , expect , beforeEach , afterEach } from 'vitest' ;
3740import { ObjectQL } from '@objectstack/objectql' ;
3841import { SqlDriver } from '@objectstack/driver-sql' ;
39- import { InMemoryDriver } from '@objectstack/driver-memory' ;
4042import { resetPlatformAdminEmailMemo , resolveAuthzContext } from '@objectstack/core' ;
4143import { ADMIN_FULL_ACCESS } from '@objectstack/spec/identity' ;
4244
@@ -93,7 +95,6 @@ const sysUserPosition = {
9395 fields : { id : { ...text ( 'id' ) , primaryKey : true } , user_id : text ( 'user_id' ) , position : text ( 'position' ) , organization_id : text ( 'organization_id' ) } ,
9496} ;
9597
96- type DriverKind = 'sqlite' | 'memory' ;
9798interface Rig {
9899 engine : ObjectQL ;
99100 /** The driver itself, for the rows only a store built before 18.x holds. */
@@ -122,19 +123,19 @@ afterEach(async () => {
122123 }
123124} ) ;
124125
125- async function boot ( kind : DriverKind , opts : { organizationObject : boolean ; legacyStore ?: boolean } ) : Promise < Rig > {
126+ async function boot ( opts : { organizationObject : boolean ; nullableTenantColumn ?: boolean } ) : Promise < Rig > {
126127 const engine = new ObjectQL ( ) ;
127128 engines . push ( engine ) ;
128- const driver = kind === 'sqlite'
129- ? new SqlDriver ( { client : 'better-sqlite3' , connection : { filename : ':memory:' } , useNullAsDefault : true } )
130- : new InMemoryDriver ( ) ;
131- engine . registerDriver ( driver as never , true ) ;
129+ const driver = new SqlDriver ( { client : 'better-sqlite3' , connection : { filename : ':memory:' } , useNullAsDefault : true } ) ;
130+ engine . registerDriver ( driver , true ) ;
132131 await engine . init ( ) ;
133132 const objects = [ sysUser , sysMember , sysUserPermissionSet , sysUserPosition , ...( opts . organizationObject ? [ sysOrganization ] : [ ] ) ] ;
134133 for ( const o of objects ) engine . registry . registerObject ( o as never ) ;
135134 await engine . syncSchemas ( ) ;
136- // A store built before 18.x: the tenant column nullable (ADR-0131 D1).
137- if ( opts . legacyStore ) await relaxEveryRegisteredTenantColumn ( engine ) ;
135+ // A tenant column that is NOT NULL nowhere: a store built before 18.x has one
136+ // (ADR-0131 D1 makes a fresh one NOT NULL), and so does a driver that
137+ // enforces no NOT NULL.
138+ if ( opts . nullableTenantColumn ) await relaxEveryRegisteredTenantColumn ( engine ) ;
138139 bindTestSecurityCatalog ( engine , { permissions : [ { name : ADMIN_FULL_ACCESS } ] } ) ;
139140 registerLastAdminGuard ( engine as unknown as LastAdminGuardEngine , { packageId : 'test.last-admin-guard-parity' } ) ;
140141 return { engine, driver : driver as unknown as Rig [ 'driver' ] } ;
@@ -173,22 +174,29 @@ async function refusalOf(write: Promise<unknown>): Promise<{ code?: unknown; sta
173174
174175describe ( '[ADR-0131 D1/D9] where an organization-less admin_full_access grant can still be written' , ( ) => {
175176 type Expect = 'derived' | 'refused-d9' | 'refused-not-null' | 'stored-null' ;
176- const cases : Array < { kind : DriverKind ; organizations : number | 'no-object' ; posture : 'single' | 'isolated' ; expected : Expect } > = [
177- { kind : 'sqlite' , organizations : 'no-object' , posture : 'single' , expected : 'refused-not-null' } ,
178- { kind : 'sqlite' , organizations : 0 , posture : 'single' , expected : 'refused-d9' } ,
179- { kind : 'sqlite' , organizations : 1 , posture : 'single' , expected : 'derived' } ,
180- { kind : 'sqlite' , organizations : 2 , posture : 'single' , expected : 'refused-d9' } ,
181- { kind : 'sqlite' , organizations : 0 , posture : 'isolated' , expected : 'refused-d9' } ,
182- { kind : 'sqlite' , organizations : 1 , posture : 'isolated' , expected : 'refused-d9' } ,
183- { kind : 'sqlite' , organizations : 2 , posture : 'isolated' , expected : 'refused-d9' } ,
184- { kind : 'memory' , organizations : 'no-object' , posture : 'single' , expected : 'stored-null' } ,
185- { kind : 'memory' , organizations : 0 , posture : 'single' , expected : 'refused-d9' } ,
177+ type Column = 'fresh' | 'nullable' ;
178+ const cases : Array < { column : Column ; organizations : number | 'no-object' ; posture : 'single' | 'isolated' ; expected : Expect } > = [
179+ { column : 'fresh' , organizations : 'no-object' , posture : 'single' , expected : 'refused-not-null' } ,
180+ { column : 'fresh' , organizations : 0 , posture : 'single' , expected : 'refused-d9' } ,
181+ { column : 'fresh' , organizations : 1 , posture : 'single' , expected : 'derived' } ,
182+ { column : 'fresh' , organizations : 2 , posture : 'single' , expected : 'refused-d9' } ,
183+ { column : 'fresh' , organizations : 0 , posture : 'isolated' , expected : 'refused-d9' } ,
184+ { column : 'fresh' , organizations : 1 , posture : 'isolated' , expected : 'refused-d9' } ,
185+ { column : 'fresh' , organizations : 2 , posture : 'isolated' , expected : 'refused-d9' } ,
186+ // The engine's no-organization-object exit (ADR-0131 D9) leaves the row
187+ // organization-less; only the column could refuse it.
188+ { column : 'nullable' , organizations : 'no-object' , posture : 'single' , expected : 'stored-null' } ,
189+ { column : 'nullable' , organizations : 1 , posture : 'single' , expected : 'derived' } ,
190+ { column : 'nullable' , organizations : 1 , posture : 'isolated' , expected : 'refused-d9' } ,
186191 ] ;
187192
188193 for ( const c of cases ) {
189- it ( `${ c . kind } , ${ c . organizations === 'no-object' ? 'no organization object' : `${ c . organizations } organization(s)` } , ${ c . posture } : ${ c . expected } — and the guard agrees with the resolver about its holder` , async ( ) => {
194+ it ( `${ c . column } tenant column , ${ c . organizations === 'no-object' ? 'no organization object' : `${ c . organizations } organization(s)` } , ${ c . posture } : ${ c . expected } — and the guard agrees with the resolver about its holder` , async ( ) => {
190195 process . env . OS_TENANCY_POSTURE = c . posture ;
191- const { engine, driver } = await boot ( c . kind , { organizationObject : c . organizations !== 'no-object' } ) ;
196+ const { engine, driver } = await boot ( {
197+ organizationObject : c . organizations !== 'no-object' ,
198+ nullableTenantColumn : c . column === 'nullable' ,
199+ } ) ;
192200 const organizations = typeof c . organizations === 'number' ? c . organizations : 0 ;
193201 // Written beneath the engine, before anything has asked it about
194202 // organizations: its probe reads the table at the grant insert below.
@@ -221,11 +229,6 @@ describe('[ADR-0131 D1/D9] where an organization-less admin_full_access grant ca
221229 expect ( banRefusal ?. code ) . toBe ( isAdmin ? 'PERMISSION_DENIED' : undefined ) ;
222230 } ) ;
223231 }
224-
225- it ( 'driver-memory refuses a walled posture outright — no walled composition holds the row there' , async ( ) => {
226- process . env . OS_TENANCY_POSTURE = 'isolated' ;
227- await expect ( boot ( 'memory' , { organizationObject : true } ) ) . rejects . toMatchObject ( { code : 'MEMORY_MULTI_TENANT_UNSUPPORTED' } ) ;
228- } ) ;
229232} ) ;
230233
231234describe ( '[ADR-0131 D5] the negative pin: an organization-less holder is no survivor' , ( ) => {
@@ -244,9 +247,9 @@ describe('[ADR-0131 D5] the negative pin: an organization-less holder is no surv
244247 expect ( await resolverStanding ( rig . engine , 'usr_legacy' ) , 'the holder is no administrator to the resolver' ) . toBe ( false ) ;
245248 }
246249
247- it ( '⭐ a lean embedding on driver-memory (no sys_organization): banning the only platform administrator is REFUSED' , async ( ) => {
250+ it ( '⭐ a lean embedding (no sys_organization) whose tenant column is NOT NULL nowhere : banning the only platform administrator is REFUSED' , async ( ) => {
248251 process . env . OS_TENANCY_POSTURE = 'single' ;
249- const rig = await boot ( 'memory' , { organizationObject : false } ) ;
252+ const rig = await boot ( { organizationObject : false , nullableTenantColumn : true } ) ;
250253 await seedTwoCandidates ( rig , false ) ;
251254
252255 await expect ( ban ( rig . engine , 'usr_owner' ) ) . rejects . toMatchObject ( { code : 'PERMISSION_DENIED' , status : 403 } ) ;
@@ -256,7 +259,7 @@ describe('[ADR-0131 D5] the negative pin: an organization-less holder is no surv
256259
257260 it ( '⭐ a store built before 18.x (sys_organization registered, the row beneath the engine): the same ban is REFUSED' , async ( ) => {
258261 process . env . OS_TENANCY_POSTURE = 'single' ;
259- const rig = await boot ( 'sqlite' , { organizationObject : true , legacyStore : true } ) ;
262+ const rig = await boot ( { organizationObject : true , nullableTenantColumn : true } ) ;
260263 await rig . driver . create ( 'sys_organization' , { id : DEFAULT_ORG , name : 'Default' , slug : 'default' } ) ;
261264 await seedTwoCandidates ( rig , true ) ;
262265
@@ -271,7 +274,7 @@ describe('[ADR-0131 D5] the positive control: the Default Organization\'s grant
271274 process . env . OS_TENANCY_POSTURE = 'single' ;
272275 process . env . OS_PLATFORM_OWNER_EMAIL = OWNER ;
273276 resetPlatformAdminEmailMemo ( ) ;
274- const { engine } = await boot ( 'sqlite' , { organizationObject : true } ) ;
277+ const { engine } = await boot ( { organizationObject : true } ) ;
275278 await engine . insert ( 'sys_organization' , { id : DEFAULT_ORG , name : 'Default Organization' , slug : 'default' } , SYSTEM ) ;
276279 await seedUser ( engine , 'usr_promoted' , 'promoted@corp.example' ) ;
277280 await engine . insert (
0 commit comments