refactor(users)!: merge users.edit and users.delete into users.manage permission
BREAKING CHANGE: permissions `users.edit` and `users.delete` have been replaced by a single `users.manage` permission; any role or code referencing the old keys must be updated - remove `USERS_EDIT` and `USERS_DELETE` from `PERMISSIONS` and `PERMISSION_DEFINITIONS` - add `USERS_MANAGE` permission covering create, edit and delete actions - update `db.js` to use `users.manage` in permission checks - update `auth/api.js` to reference the new permission key - update `UsersPage.client.js` to check `users.manage` instead of old keys - update `api/define.js` and all README examples to reflect the new key
This commit is contained in:
@@ -181,7 +181,7 @@ Champs optionnels :
|
|||||||
| Champ | Type | Description |
|
| Champ | Type | Description |
|
||||||
|-------|------|-------------|
|
|-------|------|-------------|
|
||||||
| `skipRateLimit` | `boolean` | Exempte la route du rate limiting par IP (ex. health checks) |
|
| `skipRateLimit` | `boolean` | Exempte la route du rate limiting par IP (ex. health checks) |
|
||||||
| `permission` | `string` | Sur une route `auth: 'admin'`, exige en plus cette clé de permission granulaire (ex. `'users.edit'`). Retourne 403 si l'utilisateur ne la possède pas. Voir `PERMISSIONS` dans `src/core/users/constants.js` |
|
| `permission` | `string` | Sur une route `auth: 'admin'`, exige en plus cette clé de permission granulaire (ex. `'users.manage'`). Retourne 403 si l'utilisateur ne la possède pas. Voir `PERMISSIONS` dans `src/core/users/constants.js` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
* (e.g. health checks from monitoring systems).
|
* (e.g. health checks from monitoring systems).
|
||||||
* permission {string} When set on an 'admin' route, the router additionally
|
* permission {string} When set on an 'admin' route, the router additionally
|
||||||
* verifies that the authenticated user holds this granular
|
* verifies that the authenticated user holds this granular
|
||||||
* permission key (e.g. 'users.edit'). If the user lacks
|
* permission key (e.g. 'users.manage'). If the user lacks
|
||||||
* the permission, the request is rejected with 403 Forbidden.
|
* the permission, the request is rejected with 403 Forbidden.
|
||||||
*
|
*
|
||||||
* Auth levels:
|
* Auth levels:
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ const role = await createRole({ name: 'Modérateur', description: 'Peut gérer l
|
|||||||
|
|
||||||
await updateRole(roleId, {
|
await updateRole(roleId, {
|
||||||
name: 'Modérateur',
|
name: 'Modérateur',
|
||||||
permissionKeys: [PERMISSIONS.USERS_VIEW, PERMISSIONS.USERS_EDIT],
|
permissionKeys: [PERMISSIONS.USERS_VIEW, PERMISSIONS.USERS_MANAGE],
|
||||||
});
|
});
|
||||||
|
|
||||||
await deleteRole(roleId); // impossible sur les rôles système
|
await deleteRole(roleId); // impossible sur les rôles système
|
||||||
@@ -230,7 +230,7 @@ const keys = await getUserPermissions(userId);
|
|||||||
| Groupe | Clés |
|
| Groupe | Clés |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| Administration | `admin.access` |
|
| Administration | `admin.access` |
|
||||||
| Utilisateurs | `users.view`, `users.edit`, `users.delete` |
|
| Utilisateurs | `users.view`, `users.manage` |
|
||||||
| Rôles | `roles.view`, `roles.manage` |
|
| Rôles | `roles.view`, `roles.manage` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
export const PERMISSIONS = {
|
export const PERMISSIONS = {
|
||||||
ADMIN_ACCESS: 'admin.access',
|
ADMIN_ACCESS: 'admin.access',
|
||||||
USERS_VIEW: 'users.view',
|
USERS_VIEW: 'users.view',
|
||||||
USERS_EDIT: 'users.edit',
|
USERS_MANAGE: 'users.manage',
|
||||||
USERS_DELETE: 'users.delete',
|
|
||||||
ROLES_VIEW: 'roles.view',
|
ROLES_VIEW: 'roles.view',
|
||||||
ROLES_MANAGE: 'roles.manage',
|
ROLES_MANAGE: 'roles.manage',
|
||||||
};
|
};
|
||||||
@@ -15,8 +14,7 @@ export const PERMISSIONS = {
|
|||||||
export const PERMISSION_DEFINITIONS = [
|
export const PERMISSION_DEFINITIONS = [
|
||||||
{ key: 'admin.access', name: 'Accès au panneau admin', description: "Permet d'accéder à l'interface d'administration.", group_name: 'Administration' },
|
{ key: 'admin.access', name: 'Accès au panneau admin', description: "Permet d'accéder à l'interface d'administration.", group_name: 'Administration' },
|
||||||
{ key: 'users.view', name: 'Voir les utilisateurs', description: 'Permet de consulter la liste des membres et leurs profils.', group_name: 'Utilisateurs' },
|
{ key: 'users.view', name: 'Voir les utilisateurs', description: 'Permet de consulter la liste des membres et leurs profils.', group_name: 'Utilisateurs' },
|
||||||
{ key: 'users.edit', name: 'Modifier les utilisateurs', description: 'Permet de changer les informations et les rôles des membres.', group_name: 'Utilisateurs' },
|
{ key: 'users.manage', name: 'Gérer les utilisateurs', description: 'Permet de créer, modifier et supprimer des comptes membres.', group_name: 'Utilisateurs' },
|
||||||
{ key: 'users.delete', name: 'Supprimer des utilisateurs', description: 'Permet de supprimer des comptes membres.', group_name: 'Utilisateurs' },
|
|
||||||
{ key: 'roles.view', name: 'Voir les rôles', description: 'Permet de consulter la liste des rôles et leurs permissions.', group_name: 'Rôles' },
|
{ key: 'roles.view', name: 'Voir les rôles', description: 'Permet de consulter la liste des rôles et leurs permissions.', group_name: 'Rôles' },
|
||||||
{ key: 'roles.manage', name: 'Gérer les rôles', description: 'Permet de créer, modifier et supprimer des rôles.', group_name: 'Rôles' },
|
{ key: 'roles.manage', name: 'Gérer les rôles', description: 'Permet de créer, modifier et supprimer des rôles.', group_name: 'Rôles' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -66,6 +66,20 @@ async function dropRoleCheckConstraint() {
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migratePermissions() {
|
||||||
|
// Migrate users.edit / users.delete → users.manage
|
||||||
|
await query(`
|
||||||
|
INSERT INTO zen_auth_role_permissions (role_id, permission_key)
|
||||||
|
SELECT DISTINCT role_id, 'users.manage'
|
||||||
|
FROM zen_auth_role_permissions
|
||||||
|
WHERE permission_key IN ('users.edit', 'users.delete')
|
||||||
|
AND EXISTS (SELECT 1 FROM zen_auth_permissions WHERE key = 'users.manage')
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
`);
|
||||||
|
await query(`DELETE FROM zen_auth_role_permissions WHERE permission_key IN ('users.edit', 'users.delete')`);
|
||||||
|
await query(`DELETE FROM zen_auth_permissions WHERE key IN ('users.edit', 'users.delete')`);
|
||||||
|
}
|
||||||
|
|
||||||
async function seedDefaultRolesAndPermissions() {
|
async function seedDefaultRolesAndPermissions() {
|
||||||
// Permissions
|
// Permissions
|
||||||
for (const perm of PERMISSION_DEFINITIONS) {
|
for (const perm of PERMISSION_DEFINITIONS) {
|
||||||
@@ -75,6 +89,8 @@ async function seedDefaultRolesAndPermissions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await migratePermissions();
|
||||||
|
|
||||||
// Admin role
|
// Admin role
|
||||||
const adminRoleId = generateId();
|
const adminRoleId = generateId();
|
||||||
await query(
|
await query(
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ const UsersPageClient = ({ currentUserId, refreshKey, canEdit }) => {
|
|||||||
const UsersPage = ({ user }) => {
|
const UsersPage = ({ user }) => {
|
||||||
const [createModalOpen, setCreateModalOpen] = useState(false);
|
const [createModalOpen, setCreateModalOpen] = useState(false);
|
||||||
const [refreshKey, setRefreshKey] = useState(0);
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
const canEdit = user?.permissions?.includes('users.edit');
|
const canEdit = user?.permissions?.includes('users.manage');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
|
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
|
||||||
|
|||||||
@@ -897,7 +897,7 @@ async function handleAdminCreateUser(request) {
|
|||||||
|
|
||||||
export const routes = defineApiRoutes([
|
export const routes = defineApiRoutes([
|
||||||
{ path: '/users', method: 'GET', handler: handleListUsers, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
{ path: '/users', method: 'GET', handler: handleListUsers, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
||||||
{ path: '/users', method: 'POST', handler: handleAdminCreateUser, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users', method: 'POST', handler: handleAdminCreateUser, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/profile', method: 'PUT', handler: handleUpdateProfile, auth: 'user' },
|
{ path: '/users/profile', method: 'PUT', handler: handleUpdateProfile, auth: 'user' },
|
||||||
{ path: '/users/profile/email', method: 'POST', handler: handleInitiateEmailChange, auth: 'user' },
|
{ path: '/users/profile/email', method: 'POST', handler: handleInitiateEmailChange, auth: 'user' },
|
||||||
{ path: '/users/profile/password', method: 'POST', handler: handleChangeOwnPassword, auth: 'user' },
|
{ path: '/users/profile/password', method: 'POST', handler: handleChangeOwnPassword, auth: 'user' },
|
||||||
@@ -908,13 +908,13 @@ export const routes = defineApiRoutes([
|
|||||||
{ path: '/users/profile/sessions/:sessionId', method: 'DELETE', handler: handleDeleteSession, auth: 'user' },
|
{ path: '/users/profile/sessions/:sessionId', method: 'DELETE', handler: handleDeleteSession, auth: 'user' },
|
||||||
{ path: '/users/email/confirm', method: 'GET', handler: handleConfirmEmailChange, auth: 'user' },
|
{ path: '/users/email/confirm', method: 'GET', handler: handleConfirmEmailChange, auth: 'user' },
|
||||||
{ path: '/users/:id/roles', method: 'GET', handler: handleGetUserRoles, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
{ path: '/users/:id/roles', method: 'GET', handler: handleGetUserRoles, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
||||||
{ path: '/users/:id/roles', method: 'POST', handler: handleAssignUserRole, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id/roles', method: 'POST', handler: handleAssignUserRole, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/:id/roles/:roleId', method: 'DELETE', handler: handleRevokeUserRole, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id/roles/:roleId', method: 'DELETE', handler: handleRevokeUserRole, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/:id', method: 'GET', handler: handleGetUserById, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
{ path: '/users/:id', method: 'GET', handler: handleGetUserById, auth: 'admin', permission: PERMISSIONS.USERS_VIEW },
|
||||||
{ path: '/users/:id', method: 'PUT', handler: handleUpdateUserById, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id', method: 'PUT', handler: handleUpdateUserById, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/:id/email', method: 'PUT', handler: handleAdminUpdateUserEmail, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id/email', method: 'PUT', handler: handleAdminUpdateUserEmail, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/:id/password', method: 'PUT', handler: handleAdminSetUserPassword, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id/password', method: 'PUT', handler: handleAdminSetUserPassword, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/users/:id/send-password-reset', method: 'POST', handler: handleAdminSendPasswordReset, auth: 'admin', permission: PERMISSIONS.USERS_EDIT },
|
{ path: '/users/:id/send-password-reset', method: 'POST', handler: handleAdminSendPasswordReset, auth: 'admin', permission: PERMISSIONS.USERS_MANAGE },
|
||||||
{ path: '/roles', method: 'GET', handler: handleListRoles, auth: 'admin', permission: PERMISSIONS.ROLES_VIEW },
|
{ path: '/roles', method: 'GET', handler: handleListRoles, auth: 'admin', permission: PERMISSIONS.ROLES_VIEW },
|
||||||
{ path: '/roles', method: 'POST', handler: handleCreateRole, auth: 'admin', permission: PERMISSIONS.ROLES_MANAGE },
|
{ path: '/roles', method: 'POST', handler: handleCreateRole, auth: 'admin', permission: PERMISSIONS.ROLES_MANAGE },
|
||||||
{ path: '/roles/:id', method: 'GET', handler: handleGetRole, auth: 'admin', permission: PERMISSIONS.ROLES_VIEW },
|
{ path: '/roles/:id', method: 'GET', handler: handleGetRole, auth: 'admin', permission: PERMISSIONS.ROLES_VIEW },
|
||||||
|
|||||||
Reference in New Issue
Block a user