La progettazione di permessi e autorizzazione in PHP non si risolve con una schermata in cui si assegnano profili. Il problema emerge quando una stessa azione dipende da chi la esegue, per quale organizzazione lavora, su quale dato agisce e in quale stato si trova quel dato. Se queste condizioni sono distribuite tra controller, query, template e validazioni dell'interfaccia, il sistema finisce per accumulare eccezioni difficili da rivedere.
L'obiettivo deve essere che ogni decisione sia esplicita, ripetibile e verificabile: un'identità tenta di eseguire un'operazione su una risorsa in un contesto e una policy decide se è consentita. Questo approccio trasforma regole operative ambigue in controlli tecnici che prodotto, operations e sviluppo possono rivedere congiuntamente.
Separare identità, autorizzazione e ambito dei dati

L'autenticazione risponde a chi è l'utente: sessione, credenziali, provider di identità o token. L'autorizzazione risponde a cosa può fare quell'identità. Non è opportuno dedurre la seconda soltanto dalla prima né trattare entrambe come un unico livello.
Un ruolo raggruppa responsabilità, come amministratore dell'organizzazione, operatore del supporto o approvatore. Un permesso rappresenta un'operazione concreta, per esempio invoice.read, invoice.approve o member.invite. L'ambito determina su quali risorse è valida quell'operazione: fatture di un'organizzazione, pratiche di un'unità o record propri.
Questa distinzione evita un errore frequente: concedere il permesso di lettura delle fatture e presumere che ciò consenta di leggere qualsiasi fattura. La policy deve ancora verificare se la risorsa appartiene all'organizzazione attiva, se l'utente ha assegnata l'unità corrispondente e se lo stato della risorsa ammette l'azione richiesta.
Costruire una matrice di accesso a partire dalle operazioni
Prima di scegliere classi o pacchetti, elencate risorse e operazioni reali. Usate verbi di business invece di etichette vaghe come “gestire”: creare ordine, visualizzare ordine, correggere bozza, approvare ordine, annullare ordine, esportare ordini o modificare membri.
Per ogni operazione, concordate con il business quattro elementi:
- La risorsa e l'azione protetta.
- I profili che possono richiederla.
- L'ambito dei dati applicabile: organizzazione, unità, proprietario, portafoglio o assegnazione.
- Le condizioni di contesto e stato: organizzazione attiva, delega valida, orario operativo o documento in bozza.
La matrice risultante non è il codice di autorizzazione, ma una specifica riesaminabile. Inoltre, obbliga a individuare decisioni in sospeso. Se si indica che il supporto può “visualizzare ordini”, occorre precisare se può vedere dati personali, documenti allegati, ordini chiusi o informazioni di tutte le organizzazioni.
Preferire azioni piccole e stabili
Un'azione troppo ampia concentra privilegi e rende difficile applicare il principio del privilegio minimo. Separare order.read da order.export, oppure user.update da user.assign_role, consente di concedere l'accesso in modo preciso. Non è neppure opportuno creare un permesso per ogni caso specifico: se la differenza dipende dalla risorsa, di solito è una condizione della policy, non un nuovo ruolo.
Scegliere tra ruoli, permessi, attributi e contesto
I ruoli semplici funzionano quando esistono pochi profili stabili e le operazioni dipendono poco dal dato. Sono un buon punto di partenza, ma diventano fragili se compaiono nomi come manager_con_esportazione o supervisore_solo_unita_nord. Queste combinazioni codificano eccezioni come profili permanenti.
I permessi espliciti sono adatti a disaccoppiare le responsabilità dai profili e ad assegnare capacità in modo gestibile. Gli attributi sono utili quando la decisione dipende da proprietà del soggetto, della risorsa o dell'ambiente: organizzazione, unità, classificazione, proprietario, paese o livello di rischio. Le regole contestuali completano il modello quando intervengono condizioni transitorie, come una delega attiva o la fase di approvazione.
In pratica, un modello ibrido è spesso più manutenibile: i ruoli concedono permessi base; una policy valuta gli attributi dell'utente e della risorsa; e il contesto fornisce l'organizzazione selezionata o il canale operativo. Il ruolo non deve sostituire l'analisi del dato.
Centralizzare le policy e filtrare i dati all'origine
Un'applicazione PHP necessita di un punto coerente per esprimere le decisioni. Può concretizzarsi in classi di policy, servizi di autorizzazione o componenti equivalenti del framework scelto. L'importante è che i controller richiedano una decisione e che le viste non siano l'unica barriera.
if (!$authorizer->can($actor, 'order.approve', $order, $context)) {
throw new AccessDeniedException();
}La policy deve ricevere soltanto i dati necessari per decidere: identità, operazione, risorsa e contesto. Evitate di consultare variabili globali o di dipendere implicitamente dalla route corrente; ciò rende le regole difficili da testare e riutilizzare.
Il controllo individuale non basta nelle schermate di elenco. Se una query restituisce ordini di più organizzazioni e poi l'interfaccia ne nasconde alcuni, si è già verificata un'esposizione. Applicate l'ambito nel repository o nel layer di query: filtrate per organizzazione autorizzata, unità consentita o portafoglio assegnato prima di caricare i risultati. Per le risorse per identificatore, verificate sia l'operazione sia l'appartenenza della risorsa.
Stati e transizioni come parte della policy
Le operazioni sensibili dipendono spesso dallo stato. Un approvatore può approvare un ordine in attesa, ma non uno annullato né uno già approvato. Modellate esplicitamente la transizione consentita e convalidatela di nuovo nel punto in cui persiste la modifica. L'interfaccia può disabilitare un pulsante per guidare l'utente, ma la policy del server è il controllo effettivo.
Evitare eccezioni permanenti e dinieghi rivelatori
Ruoli generici come “amministratore” necessitano di limiti chiari. Un amministratore di un'organizzazione non dovrebbe diventare automaticamente amministratore della piattaforma. Analogamente, un'eccezione come “può modificare questo record specifico” deve avere proprietario, motivazione, data di revisione o scadenza e tracciabilità. Se si ripete, probabilmente manca una regola di business o un attributo del modello.
I dinieghi devono essere utili senza rivelare informazioni sensibili. Per una query diretta a una risorsa altrui, normalmente è preferibile una risposta indistinguibile da quella che indica che la risorsa non esiste. In un'azione su una risorsa già visibile, si può indicare che mancano i permessi senza dettagliare regole interne, assegnazioni o attributi protetti.
Registrate azioni sensibili autorizzate e negate quando apportano valore operativo: modifiche dei ruoli, esportazioni, approvazioni, accessi delegati e modifiche della configurazione. L'audit deve includere attore, azione, risorsa, organizzazione o contesto, momento e risultato. Non registrate credenziali, token né dati personali non necessari.
Testare l'autorizzazione come proprietà del prodotto
I test di autorizzazione devono coprire decisioni consentite e negate. Una suite minima include: un utente con permesso sulla propria organizzazione; lo stesso utente di fronte a un'altra organizzazione; un utente senza permesso; una risorsa in uno stato non valido; e una modifica del contesto, come revocare un'assegnazione o terminare una delega.
Testate direttamente le policy perché offrono diagnostica precisa e aggiungete test di integrazione per confermare che route, controller, query e operazioni di scrittura applichino la decisione. Le regressioni più pericolose sono quelle di privilegio: aggiungere un ruolo, una route o un'ottimizzazione della query che amplia l'accesso involontariamente.
- Verificate che un elenco non contenga risorse al di fuori dell'ambito.
- Verificate che conoscere un identificatore altrui non conceda accesso.
- Verificate che una modifica di stato richieda la policy corrispondente.
- Verificate che revocare un permesso invalidi la capacità nella richiesta successiva.
Adottare il modello in un'applicazione con regole disperse
Non è necessario riscrivere l'intero sistema. Iniziate inventariando route, comandi, attività pianificate e punti di esportazione che modificano o espongono dati. Date priorità alle azioni ad alto impatto e alle risorse condivise tra organizzazioni. Estraete una policy per dominio, coprite il comportamento attuale con test e correggete le regole che concedono più accesso del previsto.
Successivamente, sostituite i controlli dispersi con chiamate al servizio di autorizzazione e spostate il filtraggio dell'ambito nelle query. Riesaminate periodicamente permessi inutilizzati, ruoli con troppe capacità, deleghe scadute ed eccezioni attive. La progettazione di permessi e autorizzazione in PHP sarà manutenibile quando una nuova funzionalità potrà rispondere, prima di essere sviluppata, a chi opera, su quale risorsa, a quali condizioni e con quale evidenza è stata verificata.
Checklist per ogni nuovo modulo

- Sono definite le operazioni di business e le relative risorse?
- La matrice distingue permesso, ambito e condizione di stato?
- Le policy sono applicate in lettura, scrittura, esportazione e processi non interattivi?
- Le query filtrano i dati prima di consegnarli all'interfaccia?
- Esistono test di accesso consentito, negato e tra organizzazioni?
- Le azioni sensibili lasciano un audit proporzionato e sicuro?



