Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Patterns & Practices Enterprise Library Tutorial : Security Application Block

Îåêßíçóå áðü ôï ìÝëïò KelMan. Τελευταία δημοσίευση από το μέλος KelMan στις 18-02-2005, 00:32. Υπάρχουν 0 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  18-02-2005, 00:32 1236

    Patterns & Practices Enterprise Library Tutorial : Security Application Block

    Patterns & Practices Enterprise Library Tutorial

    Security Application Block

    Για το Patterns & Practices Enterprise Library δεν χρειάζεται να πω πολλά… Μπορείτε να βρείτε αρκετές πληροφορίες τόσο μέσα στο DNZ, όσο και στα διάφορα sites που ασχολούνται με αυτό. Παρακάτω, θα σας παρουσιάσω ένα case στο οποίο τα χρησιμοποίησα τόσο στην μορφή που δίνονται out-of-the-box, όσο και με ένα extension που έφτιαξα γι αυτά.

    Το Enterprise Library αποτελείται από μια σειρά Application Blocks τα οποία ουσιαστικά συνθέτουν ένα έτοιμο Framework από το οποίο μπορεί να επωφεληθεί κάθε σύγχρονη εφαρμογή. Για παράδειγμα, αν θέλουμε να υλοποιήσουμε caching μηχανισμό, παρόμοιο με αυτόν του ASP.NET υπάρχει το Caching Application Block. Αν θέλουμε να υλοποιήσουμε έναν μηχανισμό για την αποθήκευση του configuration environment της εφαρμογής, τότε υπάρχει το Configuration Application Block. Αντίστοιχα υπάρχουν το Cryptography Application Block, το Data Access Application Block, το Exception Handling Application Block, το Security Application Block και το Logging and Instrumentation Application Block.

    Ένα από τα ωραία με το Enterprise Library είναι ότι χρησιμοποιεί τον εαυτό του για τις διάφορες λειτουργίες του. Για παράδειγμα, αν επιλέξω να χρησιμοποιήσω οποιοδήποτε Application Block, αυτόματα θα χρησιμοποιηθεί και το Configuration Application Block για να αποθηκευτούν οι πληροφορίες σχετικά με τις ρυθμίσεις μου. Αν χρησιμοποιήσω το Caching Application Block και επιλέξω τα data μου να cachάρονται σε μία βάση, τότε χρησιμοποιείται αυτόματα το Data Access Application Block. Αυτή η υλοποίηση έχει ως αποτέλεσμα έναν πολύ ορθολογικό σχεδιασμό που καθιστά το Enterprise Library πολύ modular αλλά και πολύ εύκολο σε ό,τι αφορά στην κατανόηση του τρόπου λειτουργίας τους και του source κώδικά τους (ο οποίος, παρεμπιπτόντως, παρέχεται προς διασκέδασή σας!).

    Ένα γρήγορο tutorial

    Αφού τελειώσετε με την εγκατάσταση, έχετε τα εξής:

    • Στο \Program Files\Microsoft Enterprise Library\Bin βρίσκονται τα DLLs τα οποία συμπεριλαμβάνετε στα project σας.
    • Στο \Program Files\Microsoft Enterprise Library\QuickStarts μερικές έτοιμες εφαρμογές για να δείτε πως δουλεύουν
    • Στο \Program Files\Microsoft Enterprise Library\src ο source κώδικας για να ρίξετε μια ματιά κάτω από το καπό

    Σε μια εφαρμογή που φτιάχνω αυτόν τον καιρό, χρειάστηκα το Caching Block, το Configuration Block και το Security Block. Επίσης λέω να χρησιμοποιήσω και το Data Access Block αλλά προς το παρόν θα σας πω για την εμπειρία μου στην χρήση του Security Block.

    Ένα από τα requirements της εφαρμογής είναι ότι θα πρέπει οι χρήστες να κάνουν Login ώστε να είναι authenticated. Θα υπάρχουν τόσο εσωτερικοί χρήστες που θα χρησιμοποιούν Windows Forms, όσο και εξωτερικοί anonymous χρήστες που θα χρησιμοποιούν Web Forms καθώς. Οι χρήστες θα υπάγονται σε ένα σύνολο από ρόλους, όπου ανάλογα σε ποιόν ρόλο ανήκουν θα τους επιτρέπονται ή θα τους απαγορεύονται συγκεκριμένες ενέργειες. Τα δικαιώματα για τις διάφορες ενέργειες θα πρέπει να είναι δυναμικά και πολύ πιθανόν να οριστικοποιηθούν παράλληλα με τη χρήση της εφαρμογής. Επίσης, οι κανόνες του authorization είναι του στυλ «Την ενέργεια Α να μπορεί να την κάνει η Χ ομάδα χρηστών αλλά όχι η Ψ εκτός αν αυτός που ανήκει στην Ψ είναι ο Ζ και δεν ανήκει στην ομάδα Ω»

    Κάτι τέτοια είναι κομμένα και ραμμένα για το Security Application Block το οποίο παρέχει βοήθεια τόσο στο κομμάτι του Authentication, όσο και στο κομμάτι του Authorization. Αυτός είναι ένας από τους λόγους που αποφάσισα να φτιάξω ένα proof-of-point application προκειμένου να βεβαιωθώ ότι το Security Application Block μου κάνει.

    Για λοιπόν, θα γίνω Μαμαλάκης και θα σας δείξω ποια είναι η διαδικασία που ακολουθούμε.

    To πρώτο πράγμα που κάνουμε είναι να ξεκινήσουμε τη δημιουργία της εφαρμογής μας. Φτιάχνουμε το Solution (για παράδειγμα ένα απλό Windows Form Application) και προσθέτουμε ένα App.config (Assembly Configuration File) αρχείο. Μέσα εκεί θα αποθηκευτούν όλες οι configuration πληροφορίες της εφαρμογής και θα το χρησιμοποιήσουν το Enterprise Library για να αποθηκεύσει τις configuration πληροφορίες για τα blocks που θα χρησιμοποιήσουμε.

    Το δεύτερο βήμα είναι να τρέξουμε το configuration console του Enterprise Library.

     

     

    Επιλέγουμε File, Open και φορτώνουμε το App.config που δημιουργήσαμε προηγουμένως.

    Κατόπιν, κάνουμε δεξί κλικ πάνω στο Application node και επιλέγουμε New, Security Application Block.

    Αν αυτήν την στιγμή πατήσουμε File, Save θα δούμε ότι στο φάκελο που βρίσκεται η εφαρμογή έχει δημιουργηθεί το αρχείο securityconfiguration.config το οποίο θα περιέχει όλες τις ρυθμίσεις για το Security Application Block ενώ το App.config περιέχει ένα reference προς αυτό.

    Προσοχή γιατί εδώ είναι το πρώτο σημείο που προκαλεί αρκετούς πονοκεφάλους: Καθώς ενημερώνετε τα διάφορα config αρχεία του Enterprise Library (δεν μιλάω για το App.config), θα πρέπει αμέσως να τα κάνετε copy στο bin φάκελο ώστε να τα βρει το assembly όταν θα τρέξει. Βέβαια, αν γράφετε σε C# τότε μπορείτε να το κάνετε αυτό μέσα από τα post-build events.

    Τώρα ήρθε η ώρα για να ρυθμίσουμε το block μας. Κάθε block αποτελείται από μια σειρά providers οι οποίοι ομαδοποιούνται ανάλογα με το functionality που παρέχουν. Στην περίπτωση του Security Application Block έχουμε τις ομάδες Authentication, Authorization, Profile, Roles και Security Cache. Για κάθε μια από τις παραπάνω ομάδες υπάρχουν κάποιοι έτοιμοι providers αλλά μπορούμε να φτιάξουμε και τους δικούς μας.

     

    Στο παράδειγμά μας, το ζητούμενο είναι να γίνεται authentication βάσει πληροφοριών που βρίσκονται σε κάποια βάση. Στον φάκελο \Program Files\Microsoft Enterprise Library\QuickStarts\Security υπάρχει ένα SQL scriptάκι που προετοιμάζει τη βάση δημιουργώντας τους πίνακες και κάποια stored procedures που θα χρειαστούν για να τους χειριστούμε. Αν έχουμε κάποιους άλλους, δικούς μας πίνακες που θέλουμε να χρησιμοποιηθούν γι αυτόν το σκοπό, θα πρέπει να επέμβουμε στο scriptάκι και να δημιουργήσουμε μόνο τα stored procedures αλλάζοντας τα ώστε να αναφέρονται στα objects του δικού μας σχήματος.

     

    Στην περίπτωση του Authentication έχουμε δύο providers, ο πρώτος είναι ο Database Provider και ο δεύτερος ο Windows Provider. Αν κάνουμε δεξί κλικ στο Authentication node και προσθέσουμε τον Database Provider, τότε αυτόματα θα προστεθεί το Cryptography Application Block που χρησιμοποιείται για την κρυπτογράφηση των passwords και το Data Access Application Block μέσω του οποίου θα γίνεται η πρόσβαση στη βάση. Έτσι λοιπόν, πριν να συνεχίσουμε, θα χρειαστεί να ρυθμίσουμε τα δύο νέα blocks. H ρύθμιση του Data Access Application Block είναι πολύ εύκολη. Ουσιαστικά το μόνο που έχουμε να κάνουμε είναι να δηλώσουμε όνομα Database και Server στο Sql Connection String node.

    Στο Cryptography Application block επιλέγουμε έναν Hash Provider κάνοντας δεξί κλικ, New και HashAlgorithm Provider πάνω στο Hash Providers node.

    Προσοχή, το δεύτερο σημείο που προκαλεί πονοκεφάλους: Βάλτε στο Property SaltEnabled την τιμή False. Όταν το salt είναι enabled, τότε κατά το hashing προστίθενται στο τέλος μερικά τυχαία bytes ώστε να αντιμετωπιστούν τα dictionary attacks. Στην δική μας περίπτωση όμως δεν το θέλουμε αυτό γιατί σκοπεύουμε να συγκρίνουμε το hash που βρίσκεται στη βάση με το hash που θα παράγεται από το password που δίνει ο χρήστης.

    Τώρα πλέον, μπορούμε να επιστρέψουμε στο Authentication node και στο Database Provider και εκεί να συνδέσουμε τον Database Provider με τη βάση και το Hash Provider που δηλώσαμε προηγουμένως. Κάνουμε save και copy τα .config αρχεία στο bin φάκελο και είμαστε έτοιμοι να γράψουμε κώδικα!

     

    Αρχικά, θα πρέπει να βάλουμε reference για κάθε block που χρησιμοποιούμε. Είπαμε ότι τα DLLs βρίσκονται στο \Program Files\Microsoft Enterprise Library\Bin, οπότε θα πρέπει να πατήσουμε Browse και να τα τσιμπήσουμε από το συγκεκριμένο φάκελο. Για το παράδειγμα μας, θα χρειαστούμε references στα:

    • Microsoft.Practices.EnterpriseLibrary.Common.dll
    • Microsoft.Practices.EnterpriseLibrary.Configuration.dll
    • Microsoft.Practices.EnterpriseLibrary.Data.dll
    • Microsoft.Practices.EnterpriseLibrary.Security.dll
    • Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.dll
    • Microsoft.Practices.EnterpriseLibrary.Security.Database.dll
    • Microsoft.Practices.EnterpriseLibrary.Security.Database.Authentication.dll

    Έτσι λοιπόν ο απαιτούμενος κώδικας είναι:

    Τα imports για να μην γράφουμε πολλά…

         Imports System.Text
         Imports System.Security.Principal
         Imports Microsoft.Practices.EnterpriseLibrary.Security

    Σε επίπεδο κλάσης, θα χρειαστούμε τις παρακάτω μεταβλητές:

         Private authenticated As Boolean
         Private authenticationProvider As IauthenticationProvider = _
         AuthenticationFactory.GetAuthenticationProvider("Database Provider")
         Private identity As IIdentity

    Και ο κώδικας της ρουτίνας που κάνει το authentication:

         Dim username As String = txtUserName.Text
         Dim password As String = txtPassword.Text
         Dim passwordBytes As Byte() = ASCIIEncoding.ASCII.GetBytes(password)
         Dim credentials As NamePasswordCredential = _
                            New NamePasswordCredential(username, passwordBytes)
         authenticated = authenticationProvider.Authenticate(credentials, identity)
         If (authenticated) Then
             MessageBox.Show("User " & identity.Name & " authenticated")
         Else
             MessageBox.Show("User not authenticated")
         End If

    Παρατηρώντας το παραπάνω, βλέπουμε πόσο απλό είναι να υλοποιηθεί αλλά και ότι αν αλλάξουμε μηχανισμό Authentication διαλέγοντας διαφορετικό Authentication Provider δεν θα χρειαστεί να πειράξουμε καθόλου τον κώδικά μας παρά μόνο στο GetAuthenticationProvider που θα δώσουμε ως όρισμα το όνομα του νέου provider!

    Υπάρχουν κι άλλα πράγματα που θα χρειαστεί να φτιάξουμε, όπως για παράδειγμα τις ρουτίνες που κάπως θα κάνουν management της βάσης, δηλαδή προσθήκη χρηστών και ρόλων, διαχείριση passwords, κλπ. Και πάλι δεν θα χρειαστεί να ανακαλύψουμε τον τροχό καθώς στο SecurityQuickStart (\Program Files\Microsoft Enterprise Library\QuickStarts\Security\VB\SecurityQuickStart) υπάρχει μια έτοιμη κλάση που ονομάζεται SecruityDatabaseManager.vb (παρατηρήστε το ορθογραφικό!) που παρέχει τέτοιες υπηρεσίες.  Έτσι, ο κώδικας για να προσθέσουμε έναν χρήστη είναι απλός:

            databaseManager.InsertUser(txtUserName.Text, txtPassword.Text)

    Παρατηρήστε επίσης ότι ενώ καλούμε το method InsertUser παρέχοντας το password σε απλό string, αυτό αποθηκεύεται σε hashed μορφή ανάλογα με τον HashProvider που έχουμε επιλέξει και όλο το κόλπο γίνεται με τη συνεργασία του Security Application Block με το Cryptography Application Block, όπερ σημαίνει ότι δεν χρειάζεται να ασχοληθούμε με τις λεπτομέρειες της κρυπτογράφησης.

    Στο κομμάτι του Authorization τώρα, θα ξεκινήσουμε πάλι από την κονσόλα για το configuration των blocks.

    Δεξί κλικ στο Authorization node και New, Authorization Rule Provider. To μόνο που μένει είναι να αρχίσουμε να βάζουμε rules. Δεξί κλικ στο RuleProvider node και New, Rule και εμφανίζεται ένα παράθυρο που μας επιτρέπει να κατασκευάσουμε μια boolean έκφραση που θα αποτελέσει το rule “Print Document”. Για να καθορίσουμε που θα βρίσκονται αποθηκευμένοι οι ρόλοι, κάνουμε δεξί κλικ στο Roles node και επιλέγουμε τον ανάλογο provider που στην περίπτωσή μας είναι ο Role Database Provider. Save και δεν ξεχνάμε να κάνουμε copy τα .config αρχεία στο bin και ξαναγυρίζουμε στον κώδικα.

    Σε επίπεδο κλάσης, θα πρέπει να δηλώσουμε

        Private ruleProvider As IauthorizationProvider = _
                  Α
    uthorizationFactory.GetAuthorizationProvider("RuleProvider")

    Και κατόπιν η ρουτίνα για το authorization είναι πολύ απλή:

        Dim principal As IPrincipal = _
            New GenericPrincipal(New GenericIdentity(identity.Name), _
                                                      databaseManager.GetRolesForUser(identity.Name))
        If (Not Me.ruleProvider Is Nothing) Then
            Dim authorized As Boolean = Me.ruleProvider.Authorize(principal, "Print Document Rule")
            If (authorized) Then
                  MessageBox.Show("authorized")
            Else
                  MessageBox.Show("not authorized")
            End If
        End If

    Από κει και πέρα, the sky is the limit… Μπορείτε για παράδειγμα να υλοποιήσετε declarative authorization με χρήση attributes.

    Γυρίζοντας πίσω στο σενάριο για το οποίο χρειάστηκα αυτόν τον μηχανισμό των ρόλων, υπάρχει ένα σημείο που η λύση αυτή δεν με κάλυπτε. Αν επρόκειτο να φτιάξω ένα server side component το οποίο θα κάνει (ή όχι) κάποιες ενέργειες ανάλογα με το ποιος το καλεί, τότε δεν θα υπήρχε πρόβλημα. Ωστόσο στην περίπτωση μιας φόρμας που βρίσκεται client side, οι boolean εκφράσεις που ορίζουν ποιος ρόλος κάνει τι, είναι εκτεθειμένες μέσα στα .config αρχεία τα οποία ανοίγουν με έναν απλό editor και αυτό από άποψη security είναι απαράδεκτο. Γι αυτόν το λόγο χρειάστηκε να φτιάξω τον δικό μου, Custom Authorization Rule Provider ο οποίος θα ψάχνει αυτές τις εκφράσεις μέσα σε μια βάση. Ο κώδικας είναι πολύ απλός:

        Public Class DatastoreAuthorizationProvider
            Inherits ConfigurationProvider
            Implements IAuthorizationProvider

            Private securityConfigView As SecurityConfigurationView
            Public Overrides Sub Initialize(ByVal configurationView As ConfigurationView)
            End Sub

            Public Function Authorize(ByVal principal As IPrincipal, ByVal ruleName As String) _
                                         As Boolean Implements AuthorizationProvider.Authorize
                If principal Is Nothing Then
                    Throw New ArgumentNullException("principal")
                End If
                If ruleName Is Nothing Or ruleName.Length = 0 Then
                    Throw New ArgumentNullException("ruleName")
                End If
                SecurityAuthorizationCheckEvent.Fire(principal.Identity.Name, ruleName)
                Dim booleanExpression As booleanExpression = GetParsedExpression(ruleName)
                If booleanExpression Is Nothing Then
                    Throw New AuthorizationRuleNotFoundException("Authorization rule " & ruleName & " not found in database.")
                End If

                Dim result As Boolean = booleanExpression.Evaluate(principal)
                If result = False Then
                    SecurityAuthorizationFailedEvent.Fire(principal.Identity.Name, ruleName)
                End If
                Return result
            End Function

            Private Function GetParsedExpression(ByVal ruleName As String) As BooleanExpression
                Dim parser As New parser
                Return parser.P***(SQLHelper.GetRuleByName(ruleName))
            End Function

        End Class

    Η κλάση που έχουμε σκοπό να λειτουργήσει ως provider θα πρέπει να κάνει inherit την κλάση ConfigurationProvider και Implement το IauthorizationProvider interface. Σε αυτό καθορίζεται το method Authorize που περιέχει τη λογική του authorization:

    Τα πρώτα δύο If κάνουν ελέγχους για τα arguments.

    To SecurityAuthorizationCheckEvent.Fire χρειάζεται για να μπορεί να συνεργαστεί ο provider.

    Και τώρα αρχίζουν τα ωραία… Πρέπει βάσει του ονόματος του rule να πάρουμε το ανάλογο expression. To expression αυτό είναι σε μορφή text, οπότε ο SQLHelper το τραβάει από τη βάση και ο parser το μετατρέπει σε boolean expression object. Κατόπιν, αυτό γίνεται Evaluate με παράμετρο το principal object που αντιπροσωπεύει τον χρήστη…

    Όλα είναι modular και δεν χρειάζεται να ασχοληθούμε ούτε με το parsing, ούτε με το πώς γίνεται το evaluation

    Το πράγμα γίνεται λίγο περισσότερο σύνθετο αν ο Custom Provider που θέλουμε να φτιάξουμε πρέπει να συνεργάζεται με την κονσόλα (για να διαβάζει settings για παράδειγμα) όπου ακολουθείται η λογική μιας designer κλάσης που συνδέει την κονσόλα με τον provider.

    Το μόνο που μένει πλέον είναι να πάμε στην κονσόλα και να δηλώσουμε τον Custom Authorization Provider.

    Το τελευταίο σημείο προσοχής είναι το εξής: To παραγόμενο DLL του Custom Authorization Provider θα πρέπει να το βάλετε στο \Program Files\Microsoft Enterprise Library\Bin διαφορετικά το Enterprise Library Configuration utility, όταν θα πάει να αποθηκεύσει τα .config αρχεία, θα παραπονιέται ότι το type δεν είναι valid!

    Αυτά τα ολίγα και εισαγωγικά προς το παρόν σχετικά με τα Patterns & Practices Enterprise Library…


    Vir prudens non contra ventum mingit
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems