Permissions
API
function checkPermissions(
requiredPermissions: string[] | undefined,
userPermissions: string[] | undefined,
): boolean
function checkRouteAccess(
route: MFERoute,
userRoles: string[] | undefined,
): boolean
function createCanDirective(
getPermissions: () => string[] | undefined,
): ObjectDirective<HTMLElement, string | string[]>
ctx.http partono comunque. La sicurezza reale è server-side (API authorization).checkPermissions(required, user)
Verifica se l'utente ha almeno uno dei permessi richiesti (logica OR).
import { checkPermissions } from '@pzeta/mfe-contracts/helpers'
checkPermissions(['crm.write'], ['crm.read', 'crm.write']) // true
checkPermissions(['crm.write'], ['crm.read']) // false
checkPermissions(undefined, ['crm.read']) // true (no required → allow)
checkPermissions(['crm.write'], undefined) // false (no user perms → deny)
checkPermissions(['anything'], ['*']) // true (wildcard admin)
Regole
required | user | Risultato | Motivo |
|---|---|---|---|
undefined / [] | qualsiasi | true | Nessun permesso richiesto |
| qualsiasi | undefined / [] | false | Utente senza permessi |
| qualsiasi | contiene '*' | true | Wildcard admin UI |
[a, b] | contiene a o b | true | OR logic |
[a, b] | non contiene né a né b | false | — |
Wildcard *
const admin: MFEUser = { id: '1', username: 'admin', permissions: ['*'] }
L'admin UI ha accesso a tutti gli elementi senza enumerare ogni permesso. Server-side deve comunque validare ogni operazione.
checkRouteAccess(route, userRoles)
Verifica se l'utente può accedere a una rotta in base a route.meta.roles.
import { checkRouteAccess } from '@pzeta/mfe-contracts/helpers'
const route: MFERoute = {
path: '/admin',
label: 'Admin Panel',
meta: { roles: ['admin', 'superuser'] },
}
checkRouteAccess(route, ['user', 'admin']) // true
checkRouteAccess(route, ['user']) // false
checkRouteAccess(route, undefined) // false
checkRouteAccess({ path: '/', label: 'Home' }, undefined) // true (no roles richiesti)
Regole
- Se
route.meta.rolesè vuoto o assente → true (nessun vincolo di ruolo) - Altrimenti → l'utente deve avere almeno uno dei ruoli richiesti
checkRouteAccess verifica solo i ruoli, non i permessi. Per check permission usa checkPermissions(route.permission ? [route.permission] : [], user.permissions).Direttiva v-can
Factory che crea una direttiva Vue per mostrare/nascondere/disabilitare elementi in base ai permessi utente.
function createCanDirective(
getPermissions: () => string[] | undefined,
): ObjectDirective<HTMLElement, string | string[]>
Registrazione nella shell
// shell/main.ts
import { createApp, computed } from 'vue'
import { createCanDirective } from '@pzeta/mfe-contracts'
import App from './App.vue'
const app = createApp(App)
const userPermissions = computed(() => currentUser.value?.permissions ?? [])
app.directive('can', createCanDirective(() => userPermissions.value))
app.mount('#app')
() => string[] | undefined): viene chiamato ogni volta che la direttiva valuta i permessi. Così quando i permessi dell'utente cambiano (login/logout/refresh), la valutazione si aggiorna.Uso nei template
<template>
<!-- Nasconde l'elemento se manca il permesso -->
<button v-can="'crm.clienti.delete'" @click="onDelete">
Elimina cliente
</button>
<!-- Disabilita invece di nascondere -->
<button v-can.disable="'crm.clienti.export'" @click="onExport">
Esporta CSV
</button>
<!-- OR logic: basta uno dei permessi -->
<a v-can="['crm.clienti.read', 'crm.admin']" :href="`/clienti/${id}`">
Apri dettaglio
</a>
<!-- Niente o stringa vuota → elemento sempre visibile -->
<button v-can="">Sempre visibile</button>
</template>
Modalità
| Sintassi | Comportamento se manca permesso |
|---|---|
v-can="'perm'" | style.display = 'none' (nasconde) |
v-can.disable="'perm'" | setAttribute('disabled', 'disabled') + opacity: 0.5 + pointer-events: none |
Quando il permesso è concesso, l'elemento viene ripristinato allo stato originale (display salvato in mount).
Comportamento lifecycle
| Hook | Cosa fa |
|---|---|
mounted(el, binding) | Salva el.style.display originale. Se value non vuoto, applica check |
updated(el, binding) | Se value diventa vuoto → ripristina elemento. Altrimenti riapplica check |
Reattività
La direttiva si aggiorna automaticamente quando binding.value cambia (Vue chiama updated()). Per cambi nei permessi utente (senza cambio binding), il getter viene rivalutato ad ogni updated() triggering successivo.
Per forzare la rivalutazione su cambio permessi anche senza cambio v-can, ri-renderizza l'albero con :key:
<div :key="`perms-${user.permissions?.join(',')}`">
<button v-can="'admin'">Admin only</button>
</div>
Pattern: composizione con manifest permission
import { checkPermissions } from '@pzeta/mfe-contracts/helpers'
// In shell: verifica se l'utente può aprire il MFE
const canOpenMfe = (manifest: MFEManifest, user: MFEUser) =>
checkPermissions(manifest.permissions, user.permissions)
// Per filtrare il menu prima del rendering
const accessibleMfes = manifests.filter((m) => canOpenMfe(m, currentUser))
Tipi correlati
MFEUser— fonte dirolesepermissionsMFERoute—meta.rolesepermissionsu routeMFEManifest—permissionsa livello modulo