Stop persisting roles to localStorage as authority; AdminSettingsModal reads and writes role matrices via /api/v1/admin/roles; hasPermission denies when capabilities are not loaded from /auth/me. Co-authored-by: Cursor <cursoragent@cursor.com>
139 lines
4.7 KiB
TypeScript
139 lines
4.7 KiB
TypeScript
import test from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { createInMemoryAuthClient } from '../api/auth.local';
|
||
import { DEFAULT_ROLES, hasPermission } from './rbac';
|
||
import { UserProfile } from '../types/docgov';
|
||
|
||
test('全链路场景 1: 用户自主受控注册(仅填用户名/密码/姓名,职位严格绑定访客待指派)', async () => {
|
||
const client = createInMemoryAuthClient({
|
||
allowRegistration: true,
|
||
requireApproval: false,
|
||
env: 'dev',
|
||
});
|
||
|
||
// 1. 自主注册新员工
|
||
const regRes = await client.register({
|
||
username: 'new_intern',
|
||
password: 'InternPass123',
|
||
name: '陈实习',
|
||
department: '微创器械研发部',
|
||
employeeId: 'INT-001',
|
||
});
|
||
assert.equal(regRes.status, 'active');
|
||
assert.match(regRes.message, /成功/);
|
||
|
||
// 2. 登录并校验角色为 role-guest,职位为 访客/待指派
|
||
const session = await client.login({
|
||
username: 'new_intern',
|
||
password: 'InternPass123',
|
||
});
|
||
assert.equal(session.user.name, '陈实习');
|
||
assert.equal(session.user.roleId, 'role-guest');
|
||
assert.equal(session.user.position, '访客/待指派');
|
||
|
||
// 3. 校验访客默认无项目立项与权限管理能力
|
||
const profile: UserProfile = session.user;
|
||
const guestCaps = DEFAULT_ROLES.find((r) => r.id === 'role-guest')?.permissions ?? [];
|
||
assert.equal(hasPermission(profile, DEFAULT_ROLES, 'project:create', guestCaps), false);
|
||
assert.equal(hasPermission(profile, DEFAULT_ROLES, 'perm:manage', guestCaps), false);
|
||
});
|
||
|
||
test('全链路场景 2: 系统准入策略(关闭自主注册与开启强制审批流转)', async () => {
|
||
const client = createInMemoryAuthClient({
|
||
allowRegistration: false,
|
||
requireApproval: true,
|
||
env: 'test',
|
||
});
|
||
|
||
// 1. 关闭自主注册时拦截
|
||
await assert.rejects(
|
||
async () => {
|
||
await client.register({
|
||
username: 'outsider',
|
||
password: 'Pass123456',
|
||
name: '外协人员',
|
||
});
|
||
},
|
||
/暂停自主注册/
|
||
);
|
||
|
||
// 2. 管理员开启注册但要求审批
|
||
await client.updateSystemAuthConfig({
|
||
allowRegistration: true,
|
||
requireApproval: true,
|
||
});
|
||
|
||
const regRes = await client.register({
|
||
username: 'outsider',
|
||
password: 'Pass123456',
|
||
name: '外协人员',
|
||
});
|
||
assert.equal(regRes.status, 'pending_approval');
|
||
|
||
// 3. 待审状态禁止登录
|
||
await assert.rejects(
|
||
async () => {
|
||
await client.login({ username: 'outsider', password: 'Pass123456' });
|
||
},
|
||
/审核/
|
||
);
|
||
|
||
// 4. 管理员审核通过并授予工程师职位
|
||
await client.updateUserStatus(regRes.userId!, 'active', 'role-engineer', '方案工程师');
|
||
|
||
// 5. 再次登录成功激活
|
||
const session = await client.login({ username: 'outsider', password: 'Pass123456' });
|
||
assert.equal(session.user.status, 'active');
|
||
assert.equal(session.user.roleId, 'role-engineer');
|
||
assert.equal(session.user.position, '方案工程师');
|
||
});
|
||
|
||
test('全链路场景 3: 管理员开通账号首次强制改密闭环', async () => {
|
||
const client = createInMemoryAuthClient({
|
||
adminUsername: 'admin',
|
||
adminPassword: 'admin-password-123',
|
||
});
|
||
|
||
// 1. 管理员开通新员工账号
|
||
const created = await client.createAdminUser({
|
||
username: 'sun_engineer',
|
||
name: '孙工',
|
||
initialPassword: 'InitPassword123',
|
||
roleId: 'role-engineer',
|
||
position: '方案工程师',
|
||
department: '骨科器械组',
|
||
});
|
||
assert.equal(created.mustChangePassword, true);
|
||
|
||
// 2. 孙工首次登录,返回 mustChangePassword = true
|
||
const session = await client.login({
|
||
username: 'sun_engineer',
|
||
password: 'InitPassword123',
|
||
});
|
||
assert.equal(session.mustChangePassword, true);
|
||
|
||
// 3. 执行首次强制改密
|
||
await client.changePassword(session.user.id, 'InitPassword123', 'SunNewSecret999');
|
||
|
||
// 4. 再次登录,验证 mustChangePassword 复位为 false
|
||
const freshSession = await client.login({
|
||
username: 'sun_engineer',
|
||
password: 'SunNewSecret999',
|
||
});
|
||
assert.equal(freshSession.mustChangePassword, false);
|
||
});
|
||
|
||
test('全链路场景 4: 环境标识随登录会话返回(dev/test/main)', async () => {
|
||
const devClient = createInMemoryAuthClient({ env: 'dev' });
|
||
const devSession = await devClient.login({ username: 'admin', password: 'admin-secret-password-123' });
|
||
assert.equal(devSession.env, 'dev');
|
||
|
||
const testClient = createInMemoryAuthClient({ env: 'test' });
|
||
const testSession = await testClient.login({ username: 'admin', password: 'admin-secret-password-123' });
|
||
assert.equal(testSession.env, 'test');
|
||
|
||
const prodClient = createInMemoryAuthClient({ env: 'main' });
|
||
const prodSession = await prodClient.login({ username: 'admin', password: 'admin-secret-password-123' });
|
||
assert.equal(prodSession.env, 'main');
|
||
});
|