Skip to content

Commit 295745e

Browse files
committed
wip(plugin-security): granted_by writer measurement rig
Claude-Session: https://claude.ai/code/session_01WkL6Eijt432S1Y7ekb6ovQ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3b49318 commit 295745e

1 file changed

Lines changed: 222 additions & 0 deletions

File tree

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* MEASUREMENT RIG (temporary shape) — what `granted_by` stores per writer case,
5+
* over a real `ObjectQL` engine and a real SQL driver with the real
6+
* `SecurityPlugin` middleware (and so the real `DelegatedAdminGate`) in front.
7+
*/
8+
9+
import { describe, it, expect, vi, afterEach } from 'vitest';
10+
import { ObjectQL } from '@objectstack/objectql';
11+
import { SqlDriver } from '@objectstack/driver-sql';
12+
import { PermissionSetSchema } from '@objectstack/spec/security';
13+
14+
import { SecurityPlugin } from './security-plugin.js';
15+
import { createInvitationPlacementService } from './invitation-placement.js';
16+
import { reconcileOrgAdminGrant } from './auto-org-admin-grant.js';
17+
import {
18+
SysPermissionSet,
19+
SysPosition,
20+
SysPositionPermissionSet,
21+
SysUserPermissionSet,
22+
SysUserPosition,
23+
} from './objects/index.js';
24+
25+
const SYS = { context: { isSystem: true } } as never;
26+
27+
const pk = { name: 'id', type: 'text' as const, primaryKey: true };
28+
const text = (name: string) => ({ name, type: 'text' as const });
29+
const inline = (name: string, fields: string[]) => ({
30+
name,
31+
label: name,
32+
fields: { id: pk, ...Object.fromEntries(fields.map((f) => [f, text(f)])) },
33+
});
34+
35+
const TENANT_ADMIN = PermissionSetSchema.parse({
36+
name: 'qa_tenant_admin',
37+
objects: {
38+
'*': {
39+
allowRead: true, allowCreate: true, allowEdit: true, allowDelete: true,
40+
viewAllRecords: true, modifyAllRecords: true,
41+
},
42+
},
43+
});
44+
45+
const CRUD = { allowRead: true, allowCreate: true, allowEdit: true, allowDelete: true, viewAllRecords: true };
46+
const DELEGATE = PermissionSetSchema.parse({
47+
name: 'qa_delegate',
48+
objects: {
49+
sys_user_permission_set: CRUD,
50+
sys_user_position: CRUD,
51+
sys_permission_set: { allowRead: true, viewAllRecords: true },
52+
sys_position: { allowRead: true, viewAllRecords: true },
53+
sys_user: { allowRead: true, viewAllRecords: true },
54+
sys_business_unit: { allowRead: true, viewAllRecords: true },
55+
},
56+
adminScope: {
57+
businessUnit: 'east',
58+
includeSubtree: true,
59+
manageAssignments: true,
60+
manageBindings: false,
61+
authorEnvironmentSets: false,
62+
assignablePermissionSets: ['qa_plain'],
63+
},
64+
});
65+
66+
const ADMIN = 'usr_admin';
67+
const DELEGATE_USER = 'usr_delegate';
68+
const TARGET = 'usr_target';
69+
const FORGED = 'usr_forged';
70+
const ISSUER = 'usr_issuer';
71+
72+
const engines: ObjectQL[] = [];
73+
afterEach(async () => {
74+
while (engines.length) {
75+
try { await engines.pop()?.destroy(); } catch { /* noop */ }
76+
}
77+
});
78+
79+
async function boot() {
80+
const engine = new ObjectQL();
81+
engine.registerDriver(
82+
new SqlDriver({ client: 'better-sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true }) as never,
83+
true,
84+
);
85+
await engine.init();
86+
engine.registerApp({
87+
id: 'com.objectstack.qa.granted-by-writer-provenance',
88+
name: 'granted_by writer provenance',
89+
version: '1.0.0',
90+
type: 'plugin',
91+
scope: 'system',
92+
objects: [
93+
inline('sys_user', ['name', 'primary_business_unit_id']),
94+
inline('sys_organization', ['name']),
95+
inline('sys_business_unit', ['name', 'parent_business_unit_id', 'organization_id']),
96+
inline('sys_business_unit_member', ['user_id', 'business_unit_id', 'organization_id']),
97+
inline('sys_member', ['user_id', 'organization_id', 'role', 'created_at']),
98+
SysPermissionSet,
99+
SysPosition,
100+
SysPositionPermissionSet,
101+
SysUserPermissionSet,
102+
SysUserPosition,
103+
],
104+
} as never);
105+
await engine.syncSchemas();
106+
engines.push(engine);
107+
108+
await engine.insert('sys_user', [ADMIN, DELEGATE_USER, TARGET, FORGED, ISSUER].map((id) => ({ id, name: id })), SYS);
109+
await engine.insert('sys_business_unit', [
110+
{ id: 'bu_hq', name: 'hq', parent_business_unit_id: null },
111+
{ id: 'bu_east', name: 'east', parent_business_unit_id: 'bu_hq' },
112+
], SYS);
113+
await engine.insert('sys_business_unit_member', [
114+
{ id: 'bum_target', user_id: TARGET, business_unit_id: 'bu_east' },
115+
], SYS);
116+
await engine.insert('sys_permission_set', [
117+
{ id: 'ps_plain', name: 'qa_plain', label: 'Plain', active: true },
118+
], SYS);
119+
await engine.insert('sys_position', [{ id: 'pos_rep', name: 'qa_rep', label: 'Rep', active: true }], SYS);
120+
await engine.insert('sys_position_permission_set', [
121+
{ id: 'pps_rep', position_id: 'pos_rep', permission_set_id: 'ps_plain' },
122+
], SYS);
123+
124+
const registered: Record<string, unknown> = {};
125+
const services: Record<string, unknown> = {
126+
manifest: { register: vi.fn() },
127+
objectql: engine,
128+
metadata: {
129+
get: async (_type: string, name: string) => engine.getSchema(name) ?? null,
130+
list: async () => [TENANT_ADMIN, DELEGATE],
131+
},
132+
};
133+
const ctx = {
134+
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
135+
registerService: vi.fn((name: string, svc: unknown) => { registered[name] = svc; }),
136+
hook: () => undefined,
137+
getService: (name: string) => {
138+
if (!(name in services)) throw new Error(`service not registered: ${name}`);
139+
return services[name];
140+
},
141+
};
142+
const plugin = new SecurityPlugin({ fallbackPermissionSet: 'qa_tenant_admin' });
143+
await plugin.init(ctx as never);
144+
await plugin.start(ctx as never);
145+
return { engine, registered, plugin };
146+
}
147+
148+
const asAdmin = { userId: ADMIN, positions: [], permissions: ['qa_tenant_admin'] };
149+
const asDelegate = { userId: DELEGATE_USER, positions: [], permissions: ['qa_delegate'] };
150+
151+
const grantRow = (extra: Record<string, unknown> = {}) => ({
152+
user_id: TARGET, permission_set_id: 'ps_plain', ...extra,
153+
});
154+
const assignmentRow = (extra: Record<string, unknown> = {}) => ({
155+
user_id: TARGET, position: 'qa_rep', business_unit_id: 'bu_east', ...extra,
156+
});
157+
158+
async function stored(engine: ObjectQL, object: string, id: unknown) {
159+
return (await engine.findOne(object, { where: { id }, context: { isSystem: true } } as never)) as any;
160+
}
161+
162+
async function attempt(p: Promise<any>) {
163+
return p.then((r) => ({ ok: true as const, r }), (e) => ({ ok: false as const, code: e?.code, msg: String(e?.message) }));
164+
}
165+
166+
describe('MEASURE granted_by per writer case', () => {
167+
for (const [object, row] of [
168+
['sys_user_permission_set', grantRow],
169+
['sys_user_position', assignmentRow],
170+
] as const) {
171+
for (const [who, ctx] of [['tenant-admin', asAdmin], ['delegate', asDelegate]] as const) {
172+
for (const explicit of [true, false]) {
173+
it(`${object} · ${who} · ${explicit ? 'explicit granted_by' : 'no granted_by'}`, async () => {
174+
const { engine } = await boot();
175+
const out = await attempt(engine.insert(object, row(explicit ? { granted_by: FORGED } : {}), { context: { ...ctx } } as never));
176+
const s = out.ok ? await stored(engine, object, out.r?.id) : null;
177+
console.log(`MEASURE ${object} ${who} explicit=${explicit} ->`, JSON.stringify(out.ok ? { granted_by: s?.granted_by ?? null } : out));
178+
});
179+
}
180+
}
181+
182+
it(`${object} · system insert with a value`, async () => {
183+
const { engine } = await boot();
184+
const out = await attempt(engine.insert(object, row({ granted_by: FORGED }), SYS));
185+
const s = out.ok ? await stored(engine, object, out.r?.id) : null;
186+
console.log(`MEASURE ${object} system explicit=true ->`, JSON.stringify(out.ok ? { granted_by: s?.granted_by ?? null } : out));
187+
});
188+
189+
for (const [who, ctx] of [['tenant-admin', asAdmin], ['delegate', asDelegate]] as const) {
190+
it(`${object} · ${who} · UPDATE granted_by`, async () => {
191+
const { engine } = await boot();
192+
const created = await engine.insert(object, row({ granted_by: ISSUER }), SYS);
193+
const out = await attempt(engine.update(object, { granted_by: FORGED }, { where: { id: (created as any).id }, context: { ...ctx } } as never));
194+
const s = await stored(engine, object, (created as any).id);
195+
console.log(`MEASURE ${object} ${who} UPDATE ->`, JSON.stringify({ outcome: out.ok ? 'landed' : out, granted_by: s?.granted_by ?? null }));
196+
});
197+
}
198+
}
199+
200+
it('invitation placement (system) apply', async () => {
201+
const { engine, registered } = await boot();
202+
const svc: any = Object.values(registered).find((s: any) => s && typeof s.apply === 'function');
203+
const res = await svc.apply({ intent: { businessUnitId: 'bu_east', positions: ['qa_rep'] }, userId: TARGET, grantedBy: ISSUER });
204+
const rows = await engine.find('sys_user_position', { where: { user_id: TARGET }, context: { isSystem: true } } as never) as any[];
205+
console.log('MEASURE invitation-placement ->', JSON.stringify({ res, granted_by: rows.map((r) => r.granted_by) }));
206+
});
207+
208+
it('auto-org-admin reconcile (system) with attributed human', async () => {
209+
const { engine } = await boot();
210+
await engine.insert('sys_permission_set', [
211+
{ id: 'ps_oa', name: 'organization_admin', label: 'OA', active: true },
212+
{ id: 'ps_oa_nb', name: 'organization_admin_no_bypass', label: 'OA nb', active: true },
213+
], SYS);
214+
await engine.insert('sys_member', { id: 'm1', user_id: TARGET, organization_id: 'o1', role: 'owner', created_at: new Date().toISOString() }, SYS);
215+
const res = await reconcileOrgAdminGrant(engine, TARGET, 'o1', { posture: 'single', attributedUserId: ISSUER });
216+
const rows = await engine.find('sys_user_permission_set', { where: { user_id: TARGET }, context: { isSystem: true } } as never) as any[];
217+
console.log('MEASURE auto-org-admin ->', JSON.stringify({ res, granted_by: rows.map((r) => r.granted_by) }));
218+
});
219+
});
220+
221+
void createInvitationPlacementService;
222+
void expect;

0 commit comments

Comments
 (0)