feat: add platform staff management

This commit is contained in:
Codex
2026-06-30 08:28:44 +08:00
parent 780feee4f8
commit 4f64b7aed8
21 changed files with 1205 additions and 25 deletions

View File

@@ -22,6 +22,7 @@ const AUTH_USER_ID = '00000000-0000-0000-0000-00000000a101';
const AUTH_TENANT_ADMIN_USER_ID = '00000000-0000-0000-0000-00000000a102';
const AUTH_PLATFORM_ADMIN_USER_ID = '00000000-0000-0000-0000-00000000a999';
const AUTH_RESTRICTED_PLATFORM_ADMIN_USER_ID = '00000000-0000-0000-0000-00000000a998';
const AUTH_PLATFORM_STAFF_USER_ID = '00000000-0000-4000-8000-00000000a997';
const AUTH_JWT_SECRET = 'development-jwt-secret-change-me';
const START_SERVER = process.argv.includes('--start-server');
const ENABLE_REAL_STORAGE_SIGN_TESTS = process.env.ENABLE_REAL_STORAGE_SIGN_TESTS === 'true';
@@ -1179,6 +1180,247 @@ async function testPlatformAdminPermissions() {
assert.equal(paymentDenied.code, 'PLATFORM_PERMISSION_REQUIRED', 'manual service-fee payment must require billing payment permission');
}
async function testPlatformStaffManagement() {
const adminHeaders = { 'x-platform-admin-key': 'local-platform-admin-key' };
const staffSuffix = Date.now().toString(36);
const staffEmail = `platform.staff.${staffSuffix}@example.test`;
const staffPhone = `139${String(Date.now()).slice(-8)}`;
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL || DEFAULT_DATABASE_URL });
try {
await pool.query(
`
insert into auth.users (
id, aud, role, phone, email, phone_confirmed_at, email_confirmed_at,
raw_app_meta_data, raw_user_meta_data, created_at, updated_at
)
values (
$1, 'authenticated', 'authenticated', $2, $3,
now(), now(),
'{"provider":"phone","providers":["phone"],"app_role":"platform_admin"}'::jsonb,
'{}'::jsonb, now(), now()
)
on conflict (id)
do update set phone = excluded.phone,
email = excluded.email,
raw_app_meta_data = excluded.raw_app_meta_data,
updated_at = now()
`,
[AUTH_PLATFORM_STAFF_USER_ID, staffPhone, staffEmail],
);
} finally {
await pool.end();
}
const created = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PUT',
body: {
authUserId: AUTH_PLATFORM_STAFF_USER_ID,
username: 'platform_staff_operator',
email: staffEmail,
phone: staffPhone,
name: 'Platform Staff Operator',
status: 'active',
platformPermissions: {
'platform:staff:read': true,
'platform:overview:read': true,
'platform:tenant:read': true,
},
metadata: {
title: 'operations',
secretToken: 'should-not-be-returned-through-audit',
},
},
});
assert.ok(created.item?.id, 'platform admin should create platform staff');
assert.equal(created.item?.platformPermissions?.['platform:tenant:read'], true, 'platform staff response should include granted permissions');
assert.ok(!JSON.stringify(created).includes('local-platform-admin-key'), 'platform staff response must not leak platform key');
const invalidPermission = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PUT',
body: {
authUserId: AUTH_PLATFORM_STAFF_USER_ID,
username: 'invalid_platform_staff',
name: 'Invalid Platform Staff',
platformPermissions: {
'platform:unknown:write': true,
},
},
expectStatus: 400,
});
assert.equal(invalidPermission.code, 'INVALID_PLATFORM_PERMISSION', 'platform staff permissions should reject unknown permission keys');
const missingAuthUser = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PUT',
body: {
authUserId: '00000000-0000-4000-8000-00000000dead',
username: 'missing_auth_platform_staff',
name: 'Missing Auth Platform Staff',
platformPermissions: {
'platform:overview:read': true,
},
},
expectStatus: 404,
});
assert.equal(missingAuthUser.code, 'AUTH_USER_NOT_FOUND', 'platform staff must bind an existing Supabase Auth user');
const missingAuthBinding = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PUT',
body: {
username: 'unbound_platform_staff',
name: 'Unbound Platform Staff',
platformPermissions: {
'platform:overview:read': true,
},
},
expectStatus: 400,
});
assert.equal(missingAuthBinding.code, 'REQUIRED_FIELD', 'platform staff authUserId should be required');
const emptyPermissionDenied = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PUT',
body: {
authUserId: AUTH_PLATFORM_STAFF_USER_ID,
username: 'empty_permission_platform_staff',
name: 'Empty Permission Platform Staff',
status: 'active',
platformPermissions: {},
},
expectStatus: 400,
});
assert.equal(emptyPermissionDenied.code, 'PLATFORM_PERMISSION_EMPTY', 'active platform staff should require explicit permissions');
const staffList = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: adminHeaders,
query: { q: 'platform_staff_operator' },
});
assert.ok(staffList.items?.some(item => item.id === created.item.id), 'platform staff list should include created staff');
const staffJwt = await createSupabaseJwt(AUTH_PLATFORM_STAFF_USER_ID, {
phone: staffPhone,
appRole: 'platform_admin',
tenantId: false,
});
const staffHeaders = { authorization: `Bearer ${staffJwt}` };
const staffPermissions = await request('/api/platform-admin/permissions', {
tenantId: false,
userId: false,
headers: staffHeaders,
});
assert.equal(staffPermissions.item?.effective?.['platform:staff:read'], true, 'staff should expose its staff read permission');
assert.equal(staffPermissions.item?.effective?.['platform:tenant:write'], false, 'staff should not expose ungranted tenant write permission');
const staffCanListTenants = await request('/api/platform-admin/tenants', {
tenantId: false,
userId: false,
headers: staffHeaders,
});
assert.ok(Array.isArray(staffCanListTenants.items), 'staff should list tenants with tenant read permission');
const staffCreateTenantDenied = await request('/api/platform-admin/tenants', {
tenantId: false,
userId: false,
headers: staffHeaders,
method: 'POST',
body: {
slug: `staff-denied-${Date.now().toString(36)}`,
name: 'Staff Denied Tenant',
},
expectStatus: 403,
});
assert.equal(staffCreateTenantDenied.code, 'PLATFORM_PERMISSION_REQUIRED', 'platform staff should not create tenants without tenant write permission');
const staffWriteDenied = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: staffHeaders,
method: 'PUT',
body: {
username: 'staff_cannot_create_staff',
name: 'Staff Cannot Create Staff',
platformPermissions: { 'platform:overview:read': true },
},
expectStatus: 403,
});
assert.equal(staffWriteDenied.code, 'PLATFORM_PERMISSION_REQUIRED', 'staff write should require explicit platform staff write permission');
const platformAdminJwt = await createSupabaseJwt(AUTH_PLATFORM_ADMIN_USER_ID, {
phone: '13999999999',
appRole: 'platform_admin',
tenantId: false,
});
const platformAdminHeaders = { authorization: `Bearer ${platformAdminJwt}` };
const selfDowngradeDenied = await request('/api/platform-admin/staff', {
tenantId: false,
userId: false,
headers: platformAdminHeaders,
method: 'PUT',
body: {
id: '00000000-0000-0000-0000-000000000999',
authUserId: AUTH_PLATFORM_ADMIN_USER_ID,
username: 'smoke_platform_admin',
phone: '13999999999',
name: 'Smoke Platform Admin',
status: 'active',
platformPermissions: { 'platform:overview:read': true },
},
expectStatus: 400,
});
assert.equal(selfDowngradeDenied.code, 'CANNOT_DOWNGRADE_SELF', 'platform admin should not remove its own super permission');
const disabled = await request('/api/platform-admin/staff/status', {
tenantId: false,
userId: false,
headers: adminHeaders,
method: 'PATCH',
body: {
staffId: created.item.id,
status: 'disabled',
reason: 'integration disable test',
revokeSessions: true,
},
});
assert.equal(disabled.item?.status, 'disabled', 'platform admin should disable platform staff');
const disabledStaffDenied = await request('/api/platform-admin/overview', {
tenantId: false,
userId: false,
headers: staffHeaders,
expectStatus: 403,
});
assert.equal(disabledStaffDenied.code, 'PLATFORM_ADMIN_REQUIRED', 'disabled platform staff JWT should no longer authenticate as platform admin');
const audit = await request('/api/platform-admin/audit-logs', {
tenantId: false,
userId: false,
headers: adminHeaders,
query: { targetType: 'platform_user', q: 'platform.staff', limit: 20 },
});
assert.ok(
audit.items?.some(item => item.targetId === created.item.id && item.action === 'platform.staff.status_updated'),
'platform staff status changes should be audited',
);
assert.ok(!JSON.stringify(audit).includes('should-not-be-returned-through-audit'), 'platform staff audit should not leak token-like metadata');
}
async function testSupabaseJwksIdentity() {
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
const publicJwk = await exportJWK(publicKey);
@@ -9014,6 +9256,7 @@ async function main() {
await check('phone binding', testPhoneBinding);
await check('Supabase JWT identity', testSupabaseJwtIdentity);
await check('platform admin permissions', testPlatformAdminPermissions);
await check('platform staff management', testPlatformStaffManagement);
await check('Supabase JWKS JWT identity', testSupabaseJwksIdentity);
await check('legacy auth headers disabled', testLegacyAuthHeadersDisabled);
await check('platform tenant operations and audit', testPlatformTenantOperationsAndAudit);