Release 2.8.0
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
const common = deps.common;
|
||||
const pages = deps.pages;
|
||||
const getAuditUserId = deps.getAuditUserId;
|
||||
const recordRequestAuditEvent = deps.recordRequestAuditEvent;
|
||||
const rbacData = deps.rbacData;
|
||||
const permissions = Array.isArray(deps.permissions) ? deps.permissions : [];
|
||||
const readArrayField = deps.readArrayField;
|
||||
@@ -114,6 +115,7 @@
|
||||
sectionIndex: toSortIndex(permission.sectionIndex),
|
||||
sectionOrder: Number(permission.sectionOrder) || 999,
|
||||
resourceIndex: toSortIndex(permission.resourceIndex),
|
||||
resourceOrder: Number(permission.resourceOrder) || 999,
|
||||
permissions: []
|
||||
};
|
||||
groupIndex.set(sectionKey, group);
|
||||
@@ -191,7 +193,7 @@
|
||||
};
|
||||
}
|
||||
|
||||
app.get('/rbac', requirePermission('rbac.read'), async function (req, res, next) {
|
||||
app.get('/settings/roles', requirePermission('rbac.read'), async function (req, res, next) {
|
||||
try {
|
||||
const page = Math.max(1, Math.floor(Number(req.query.page) || 1));
|
||||
const search = common.getSearchQuery(req);
|
||||
@@ -207,7 +209,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/rbac/new', requirePermission('rbac.create'), function (req, res, next) {
|
||||
app.get('/settings/roles/new', requirePermission('rbac.create'), function (req, res, next) {
|
||||
const page = Math.max(1, Math.floor(Number(req.query.page) || 1));
|
||||
const search = common.getSearchQuery(req);
|
||||
const sort = common.getSortQuery(req);
|
||||
@@ -223,7 +225,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/rbac/:id/duplicate', requirePermission('rbac.read'), requirePermission('rbac.create'), async function (req, res, next) {
|
||||
app.get('/settings/roles/:id/duplicate', requirePermission('rbac.read'), requirePermission('rbac.create'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -252,7 +254,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/rbac', requirePermission('rbac.create'), async function (req, res, next) {
|
||||
app.post('/settings/roles', requirePermission('rbac.create'), async function (req, res, next) {
|
||||
try {
|
||||
const name = common.validateMaxLength(req.body.name || '', ROLE_NAME_MAX_LENGTH, 'Role name');
|
||||
const description = common.validateMaxLength(req.body.description || '', ROLE_DESCRIPTION_MAX_LENGTH, 'Role description');
|
||||
@@ -319,13 +321,24 @@
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
res.redirect('/rbac/' + insertedRoleId + '/edit?message=' + encodeURIComponent('Role created.'));
|
||||
if (typeof recordRequestAuditEvent === 'function') {
|
||||
await recordRequestAuditEvent(pool, req, {
|
||||
category: 'roles',
|
||||
eventType: 'role.created',
|
||||
actorUserId: actorId,
|
||||
targetType: 'role',
|
||||
targetId: insertedRoleId,
|
||||
targetLabel: name,
|
||||
details: { permissionKeys: selectedPermissionKeys, userIds: normalizedUserIds }
|
||||
});
|
||||
}
|
||||
res.redirect('/settings/roles/' + insertedRoleId + '/edit?message=' + encodeURIComponent('Role created.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/rbac/:id/edit', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
app.get('/settings/roles/:id/edit', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -354,7 +367,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/rbac/:id', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
app.post('/settings/roles/:id', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -376,15 +389,18 @@
|
||||
const selectedUserIds = shouldSyncUsers
|
||||
? readArrayField(req.body, ['user_ids[]', 'user_ids'])
|
||||
: [];
|
||||
const visibleUserIds = shouldSyncUsers
|
||||
? normalizeSelectedIds(readArrayField(req.body, ['user_ids_present[]', 'user_ids_present']))
|
||||
: [];
|
||||
const normalizedPermissionKeys = shouldSyncPermissions ? normalizePermissionKeys(selectedPermissionKeys) : [];
|
||||
const normalizedUserIds = shouldSyncUsers ? normalizeSelectedIds(selectedUserIds) : [];
|
||||
let normalizedUserIds = shouldSyncUsers ? normalizeSelectedIds(selectedUserIds) : [];
|
||||
|
||||
if (!name) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('Role name is required.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('Role name is required.'));
|
||||
}
|
||||
|
||||
if (await common.fetchDuplicateName(pool, 'a_roles', name, roleId)) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('A role with that name already exists.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('A role with that name already exists.'));
|
||||
}
|
||||
|
||||
const validPermissionKeys = new Set(permissions.map(function (permission) {
|
||||
@@ -393,12 +409,17 @@
|
||||
if (shouldSyncPermissions && normalizedPermissionKeys.some(function (permissionKey) {
|
||||
return !validPermissionKeys.has(permissionKey);
|
||||
})) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected permissions are invalid.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected permissions are invalid.'));
|
||||
}
|
||||
|
||||
let availableUsers = [];
|
||||
if (shouldSyncUsers) {
|
||||
availableUsers = await rbacData.fetchUsersWithRoles(pool);
|
||||
const existingRoleUserIds = await rbacData.fetchRoleUserIds(pool, roleId);
|
||||
const visibleUserIdSet = new Set(visibleUserIds);
|
||||
normalizedUserIds = existingRoleUserIds.filter(function (userId) {
|
||||
return !visibleUserIdSet.has(userId);
|
||||
}).concat(normalizedUserIds);
|
||||
}
|
||||
const validUserIds = new Set(availableUsers.map(function (user) {
|
||||
return Number(user.id);
|
||||
@@ -406,7 +427,7 @@
|
||||
if (shouldSyncUsers && normalizedUserIds.some(function (userId) {
|
||||
return !validUserIds.has(userId);
|
||||
})) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected users are invalid.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected users are invalid.'));
|
||||
}
|
||||
|
||||
const connection = await pool.getConnection();
|
||||
@@ -429,13 +450,24 @@
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('Role updated.'));
|
||||
if (typeof recordRequestAuditEvent === 'function') {
|
||||
await recordRequestAuditEvent(pool, req, {
|
||||
category: 'roles',
|
||||
eventType: 'role.updated',
|
||||
actorUserId: req.currentUser.id,
|
||||
targetType: 'role',
|
||||
targetId: roleId,
|
||||
targetLabel: name,
|
||||
details: { permissionsChanged: shouldSyncPermissions, usersChanged: shouldSyncUsers }
|
||||
});
|
||||
}
|
||||
res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('Role updated.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/rbac/:id/permissions', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
app.post('/settings/roles/:id/permissions', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -459,7 +491,7 @@
|
||||
if (normalizedPermissionKeys.some(function (permissionKey) {
|
||||
return !validPermissionKeys.has(permissionKey);
|
||||
})) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected permissions are invalid.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected permissions are invalid.'));
|
||||
}
|
||||
|
||||
const connection = await pool.getConnection();
|
||||
@@ -473,13 +505,24 @@
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('Permissions updated.'));
|
||||
if (typeof recordRequestAuditEvent === 'function') {
|
||||
await recordRequestAuditEvent(pool, req, {
|
||||
category: 'roles',
|
||||
eventType: 'role.permissions_updated',
|
||||
actorUserId: req.currentUser.id,
|
||||
targetType: 'role',
|
||||
targetId: roleId,
|
||||
targetLabel: role.name,
|
||||
details: { permissionKeys: normalizedPermissionKeys }
|
||||
});
|
||||
}
|
||||
res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('Permissions updated.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/rbac/:id/users', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
app.post('/settings/roles/:id/users', requirePermission('rbac.update'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -505,17 +548,28 @@
|
||||
if (normalizedUserIds.some(function (userId) {
|
||||
return !validUserIds.has(userId);
|
||||
})) {
|
||||
return res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected users are invalid.'));
|
||||
return res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('One or more selected users are invalid.'));
|
||||
}
|
||||
|
||||
await rbacData.syncRoleUsers(pool, roleId, normalizedUserIds);
|
||||
res.redirect('/rbac/' + roleId + '/edit?message=' + encodeURIComponent('Users updated.'));
|
||||
if (typeof recordRequestAuditEvent === 'function') {
|
||||
await recordRequestAuditEvent(pool, req, {
|
||||
category: 'roles',
|
||||
eventType: 'role.users_updated',
|
||||
actorUserId: req.currentUser.id,
|
||||
targetType: 'role',
|
||||
targetId: roleId,
|
||||
targetLabel: role.name,
|
||||
details: { userIds: normalizedUserIds }
|
||||
});
|
||||
}
|
||||
res.redirect('/settings/roles/' + roleId + '/edit?message=' + encodeURIComponent('Users updated.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/rbac/:id/delete', requirePermission('rbac.delete'), async function (req, res, next) {
|
||||
app.post('/settings/roles/:id/delete', requirePermission('rbac.delete'), async function (req, res, next) {
|
||||
try {
|
||||
const roleId = Number(req.params.id);
|
||||
if (!Number.isInteger(roleId) || roleId <= 0) {
|
||||
@@ -527,14 +581,24 @@
|
||||
return res.status(404).send('Role not found.');
|
||||
}
|
||||
if (String(role.role_key || '') === 'administrators') {
|
||||
return res.redirect('/rbac?message=' + encodeURIComponent('The built-in Administrators role cannot be deleted.'));
|
||||
return res.redirect('/settings/roles?message=' + encodeURIComponent('The built-in Administrators role cannot be deleted.'));
|
||||
}
|
||||
if (Number(role.user_count) > 0) {
|
||||
return res.redirect('/rbac?message=' + encodeURIComponent('Remove all users from this role before deleting it.'));
|
||||
return res.redirect('/settings/roles?message=' + encodeURIComponent('Remove all users from this role before deleting it.'));
|
||||
}
|
||||
|
||||
await pool.query('DELETE FROM a_roles WHERE id = ?', [roleId]);
|
||||
res.redirect('/rbac?message=' + encodeURIComponent('Role deleted.'));
|
||||
if (typeof recordRequestAuditEvent === 'function') {
|
||||
await recordRequestAuditEvent(pool, req, {
|
||||
category: 'roles',
|
||||
eventType: 'role.deleted',
|
||||
actorUserId: req.currentUser.id,
|
||||
targetType: 'role',
|
||||
targetId: roleId,
|
||||
targetLabel: role.name
|
||||
});
|
||||
}
|
||||
res.redirect('/settings/roles?message=' + encodeURIComponent('Role deleted.'));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user