bdc8bef857
rolesController + routes/roles (admin, inline guards): GET список (с числом пользователей), POST создать кастомную роль (имя-идентификатор + метка + base_roles; засев прав из функциональной базы), PUT изменить, DELETE удалить (пользователей возвращает на базу), GET /:name/permissions (эффективная карта база+оверлей + defs). setPermission теперь принимает кастомные роли (ключ валидируется по базе, хранится под именем роли). Смонтировано в server.js + тест-харнесс. Тест roles-api 5/5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
4.3 KiB
JavaScript
71 lines
4.3 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Phase C, C-4a — API конструктора ролей (/api/roles, admin).
|
|
* Имя роли — латинский идентификатор (sanitize), метка — любая. Создание засевает
|
|
* права из функциональной базы; setPermission принимает кастомные роли (ключ по базе);
|
|
* удаление возвращает пользователей на базу; встроенные роли защищены; не-админу 403.
|
|
*/
|
|
const { describe, it, before, after } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { db, getToken, inject, cleanup } = require('./setup');
|
|
|
|
after(() => cleanup());
|
|
|
|
describe('roles API (C-4)', () => {
|
|
let adminToken, studentUid;
|
|
|
|
before(async () => {
|
|
adminToken = (await getToken('admin')).token;
|
|
studentUid = (await getToken('student')).userId;
|
|
});
|
|
|
|
it('создание кастомной роли (имя санитизируется) + засев прав из базы', async () => {
|
|
const r = await inject('POST', '/api/roles', { name: 'Curator-1', label: 'Куратор', baseRoles: ['teacher'] }, adminToken);
|
|
assert.equal(r.status, 200, JSON.stringify(r.body));
|
|
assert.equal(r.body.name, 'curator1', 'имя приведено к латинскому идентификатору');
|
|
|
|
const list = await inject('GET', '/api/roles', null, adminToken);
|
|
assert.ok(list.body.some(x => x.name === 'curator1' && !x.isBuiltin && x.baseRoles[0] === 'teacher'));
|
|
|
|
const rp = await inject('GET', '/api/roles/curator1/permissions', null, adminToken);
|
|
assert.equal(rp.status, 200);
|
|
assert.equal(rp.body.base, 'teacher');
|
|
assert.ok(rp.body.definitions.length > 0, 'есть определения ключей базы');
|
|
assert.equal(rp.body.permissions['classes.manage'], true, 'classes.manage засеян из teacher (default 1)');
|
|
});
|
|
|
|
it('конфиг права кастомной роли через setPermission (ключ валиден по базе)', async () => {
|
|
const r = await inject('POST', '/api/permissions', { role: 'curator1', permission: 'questions.manage', enabled: true }, adminToken);
|
|
assert.equal(r.status, 200, JSON.stringify(r.body));
|
|
const row = db.prepare("SELECT enabled FROM role_permissions WHERE role='curator1' AND permission='questions.manage'").get();
|
|
assert.ok(row && row.enabled === 1, 'право сохранено под именем кастомной роли');
|
|
|
|
const bad = await inject('POST', '/api/permissions', { role: 'curator1', permission: 'tests.free', enabled: true }, adminToken);
|
|
assert.equal(bad.status, 400, 'tests.free — студенческий ключ, не для teacher-базы');
|
|
});
|
|
|
|
it('удаление роли возвращает пользователей на функциональную базу', async () => {
|
|
await inject('PATCH', `/api/admin/users/${studentUid}/role`, { role: 'curator1' }, adminToken);
|
|
let row = db.prepare('SELECT role, custom_role FROM users WHERE id=?').get(studentUid);
|
|
assert.equal(row.custom_role, 'curator1'); assert.equal(row.role, 'teacher');
|
|
|
|
const del = await inject('DELETE', '/api/roles/curator1', null, adminToken);
|
|
assert.equal(del.status, 200);
|
|
assert.ok(del.body.reassigned >= 1);
|
|
row = db.prepare('SELECT role, custom_role FROM users WHERE id=?').get(studentUid);
|
|
assert.equal(row.custom_role, null, 'custom_role снят');
|
|
assert.equal(row.role, 'teacher', 'остался на функциональной базе');
|
|
assert.ok(!db.prepare("SELECT 1 FROM roles WHERE name='curator1'").get(), 'роль удалена');
|
|
});
|
|
|
|
it('встроенную роль нельзя удалить/изменить', async () => {
|
|
assert.equal((await inject('DELETE', '/api/roles/teacher', null, adminToken)).status, 400);
|
|
assert.equal((await inject('PUT', '/api/roles/teacher', { label: 'x' }, adminToken)).status, 400);
|
|
});
|
|
|
|
it('не-админу — 403', async () => {
|
|
const fresh = await getToken('student'); // свежий токен (studentTok мог быть инвалидирован сменой роли)
|
|
assert.equal((await inject('GET', '/api/roles', null, fresh.token)).status, 403);
|
|
});
|
|
});
|