fix(core) : RBAC review fixes - code readonly in edit, TOCTOU doc, canManage reactive, itemsPerPage 999
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,8 +19,7 @@
|
||||
:label="t('admin.roles.form.code')"
|
||||
input-class="w-full"
|
||||
required
|
||||
:readonly="isEditMode && role?.isSystem"
|
||||
:hint="isEditMode && role?.isSystem ? t('admin.roles.delete.systemTooltip') : ''"
|
||||
:readonly="isEditMode"
|
||||
/>
|
||||
|
||||
<MalioInputTextArea
|
||||
@@ -121,7 +120,7 @@ const permissionsByModule = computed<PermissionModule[]>(() => {
|
||||
async function loadPermissions() {
|
||||
const data = await api.get<{ member: Permission[] }>(
|
||||
'/permissions',
|
||||
{ 'orphan': false, itemsPerPage: 200 },
|
||||
{ 'orphan': false, itemsPerPage: 999 },
|
||||
{ toast: false },
|
||||
)
|
||||
allPermissions.value = data.member
|
||||
@@ -183,19 +182,24 @@ function handleToggleAll(module: string, selected: boolean) {
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
const body = {
|
||||
label: form.value.label,
|
||||
code: form.value.code,
|
||||
description: form.value.description || null,
|
||||
permissions: Array.from(selectedPermissionIds.value).map(id => `/api/permissions/${id}`),
|
||||
}
|
||||
const permissions = Array.from(selectedPermissionIds.value).map(id => `/api/permissions/${id}`)
|
||||
|
||||
if (isEditMode.value && props.role) {
|
||||
await api.patch(`/roles/${props.role.id}`, body, {
|
||||
// Le code est immuable apres creation (garde backend RoleProcessor)
|
||||
await api.patch(`/roles/${props.role.id}`, {
|
||||
label: form.value.label,
|
||||
description: form.value.description || null,
|
||||
permissions,
|
||||
}, {
|
||||
toastSuccessMessage: t('admin.roles.toast.updated'),
|
||||
})
|
||||
} else {
|
||||
await api.post('/roles', body, {
|
||||
await api.post('/roles', {
|
||||
label: form.value.label,
|
||||
code: form.value.code,
|
||||
description: form.value.description || null,
|
||||
permissions,
|
||||
}, {
|
||||
toastSuccessMessage: t('admin.roles.toast.created'),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ const effectivePermissions = computed<EffectivePermission[]>(() => {
|
||||
async function loadData() {
|
||||
const [rolesData, permsData] = await Promise.all([
|
||||
api.get<{ member: Role[] }>('/roles', {}, { toast: false }),
|
||||
api.get<{ member: Permission[] }>('/permissions', { orphan: false, itemsPerPage: 200 }, { toast: false }),
|
||||
api.get<{ member: Permission[] }>('/permissions', { orphan: false, itemsPerPage: 999 }, { toast: false }),
|
||||
])
|
||||
allRoles.value = rolesData.member
|
||||
allPermissions.value = permsData.member
|
||||
|
||||
@@ -81,7 +81,7 @@ import type { Role } from '~/shared/types/rbac'
|
||||
const { t } = useI18n()
|
||||
const api = useApi()
|
||||
const { can } = usePermissions()
|
||||
const canManage = can('core.roles.manage')
|
||||
const canManage = computed(() => can('core.roles.manage'))
|
||||
|
||||
useHead({ title: t('admin.roles.title') })
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ const { can } = usePermissions()
|
||||
|
||||
useHead({ title: t('admin.users.title') })
|
||||
|
||||
const canManage = can('core.users.manage')
|
||||
const canManage = computed(() => can('core.users.manage'))
|
||||
|
||||
const users = ref<UserListItem[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -53,6 +53,13 @@ final class AdminHeadcountGuard implements AdminHeadcountGuardInterface
|
||||
* La verification est volontairement conservative (<=1) pour couvrir
|
||||
* le cas defensif ou la base serait deja dans un etat incoherent (0 admin).
|
||||
*
|
||||
* TOCTOU accepte : la verification n'utilise pas de verrou pessimiste
|
||||
* (SELECT ... FOR UPDATE). Deux demotions concurrentes pourraient donc
|
||||
* passer le garde simultanement. Ce risque est accepte dans le contexte
|
||||
* PME/CRM ou les operations d'administration sont rares et mono-operateur.
|
||||
* Si la concurrence admin devient un enjeu, ajouter un verrou pessimiste
|
||||
* sur countAdmins() ou une contrainte CHECK en base.
|
||||
*
|
||||
* @throws LastAdminProtectionException si le nombre d'admins est inferieur ou egal a 1
|
||||
*/
|
||||
private function checkAdminHeadcount(): void
|
||||
|
||||
Reference in New Issue
Block a user