| 123456789101112131415161718 |
- import {test} from 'node:test';
- import assert from 'node:assert/strict';
- import {readFileSync} from 'node:fs';
- import vm from 'node:vm';
- import {PGlite} from '@electric-sql/pglite';
- const policy=readFileSync(new URL('../cloud/admin-role-policy.js',import.meta.url),'utf8');
- const context={};vm.createContext(context);vm.runInContext(policy,context);
- test('旧管理员仅兼容本帐套运营经理,普通会员不获得后台权限',()=>{const r=context.adminRoleSnapshot({isAdmin:true,roles:['admin'],role:'admin'});assert.equal(r.operationsRole,'ops-manager');assert.equal(r.isSuperAdmin,false);assert.equal(r.legacyRole,true);assert.equal(context.adminRoleSnapshot({roles:[]}).isAdmin,false);});
- test('显式角色优先,adminRoleKey 与数组一致识别,未知运营角色拒绝',()=>{for(const role of ['ops-manager','ops-staff','ops-auditor']){assert.equal(context.adminRoleSnapshot({adminRoleKey:role}).operationsRole,role);assert.equal(context.adminRoleSnapshot({roles:[role]}).operationsRole,role);}assert.equal(context.adminRoleSnapshot({isAdmin:true,adminRoleKey:'ops-missing'}).operationsRole,'denied');assert.equal(context.adminRoleSnapshot({roles:['admin','ops-missing']}).operationsRole,'denied');assert.equal(context.adminRoleSnapshot({adminRoleKey:'ops-auditor',roles:['admin','ops-manager','super-admin']}).isSuperAdmin,false);});
- async function database(){const db=new PGlite();await db.exec(`CREATE TABLE "_User"("objectId" text PRIMARY KEY,"company" text,"username" text,"realName" text,"nickname" text,"roles" jsonb,"role" text,"adminRoleKey" text,"isAdmin" boolean,"identityType" text,"isDisabled" boolean,"isDeleted" boolean,"updatedAt" timestamptz DEFAULT now());CREATE TABLE "_Session"("objectId" text,"user" text);CREATE TABLE "OperatorAccountAudit"("objectId" text PRIMARY KEY,"company" text,"actorId" text,"actorName" text,"targetId" text,"targetName" text,"action" text,"reason" text,"before" jsonb,"after" jsonb,"createdAt" timestamptz,"updatedAt" timestamptz);
- INSERT INTO "_User"("objectId","company","username","roles","isAdmin","isDisabled") VALUES('manager','co','manager','["admin"]',true,false),('staff','co','staff','["ops-staff"]',true,false),('manager2','co','manager2','["ops-manager"]',true,false),('super','co','super','["super-admin"]',true,false),('outside','other','outside','["ops-staff"]',true,false);INSERT INTO "_Session" VALUES('session','staff');`);await db.exec(readFileSync(new URL('../sql/operator-accounts.sql',import.meta.url),'utf8'));return db;}
- const change=(db,actor,target,action,payload={},version='',audit='a')=>db.query('SELECT xs_operator_change($1,$2,$3,$4,$5,$6,$7,$8) AS result',['co',actor,target,action,JSON.stringify(payload),'测试管理',version,audit]);
- test('角色修改撤销旧会话且审计只保存业务字段',async()=>{const db=await database();try{await change(db,'manager','staff','save',{displayName:'运营同事',roleKey:'ops-auditor'});assert.equal((await db.query('SELECT * FROM "_Session"')).rows.length,0);const audit=(await db.query('SELECT * FROM "OperatorAccountAudit"')).rows[0];assert.equal(audit.after.roleKey,'ops-auditor');assert.equal(audit.before.roleKey,'ops-staff');assert.equal((await db.query('SELECT "adminRoleKey" FROM "_User" WHERE "objectId"=\'staff\'')).rows[0].adminRoleKey,'ops-auditor');}finally{await db.close();}});
- test('普通运营不能管理账号,经理不能越帐套或提权超级管理员',async()=>{const db=await database();try{await assert.rejects(change(db,'staff','manager','status',{disabled:true}),/无权/);await assert.rejects(change(db,'manager','outside','status',{disabled:true}),/不存在/);await assert.rejects(change(db,'manager','super','status',{disabled:true}),/超级管理员/);await assert.rejects(change(db,'manager','staff','save',{displayName:'越权',roleKey:'super-admin'}),/不能分配/);assert.equal((await db.query('SELECT * FROM "OperatorAccountAudit"')).rows.length,0);}finally{await db.close();}});
- test('保护本人和版本冲突,停用后旧会话被清除',async()=>{const db=await database();try{await assert.rejects(change(db,'manager','manager','status',{disabled:true}),/不能停用/);await assert.rejects(change(db,'manager','manager','save',{displayName:'本人',roleKey:'ops-staff'}),/自己/);await assert.rejects(change(db,'manager','staff','status',{disabled:true},'2000-01-01'),/已被/);await change(db,'manager','staff','status',{disabled:true});assert.equal((await db.query('SELECT * FROM "_Session"')).rows.length,0);}finally{await db.close();}});
- test('两个经理互相停用时后一个操作重新验证权限,保留有效经理',async()=>{const db=await database();try{const r=await Promise.allSettled([change(db,'manager','manager2','status',{disabled:true},'','a'),change(db,'manager2','manager','status',{disabled:true},'','b')]);assert.equal(r.filter(x=>x.status==='fulfilled').length,1);assert.equal((await db.query('SELECT count(*)::int AS n FROM "_User" WHERE "objectId" IN (\'manager\',\'manager2\') AND NOT "isDisabled"')).rows[0].n,1);}finally{await db.close();}});
- test('SQL 与云函数角色口径一致',async()=>{const db=await database();try{for(const row of [{isAdmin:true},{roles:['admin']},{roles:['ops-auditor'],adminRoleKey:'ops-staff'},{adminRoleKey:'ops-missing',isAdmin:true},{roles:['super-admin']},{roles:[]},{roles:['admin','ops-missing']}]){const r=await db.query('SELECT xs_operator_role($1::jsonb) AS role',[JSON.stringify(row)]);assert.equal(r.rows[0].role,context.adminRoleSnapshot(row).roleKey);}}finally{await db.close();}});
- test('底层用户接口不能直接修改权限、跨帐套或构造管理员注册',async()=>{const db=await database();try{await assert.rejects(db.query('DELETE FROM "_User" WHERE "objectId"=\'manager\''),/不能直接删除/);await assert.rejects(db.query('UPDATE "_User" SET "roles"=\'["super-admin"]\' WHERE "objectId"=\'staff\''),/权限字段/);await assert.rejects(db.query('UPDATE "_User" SET "company"=\'other\' WHERE "objectId"=\'staff\''),/归属与状态/);await assert.rejects(db.query('INSERT INTO "_User"("objectId","roles") VALUES(\'attacker\',\'["admin"]\')'),/只能通过/);await db.query('INSERT INTO "_User"("objectId","isAdmin","identityType","isDisabled") VALUES(\'pending\',false,\'operator\',true)');assert.equal((await db.query('SELECT count(*)::int AS n FROM "_User" WHERE "objectId"=\'pending\'')).rows[0].n,1);}finally{await db.close();}});
|