Il fatto che una persona possa effettuare l’accesso non dimostra che sia autorizzata a consultare o modificare ogni dato dell’applicazione. L’autenticazione identifica l’utente; l’autorizzazione decide che cosa può fare, su quale risorsa e a quali condizioni. Una route può richiedere una sessione valida e, nonostante questo, consentire a qualcuno di leggere l’ordine di un altro account se non viene verificata la relazione tra utente e risorsa.
I test di autorizzazione in PHP devono verificare questo confine dai punti di ingresso utilizzati dall’applicazione: per esempio, una richiesta HTTP a una route web o a un’API. L’obiettivo non è testare il modo in cui un framework implementa le proprie policy, ma confermare il comportamento osservabile: chi può fare cosa, quando la richiesta viene rifiutata e quali dati restano invariati.
Tradurre le regole di accesso in una matrice

Prima di scrivere i test, esprimi le regole usando quattro elementi: attore, azione, risorsa e contesto. Il contesto include le condizioni che modificano il permesso, come appartenere alla stessa organizzazione, essere proprietario del record o avere uno stato specifico. Questa struttura evita di ridurre l’autorizzazione a un elenco di ruoli.
- Attore: utente anonimo, membro, responsabile o amministratore.
- Azione: consultare, creare, modificare, eliminare o approvare.
- Risorsa: un ordine, un documento, un account o un altro oggetto protetto.
- Contesto: proprietario, organizzazione, stato della risorsa o relazione attiva.
Per esempio, una regola può consentire a un membro di consultare i propri ordini, mentre un responsabile può consultare quelli della propria organizzazione. Un amministratore potrebbe avere accesso più ampio, nel rispetto delle regole effettive del prodotto. La matrice trasforma ogni frase di business in casi verificabili e rende visibili le combinazioni mancanti.
Non è necessario testare tutte le permutazioni immaginabili. Dai priorità ai confini di fiducia: un utente senza sessione, due utenti della stessa organizzazione, utenti di organizzazioni diverse, una persona con un ruolo privilegiato e una risorsa che non le appartiene. Aggiungi i contesti speciali che modificano la decisione, come un ordine archiviato se i suoi permessi differiscono da quelli di un ordine attivo.
Preparare scenari di integrazione rappresentativi
Prepara i dati in modo esplicito e con parsimonia. Un test tipico può creare due organizzazioni, un utente per ciascuna e una risorsa associata a una di esse. Poi autentica l’utente che tenta di accedere ed esegui una richiesta alla route pubblica dell’applicazione usando l’identificatore della risorsa. In questo modo vengono esercitati insieme l’ingresso, l’autenticazione, l’autorizzazione e la risposta.
Per ogni regola importante, includi almeno un caso consentito e uno negato. Se l’utente proprietario può modificare una risorsa, verifica che la modifica autorizzata funzioni e che un altro utente non possa ripeterla. Un caso positivo è essenziale: una suite che si aspetta solo rifiuti potrebbe passare anche se l’applicazione bloccasse pure chi ha effettivamente il permesso.
Verifica anche la risorsa inesistente. La risposta a un identificatore sconosciuto può essere diversa da quella a un identificatore esistente ma non autorizzato. Alcune applicazioni usano una risposta di risorsa non trovata per non rivelarne l’esistenza; altre comunicano che l’accesso è vietato. Il test deve riflettere la policy deliberata del prodotto, non imporre una convenzione universale.
Usa factory, builder di fixture o helper di test che producano stati validi e leggibili. Evita di dipendere da identificatori fissi, dall’ordine di esecuzione o da dati condivisi che un altro test potrebbe modificare. Se la persistenza rientra nel comportamento che vuoi verificare, controlla il risultato nel database usando gli strumenti abituali del progetto; non dare per scontato che una risposta positiva garantisca da sola lo stato corretto.
Testare l’isolamento tra utenti e organizzazioni
L’isolamento merita casi dedicati, perché gli errori spesso si manifestano quando si cambia un identificatore nell’URL o nel corpo della richiesta. Crea una risorsa appartenente a un account e prova a consultarla, modificarla o eliminarla usando un’altra identità. Ripeti la verifica tra organizzazioni quando l’applicazione applica ambiti di dati aziendali. Per le operazioni critiche, testa ogni azione: proteggere la lettura non dimostra che siano protetti anche il download, l’esportazione o l’aggiornamento.
Per evitare di duplicare l’intera suite, condividi la preparazione comune e parametrizza solo le dimensioni che esprimono la regola. Per esempio, un elenco di coppie attore-risorsa può definire quali combinazioni sono consentite. Mantieni nomi espliciti per i casi: “un membro di un’altra organizzazione non può modificare l’ordine” è più chiaro di un insieme opaco di valori booleani. Separa gli scenari quando il metodo, la route o gli effetti attesi sono diversi.
Evita di testare ogni combinazione di ruoli e risorse se molte non rappresentano regole distinte. Individua invece le equivalenze giustificate e mantieni casi espliciti per le eccezioni e i confini. Se esistono ruoli gerarchici, non dare per scontato che uno erediti automaticamente tutti i permessi di un altro: verifica la regola effettiva definita dal prodotto.
Verificare le risposte e gli effetti collaterali
In caso di rifiuto, verifica sia la risposta sia che l’operazione protetta non sia stata eseguita. A seconda dell’interfaccia, la risposta può essere un reindirizzamento, uno stato di accesso non autorizzato o vietato oppure una risposta di risorsa non trovata. Verifica il contratto pertinente — stato, formato e, quando conta, messaggio — senza legare il test a dettagli di presentazione non necessari.
Per un aggiornamento negato, verifica che i campi sensibili siano rimasti invariati. Per un’eliminazione, che il record sia ancora disponibile. Per un’operazione che genera una fattura, una notifica o un evento, verifica anche che tale effetto non si sia prodotto. Le asserzioni devono coprire gli effetti importanti per il business, non solo il codice HTTP.
Una richiesta non autorizzata non deve nemmeno consentire modifiche parziali. Se il flusso esegue più operazioni, verifica che il rifiuto avvenga prima delle mutazioni o che la transazione lasci il sistema in uno stato coerente. Non è necessario ispezionare metodi privati per dimostrarlo: osserva la risposta e i dati o gli effetti persistiti.
Mantenere test resistenti ai cambiamenti di implementazione
I test di integrazione devono passare attraverso un’interfaccia stabile dell’applicazione, come una route e una richiesta con l’identità autenticata tramite i meccanismi di test disponibili. Evita di chiamare direttamente una classe interna di policy se vuoi convalidare l’accesso effettivo a una risorsa: così facendo potresti non coprire la registrazione della route, il middleware o il modo in cui viene caricato l’oggetto.
Allo stesso tempo, non trasformare la suite in una replica dell’intera applicazione. Testa il contratto di autorizzazione in punti rappresentativi e riserva i test unitari alle regole pure che richiedono molti casi di contesto. La combinazione aiuta a individuare i problemi: un test della regola può isolare una condizione, mentre il test di integrazione conferma che tale regola protegge il flusso esposto.
Quando cambiano ruoli, route o policy, rivedi la matrice prima di modificare le aspettative. Un test aggiornato solo per accettare il nuovo risultato può nascondere un ampliamento accidentale dei permessi. Documenta il motivo del cambiamento della regola, identifica gli attori che acquisiscono o perdono l’accesso e aggiungi casi che proteggano i nuovi confini.
Lista di controllo per esaminare una modifica

- Sono definiti l’attore, l’azione, la risorsa e il contesto?
- Per la regola interessata esiste almeno uno scenario consentito e uno negato?
- Quando opportuno, viene testato l’accesso tra utenti o ambiti di dati?
- Sono coperti il caso di risorsa inesistente e la policy scelta per non rivelare informazioni?
- La richiesta passa attraverso il punto di ingresso che deve essere protetto?
- Si verifica che un rifiuto non modifichi i dati né generi effetti collaterali?
- I dati di test sono isolati, leggibili e riproducibili?
- Le aspettative riflettono una decisione di prodotto e non un dettaglio accidentale del framework?
Una suite utile non dimostra in astratto che “esistono i permessi”: dimostra che le combinazioni rilevanti funzionano e che quelle non autorizzate non superano il confine. Mantenere questa matrice insieme a test di integrazione concreti facilita la revisione delle modifiche agli accessi senza legare la sicurezza a una specifica implementazione interna.



