Skip to content

Commit 29d3366

Browse files
committed
test(plugin-auth): grant fixtures carry the name the platform stores; unnamed-grant guard pins
Hand-declared grant objects in the guard suites declare the permission_set column and their seeded grants carry the name every platform writer stores. The unnamed-grant pins bypass the guard to clear a name, which the guard itself refuses as a revocation of the sole administrator's standing. Claude-Session: https://claude.ai/code/session_01WkL6Eijt432S1Y7ekb6ovQ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 30a2c86 commit 29d3366

7 files changed

Lines changed: 65 additions & 29 deletions

‎packages/plugins/plugin-auth/src/auth-plugin.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ describe('AuthPlugin', () => {
13191319
const tables: Record<string, any[]> = {
13201320
sys_permission_set: [{ id: 'ps_admin', name: 'admin_full_access' }],
13211321
sys_user_permission_set: [
1322-
{ id: 'ups1', user_id: 'u1', permission_set_id: 'ps_admin', organization_id: null },
1322+
{ id: 'ups1', user_id: 'u1', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null },
13231323
],
13241324
sys_member: [],
13251325
sys_organization: [],
@@ -1572,7 +1572,7 @@ describe('AuthPlugin', () => {
15721572
const tables: Record<string, any[]> = {
15731573
sys_permission_set: [{ id: 'ps_admin', name: 'admin_full_access' }],
15741574
sys_user_permission_set: [
1575-
{ id: 'ups1', user_id: 'admin', permission_set_id: 'ps_admin', organization_id: null },
1575+
{ id: 'ups1', user_id: 'admin', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null },
15761576
],
15771577
// The platform admin already exists; the default-org bootstrap binds it
15781578
// as `owner` on kernel:ready. A member-less seeded user is added later.
@@ -1710,7 +1710,7 @@ describe('AuthPlugin', () => {
17101710

17111711
// The admin's grant lands: the bootstrap creates the organization, and
17121712
// that moment runs the pass — no restart, no app:seeded.
1713-
ql.tables.sys_user_permission_set.push({ id: 'ups1', user_id: 'admin', permission_set_id: 'ps_admin', organization_id: null });
1713+
ql.tables.sys_user_permission_set.push({ id: 'ups1', user_id: 'admin', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null });
17141714
await fireBootstrapWrite({ object: 'sys_user_permission_set', operation: 'insert' });
17151715
await vi.waitFor(() => {
17161716
expect(ql.tables.sys_member.find((m: any) => m.user_id === 'early_u5')).toMatchObject({ role: 'member' });

‎packages/plugins/plugin-auth/src/ensure-default-organization.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function makeQl(seed: Partial<Record<string, Row[]>> = {}) {
7676
const tables: Record<string, Row[]> = {
7777
sys_permission_set: [{ id: 'ps_admin', name: 'admin_full_access' }],
7878
sys_user_permission_set: [
79-
{ id: 'ups1', user_id: 'u1', permission_set_id: 'ps_admin', organization_id: null },
79+
{ id: 'ups1', user_id: 'u1', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null },
8080
],
8181
sys_member: [],
8282
sys_organization: [],
@@ -135,8 +135,8 @@ describe('ensureDefaultOrganization (plugin-auth home)', () => {
135135
it('picks the OLDEST cross-tenant admin grant', async () => {
136136
const ql = makeQl({
137137
sys_user_permission_set: [
138-
{ id: 'b', user_id: 'u_newer', permission_set_id: 'ps_admin', organization_id: null, created_at: '2026-01-02T00:00:00Z' },
139-
{ id: 'a', user_id: 'u_older', permission_set_id: 'ps_admin', organization_id: null, created_at: '2026-01-01T00:00:00Z' },
138+
{ id: 'b', user_id: 'u_newer', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null, created_at: '2026-01-02T00:00:00Z' },
139+
{ id: 'a', user_id: 'u_older', permission_set_id: 'ps_admin', permission_set: 'admin_full_access', organization_id: null, created_at: '2026-01-01T00:00:00Z' },
140140
],
141141
});
142142
await ensureDefaultOrganization(ql);

‎packages/plugins/plugin-auth/src/grant-readers-unnamed-grant.test.ts‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ import { ensureDefaultOrganization } from './ensure-default-organization.js';
3939

4040
const SYS = { context: { isSystem: true } } as any;
4141
const NAME_HOOKS = 'plugin-security:grant-permission-set-name';
42+
const GUARD_PACKAGE = 'test.grant-readers-unnamed';
43+
44+
function registerGuard(engine: ObjectQL): void {
45+
registerLastAdminGuard(engine as unknown as LastAdminGuardEngine, {
46+
packageId: GUARD_PACKAGE,
47+
logger: { info: () => undefined, warn: () => undefined },
48+
});
49+
}
4250

4351
const engines: ObjectQL[] = [];
4452
afterEach(async () => {
@@ -51,8 +59,6 @@ afterEach(async () => {
5159

5260
interface Rig {
5361
engine: ObjectQL;
54-
/** Re-bind the plugin's name hooks after `unname` (a fresh start of the plugin's hook half). */
55-
plugin: SecurityPlugin;
5662
}
5763

5864
/** `single` posture; the first user is promoted to platform administrator — the sole administrator. */
@@ -101,10 +107,7 @@ async function soleGrantAnchoredAdmin(): Promise<Rig> {
101107
await engine.syncSchemas();
102108
await plugin.start(ctx);
103109
vi.spyOn((engine as any).logger, 'warn').mockImplementation(() => undefined);
104-
registerLastAdminGuard(engine as unknown as LastAdminGuardEngine, {
105-
packageId: 'test.grant-readers-unnamed',
106-
logger: { info: () => undefined, warn: () => undefined },
107-
});
110+
registerGuard(engine);
108111
for (const [id, createdAt] of [['usr_admin', '2025-01-01T00:00:00.000Z'], ['usr_member', '2025-03-01T00:00:00.000Z']]) {
109112
await engine.insert(
110113
'sys_user', { id, email: `${id}@un.example`, name: id, created_at: createdAt, email_verified: true, banned: false }, SYS,
@@ -115,15 +118,23 @@ async function soleGrantAnchoredAdmin(): Promise<Rig> {
115118
}
116119
const boot = await bootstrapPlatformAdmin(engine, (manifest.permissions ?? []) as any[]);
117120
expect(boot).toMatchObject({ adminPromoted: true });
118-
return { engine, plugin };
121+
return { engine };
119122
}
120123

121124
const adminGrant = async (engine: ObjectQL): Promise<any> =>
122125
((await engine.find('sys_user_permission_set', { where: { user_id: 'usr_admin' }, ...SYS })) as any[])[0];
123126

127+
/**
128+
* The state the backfill leaves on a grant it has not named. Written past both
129+
* hooks that would refuse it: the name hooks, and the guard itself — clearing
130+
* the sole administrator's grant name takes the standing away, which the guard
131+
* refuses like any other revocation. The guard is bound again afterwards.
132+
*/
124133
async function unnameAdminGrant(engine: ObjectQL): Promise<void> {
125134
(engine as any).unregisterHooksByPackage(NAME_HOOKS);
135+
(engine as any).unregisterHooksByPackage(GUARD_PACKAGE);
126136
await engine.update('sys_user_permission_set', { permission_set: null }, { where: { user_id: 'usr_admin' }, multi: true, ...SYS });
137+
registerGuard(engine);
127138
expect((await adminGrant(engine)).permission_set ?? null).toBeNull();
128139
}
129140

@@ -144,6 +155,14 @@ describe('[ADR-0131 D4] a grant that names nothing — the last-administrator gu
144155
await expect(ban(engine, 'usr_member')).rejects.toMatchObject({ code: 'PERMISSION_DENIED', status: 403 });
145156
});
146157

158+
it('clearing the sole administrator’s grant name is refused — the name is what the standing rides on', async () => {
159+
const { engine } = await soleGrantAnchoredAdmin();
160+
const grant = await adminGrant(engine);
161+
await expect(engine.update('sys_user_permission_set', { id: grant.id, permission_set: null }, SYS))
162+
.rejects.toMatchObject({ code: 'PERMISSION_DENIED', status: 403 });
163+
expect((await adminGrant(engine)).permission_set).toBe('admin_full_access');
164+
});
165+
147166
it('re-pointing the sole administrator’s grant by id is read as taking the standing away', async () => {
148167
const { engine } = await soleGrantAnchoredAdmin();
149168
const grant = await adminGrant(engine);

‎packages/plugins/plugin-auth/src/last-admin-guard.config-anchor.test.ts‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ const sysUserPermissionSet = {
112112
id: { name: 'id', type: 'text' as const, primaryKey: true },
113113
user_id: { name: 'user_id', type: 'text' as const },
114114
permission_set_id: { name: 'permission_set_id', type: 'text' as const },
115+
// [ADR-0131 D4] The grant's set BY NAME — the column the guard reads.
116+
permission_set: { name: 'permission_set', type: 'text' as const },
115117
organization_id: { name: 'organization_id', type: 'text' as const },
116118
valid_from: { name: 'valid_from', type: 'datetime' as const },
117119
valid_until: { name: 'valid_until', type: 'datetime' as const },
@@ -236,7 +238,7 @@ describe('[#11663 L2] the enumeration counts CONFIG-derived administrators', ()
236238
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
237239
await engine.insert(
238240
'sys_user_permission_set',
239-
{ id: 'ups_1', user_id: 'usr_grant', permission_set_id: 'ps_a' },
241+
{ id: 'ups_1', user_id: 'usr_grant', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
240242
SYSTEM,
241243
);
242244

@@ -311,7 +313,7 @@ describe('[#11663 L2] the FIFTH write shape is judged', () => {
311313
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
312314
await engine.insert(
313315
'sys_user_permission_set',
314-
{ id: 'ups_1', user_id: 'usr_owner', permission_set_id: 'ps_a' },
316+
{ id: 'ups_1', user_id: 'usr_owner', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
315317
SYSTEM,
316318
);
317319

@@ -403,7 +405,7 @@ describe('[#11663 L5] under a WALLED posture the legacy grant is not an administ
403405
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
404406
await engine.insert(
405407
'sys_user_permission_set',
406-
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a' },
408+
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
407409
SYSTEM,
408410
);
409411
return engine;
@@ -455,7 +457,7 @@ describe('[#11663 L5] under a WALLED posture the legacy grant is not an administ
455457
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
456458
await engine.insert(
457459
'sys_user_permission_set',
458-
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a' },
460+
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
459461
SYSTEM,
460462
);
461463

@@ -476,7 +478,7 @@ describe('[#11663 L5] under a WALLED posture the legacy grant is not an administ
476478
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
477479
await engine.insert(
478480
'sys_user_permission_set',
479-
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a' },
481+
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
480482
SYSTEM,
481483
);
482484

@@ -518,7 +520,7 @@ describe('[#11663 L5] under a WALLED posture the legacy grant is not an administ
518520
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: false }, SYSTEM);
519521
await engine.insert(
520522
'sys_user_permission_set',
521-
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a' },
523+
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
522524
SYSTEM,
523525
);
524526

@@ -537,7 +539,7 @@ describe('[#11663 L5] under a WALLED posture the legacy grant is not an administ
537539
await engine.insert('sys_permission_set', { id: 'ps_a', name: ADMIN_FULL_ACCESS, active: false }, SYSTEM);
538540
await engine.insert(
539541
'sys_user_permission_set',
540-
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a' },
542+
{ id: 'ups_1', user_id: 'usr_legacy', permission_set_id: 'ps_a', permission_set: ADMIN_FULL_ACCESS },
541543
SYSTEM,
542544
);
543545

‎packages/plugins/plugin-auth/src/last-admin-guard.re-pricing.test.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ const sysUserPermissionSet = {
8787
id: { name: 'id', type: 'text' as const, primaryKey: true },
8888
user_id: { name: 'user_id', type: 'text' as const },
8989
permission_set_id: { name: 'permission_set_id', type: 'text' as const },
90+
// [ADR-0131 D4] The grant's set BY NAME — the column the guard reads.
91+
permission_set: { name: 'permission_set', type: 'text' as const },
9092
organization_id: { name: 'organization_id', type: 'text' as const },
9193
valid_from: { name: 'valid_from', type: 'datetime' as const },
9294
valid_until: { name: 'valid_until', type: 'datetime' as const },
@@ -174,7 +176,7 @@ async function seedGrantAdmin(engine: ObjectQL, userId = 'usr_grant'): Promise<v
174176
await engine.insert('sys_permission_set', { id: 'ps_admin', name: ADMIN_FULL_ACCESS, active: true }, SYSTEM);
175177
await engine.insert(
176178
'sys_user_permission_set',
177-
{ id: 'ups_admin', user_id: userId, permission_set_id: 'ps_admin' },
179+
{ id: 'ups_admin', user_id: userId, permission_set_id: 'ps_admin', permission_set: ADMIN_FULL_ACCESS },
178180
SYSTEM,
179181
);
180182
}

‎packages/plugins/plugin-auth/src/last-admin-guard.test.ts‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ const sysUserPermissionSet = {
140140
id: { name: 'id', type: 'text' as const, primaryKey: true },
141141
user_id: { name: 'user_id', type: 'text' as const },
142142
permission_set_id: { name: 'permission_set_id', type: 'text' as const },
143+
// [ADR-0131 D4] The grant's set BY NAME — the column the guard reads.
144+
permission_set: { name: 'permission_set', type: 'text' as const },
143145
organization_id: { name: 'organization_id', type: 'text' as const },
144146
valid_from: { name: 'valid_from', type: 'datetime' as const },
145147
valid_until: { name: 'valid_until', type: 'datetime' as const },
@@ -150,6 +152,17 @@ const SYSTEM = { context: { isSystem: true } } as const;
150152
const ORG = 'org_1';
151153
const PS_ADMIN = 'ps_admin_full_access';
152154

155+
/**
156+
* [ADR-0131 D4] The name each fixture set id stands for. Every platform grant
157+
* writer stores it beside the id, so a seeded grant carries it too; `ps_gone`
158+
* is a set that was deleted after its grants were named.
159+
*/
160+
const SET_NAME_OF: Record<string, string> = {
161+
[PS_ADMIN]: ADMIN_FULL_ACCESS,
162+
ps_member: 'member_default',
163+
ps_gone: 'retired_set',
164+
};
165+
153166
/** Every ban a real deprovision performs is a system-context write. */
154167
async function ban(engine: ObjectQL, id: string): Promise<unknown> {
155168
return engine.update('sys_user', { id, banned: true }, SYSTEM);
@@ -280,11 +293,9 @@ async function seedUser(
280293
);
281294
}
282295
if (extra.platformAdmin || extra.grant) {
283-
await engine.insert(
284-
'sys_user_permission_set',
285-
{ id: `ups_${id}`, user_id: id, permission_set_id: PS_ADMIN, ...(extra.grant ?? {}) },
286-
SYSTEM,
287-
);
296+
const grant: Record<string, unknown> = { id: `ups_${id}`, user_id: id, permission_set_id: PS_ADMIN, ...(extra.grant ?? {}) };
297+
if (!('permission_set' in grant)) grant.permission_set = SET_NAME_OF[String(grant.permission_set_id)];
298+
await engine.insert('sys_user_permission_set', grant, SYSTEM);
288299
}
289300
}
290301

@@ -1916,9 +1927,9 @@ describe('[#6084] a zero-administrator reading is no longer automatically the bo
19161927
await expect(ban(engine, 'usr_platform')).rejects.toThrow(/recognises NO administrator/);
19171928
await expect(ban(engine, 'usr_platform')).rejects.toThrow(/not the bootstrap window/i);
19181929
// Holder and target are both quoted, so an operator can go find the row.
1930+
// [ADR-0131 D4] The target is the set the grant NAMES — its reference.
19191931
await expect(ban(engine, 'usr_platform')).rejects.toThrow(/'usr_platform'/);
1920-
await expect(ban(engine, 'usr_platform')).rejects.toThrow(new RegExp(PS_ADMIN));
1921-
await expect(ban(engine, 'usr_platform')).rejects.toThrow(new RegExp(ADMIN_FULL_ACCESS));
1932+
await expect(ban(engine, 'usr_platform')).rejects.toThrow(`'usr_platform' → '${ADMIN_FULL_ACCESS}'`);
19221933
await expect(ban(engine, 'usr_platform')).rejects.toThrow(/ADR-0135 D5\.2/);
19231934
});
19241935

‎packages/plugins/plugin-auth/src/scim-deactivation-reconcile-user.test.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ const sysUserPermissionSet = {
124124
id: { name: 'id', type: 'text' as const, primaryKey: true },
125125
user_id: { name: 'user_id', type: 'text' as const },
126126
permission_set_id: { name: 'permission_set_id', type: 'text' as const },
127+
// [ADR-0131 D4] The grant's set BY NAME — the column the guard reads.
128+
permission_set: { name: 'permission_set', type: 'text' as const },
127129
organization_id: { name: 'organization_id', type: 'text' as const },
128130
valid_from: { name: 'valid_from', type: 'datetime' as const },
129131
valid_until: { name: 'valid_until', type: 'datetime' as const },
@@ -349,7 +351,7 @@ async function makePlatformAdmin(h: Harness, userId: string): Promise<void> {
349351
}
350352
await h.engine.insert(
351353
'sys_user_permission_set',
352-
{ id: `ups_${userId}`, user_id: userId, permission_set_id: PS_ADMIN },
354+
{ id: `ups_${userId}`, user_id: userId, permission_set_id: PS_ADMIN, permission_set: ADMIN_FULL_ACCESS },
353355
SYSTEM,
354356
);
355357
}

0 commit comments

Comments
 (0)