fix(auth): prevent admin from revoking their last users.manage role

- add self-lockout guard in handleRevokeUserRole api handler
- sequence role additions before removals and handle delete errors in UserEditModal
- document the security rule in core/users README
This commit is contained in:
2026-04-25 10:12:31 -04:00
parent 78ba61e60e
commit efc7c93c6b
3 changed files with 39 additions and 8 deletions
@@ -123,22 +123,33 @@ const UserEditModal = ({ userId, currentUserId, isOpen, onClose, onSaved }) => {
const toAdd = selectedRoleIds.filter(id => !initialRoleIds.includes(id));
const toRemove = initialRoleIds.filter(id => !selectedRoleIds.includes(id));
await Promise.all([
...toAdd.map(roleId =>
await Promise.all(
toAdd.map(roleId =>
fetch(`/zen/api/users/${userId}/roles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ roleId }),
})
),
...toRemove.map(roleId =>
)
);
const removeResults = await Promise.all(
toRemove.map(roleId =>
fetch(`/zen/api/users/${userId}/roles/${roleId}`, {
method: 'DELETE',
credentials: 'include',
})
),
]);
}).then(async res => ({ res, data: await res.json() }))
)
);
const failedRemove = removeResults.find(({ res }) => !res.ok);
if (failedRemove) {
toast.error(failedRemove.data?.message || failedRemove.data?.error || 'Impossible de retirer ce rôle');
onSaved?.();
onClose();
return;
}
if (emailChanged) {
if (isSelf) {