Ci sono molti modi diversi di fare le autorizzazioni, ma qui ci sono alcuni suggerimenti.
Ricorda che qui ci sono due programmi: l'interfaccia amministratore, che consente a un amministratore di configurare utenti, gruppi e permessi; e il programma principale, che deve sapere chi ha quali permessi.
Inizia con il programma principale
Le autorizzazioni vengono impostate solo occasionalmente. Ma vengono letti più e più volte. La lettura è molto più importante in questo senso.
Finirai per aggiungere questo tipo di codice a quasi ogni pagina / modulo:
bool ok = HasPermission(user, permissionCode);
e talvolta
AssertHasPermission(user, permissionCode);
o forse
AssertHasPermission(user, permissionCode, Action callbackIfFailed);
quest'ultimo potrebbe essere utile se chiamato in questo modo:
AssertHasPermission(context.User, "108", () => context.Redirect("~/Error.html"));
Vuoi che questo codice funzioni in modo molto efficiente. Qualunque struttura di dati tu usi, deve essere semplice; prendere un utente e un permesso e vedere se esiste. Suggerisco una struttura piatta / denormalizzata. Al momento dell'esecuzione, non dovrebbe essere necessario camminare su un albero, ad esempio, o applicare regole a cascata.
Se i tuoi requisiti includono l'appartenenza al gruppo e le autorizzazioni di gruppo, implementalo in un secondo momento ... vedi sotto.
Una funzione potrebbe non essere una funzione
Per un utente finale, una "funzione" potrebbe essere composta da diverse funzioni nel codice. Per questo motivo, è necessario almeno uno strato di riferimento indiretto tra un'autorizzazione e una base di codice. Ecco perché nell'esempio sopra sto passando un codice di autorizzazione e non, per esempio, il nome di una pagina. Avrai bisogno di questa astrazione nel caso tu abbia bisogno di aggiungere più pagine a una funzione, o se una pagina deve essere suddivisa in diverse funzionalità (ad esempio, leggi e scrivi per lo stesso oggetto dominio).
Gruppi, permessi a cascata e regole di aggiunta / sottrazione
Sembra che i tuoi requisiti includano le autorizzazioni di gruppo. Questo genere di cose è quasi estetico - è principalmente per rendere le cose più facili per un utente finale, cioè gestire più utenti in un blocco inserendoli in un gruppo. Ma poi ci sono delle eccezioni; forse hai bisogno della possibilità di sovrascrivere le autorizzazioni di gruppo per alcuni membri, ad esempio, o hai bisogno di due permessi diversi per accedere a una singola funzione. Puoi impazzire con le opzioni.
La cosa importante è ... mantenere queste opzioni fuori dalla base del codice principale. Il tuo codice principale non dovrebbe interessare se un utente fa parte di un gruppo; importa solo se ha il giusto codice di autorizzazione dopo l'applicazione di tutte le regole di gruppo.
Suggerirei che, dopo aver accettato l'input dall'amministratore, il programma di amministrazione debba mantenere una struttura separata per gruppi e utenti, quindi camminare sulla struttura e comporre la struttura piatta che verrà utilizzata dal programma principale. Puoi pensare a questo come "compilazione" o denormalizzazione.
Se lo fai correttamente, ti risparmierai molto lavoro. In seguito, se i concetti relativi all'appartenenza al gruppo e alle autorizzazioni di gruppo cambiano (ad esempio, aggiungi blacklist e whitelisting o ti integrano con i gruppi di Active Directory, ecc.), L'elenco delle autorizzazioni calcolate dovrebbe rimanere la stessa vecchia struttura piatta alla fine . In questo modo puoi modificare l'interfaccia utente di amministrazione in base al contenuto del tuo cuore e il tuo codice base principale non richiederà alcuna modifica.
YAGNI
Nella mia esperienza, le persone diventano molto creative quando escono le possibilità di gestire le autorizzazioni. In quasi tutti i casi che ho visto, l'implementazione risultante è molto più complessa di quanto sia effettivamente necessario. Mantieni le cose semplici. Non cercare di includere ogni caso d'uso immaginabile. Concentrati invece sull'estensibilità e ricorda che puoi sempre migliorare le strutture del gruppo senza influire sul programma principale.