Δυστυχώς, εύκολη λύση δεν υπάρχει σε ένα τέτοιο θέμα. Αν δεν έχεις προβλέψει από την αρχή για ρόλους, δεν προστίθενται εύκολα εκ των υστέρων. Ο λόγος είναι ότι οι ρόλοι επηρεάζουν το UI που βλέπει ο κάθε χρήστης, τις ενέργειες που μπορεί να κάνει, τα δεδομένα που επιτρέπεται να τραβήξει από τη βάση. Και φυσικά, εννοείται ότι υπάρχει και ιεραρχία ρόλων.
Θα σου περιγράψω καταρχήν μία πλήρη υλοποίηση που είχα φτιάξει στο παρελθόν και μετά τί μπορείς να χρησιμοποιήσεις σε μία ήδη έτοιμη εφαρμογή.
Οι ρόλοι ήταν ιεραρχικοί με τους ανώτερους ρόλους να περιέχουν τους κατώτερους. Οι ρόλοι αποθηκεύονταν σε ένα πίνακα με ιεραρχική δομή ο οποίος όμως δεν χρησιμοποιούσε το συνηθισμένο πεδίο ParentID, αλλά την τεχνική "Nested Set" η οποία επιτρέπει την ανάκτηση όλων των παιδιών με ένα select αντί για τη χρήση cursors και εκμεταλλεύεται τα indexes. Οι ρόλοι του κάθε χρήστη κρατούνταν σε ένα πίνακα UserRoles από τον οποίο, με το κατάλληλο ιεραρχικό query μπορούσες να τραβήξεις όλους τους ρόλους που είχε ένας χρήστης, είτε άμεσα είτε λόγω ιεραρχίας.
Τα permissions ορίζονταν σε διάφορα επίπεδα: Ποιές ενέργειες μπορεί να εκτελέσει ο χρήστης, ποιές εγγραφές, ποιές στήλες. Το κάθε είδος permission (action, entity, field) αποθηκευόταν σε διαφορετικό πίνακα με κλειδιά το ρόλο και το user id. Όταν ήθελες να τραβήξεις π.χ. τη λίστα με τα επιτρεπτά actions, ή τις επιτρεπτές εγγραφές απλά περιλάμβανες στο query ένα inner join με τον αντίστοιχο πίνακα και τον πίνακα των ρόλων, γραμμένο έτσι ώστε να επιστρέφει όλους τους ρόλους του χρήστη, και τους υφιστάμενους ρόλους. Έτσι, έπαιρνες τις επιτρεπόμενες εγγραφές. Το πεδίο ρόλου ή χρήστη μπορούσε να έχει συγκεκριμένη τιμή, ή την τιμή All, που σήμαινε ότι ουσιαστικά το πεδίο δεν επηρέαζε το query.
Με τον τρόπο αυτό εξασφαλίζαμε ότι δεν θα έφευγαν καν από τη βάση δεδομένα για τα οποία ο χρήστης δεν είχε permissions.
Έχοντας πάρει τις εγγραφές και τα actions μπορούσες εύκολα να δείξεις τα δεδομένα π.χ. σε ένα grid και να ενεργοποιήσεις μόνο τα menu items ή κουμπιά που επιτρέπονταν. Επειδή χρησιμοποιούσαμε δυναμικό UI, το ίδιο εύκολα μπορούσες να εμφανίσεις μόνο τα πεδία που επιτρέπονταν.
Στην δική σου περίπτωση, τα πράγματα είναι λίγο πιο δύσκολα, καθώς η εφαρμογή δεν έχει ήδη φτιαχτεί με τη λογική των ρόλων. Αυτό που μπορείς να κάνεις είναι να φτιάξεις ένα ιεραρχικό πίνακα με τους ρόλους, τον πίνακα UserRoles και τον πίνακα με τα field permissions, και με ένα join μεταξύ τους θα μπορείς να πάρεις τη λίστα με τα επιτρεπόμενα πεδία. Σημασία έχει πάντως πως θα ορίσεις τα πεδία: Σε επίπεδο object, σε επίπεδο φορμας, σε επίπεδο πίνακα? Αυτό εξαρτάται από το μοντέλο με το οποίο δουλεύει η εφαρμογή σου. Εμείς το είχαμε σε επίπεδο αντικειμένου και φόρμας.
Το δύσκολο είναι το πως θα κάνεις update το UI. Αν αρχίσεις να κρύβεις controls θα δημιουργηθούν κενά στο UI. Χρησιμοποιώντας κάποια layout panels βέβαια μπορείς να το αντιμετωπίσεις κάπως.
Πέρα από το nested set υπάρχουν και άλλοι τρόποι να αποθηκεύσεις ιεραρχίες στη βάση. Τα Nested Sets είναι πολύ γρήγορα στο query αλλά φασαριόζικα στο update. Στην περίπτωση των ρόλων βέβαια, αυτό μας ταιριάζει γάντι, αλλά αν θες να δεις και άλλες μεθόδους, κοίτα το άρθρο "Trees in SQL" όπου περιγράφει μία άλλη τεχνική, τα materialized paths καθώς και τα Common Table Expressions του SQL Server 2005. Τα CTE δεν είναι τόσο γρήγορα όσο οι άλλες μέθοδοι, αλλά είναι ένας εύκολος τρόπος να χρησιμοποιήσεις το παλιό καλο ParentID χωρίς cursors.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos