CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Forum has moved

New location: CSLA .NET forum


CSLA .NET Resources:
  • CSLA .NET forum
  • CSLA .NET home page
  • Syntax for Instance Level Authorization Rule in CSLA 4

    rated by 0 users
    Not Answered This post has 0 verified answers | 3 Replies | 2 Followers

    Top 500 Contributor
    16 Posts
    Biceman posted on Fri, Aug 13 2010 11:27 PM

    Can someone show me the syntax for adding an instance level authorization rule in CSLA 4?  Rocky's overview of the Authorization Rules for CSLA 4 mentions this functionality but there is no example of how to go about adding this type of rule.

    All Replies

    Top 10 Contributor
    9,475 Posts

    Where possible (specifically for insert/update/delete operations) the Target property of the context parameter is populated with a reference to the business object instance. So your authorization rule can use the Target property to interact with the object instance.

    Obviously in the case of create/fetch there is no instance when the rule runs because the rule is indicating whether it is possible to do the create/fetch before the operation occurs - so Target isn't available in those cases.

    Rocky

    Top 500 Contributor
    16 Posts

    So in order to use instance level authorization for read, edit, delete, and add, I need to create a custom "IsInRole" class ... correct?  Your blog write-up didn't mention that explicitly so I was thinking I could use the Rules.CommonRules.IsInRole version. 

    Do I need to override the logic in the BusinessBase so that it performs an instance level authorization before a Save or Delete (e.g., Rules.BusinessRules.HasPermission(AuthorizationActions.Delete, _myCustomer) ) or does CSLA already do this for me (check at per-type level and per-instance level)?

     

    In the spirit of helping others, I'll explain the solution I came up with:

    My application contains various master data tables (e.g., colors).  The Colors table comes pre-populated with some standard entries.  These are flagged in the database as "system entries".  The user is free to add to this list of color(s).  The authorization requirements are as such:

    • No one can delete a system entry
    • Only System Admins can edit a system entry
    • The "User" role allows you to view a system entry
    • The "User" role allows you to add a new color(s) and/or edit/delete any user created color(s)

    I created a read-only property named "IsSystemEntry" in my ColorBO.

    I created a custom IsInRole class named "SysEntryIsInRole".  I added another cached list of roles (_SysPptyRoles) to the class to store the role(s) which can perform operations on system entries.  The Sub New contains a signature with:

    (Action, SysEntryRoles as List(of String), Roles as List(of String))

    Since Property level authorizations are already instance based, I did not have to worry about them in my custom class (hence there is no place holder for "element" in the Sub New).

    In the Execute method, I check context.target.  If it is Nothing then I do the same look-up processing against the _Roles list that the CommonRules.IsInRole does.  However, if the target contains a reference, then an instance level authorization is being requested.  I cast the target as my ColorBO then reference the IsSystemEntry property.  Only deletion and edit authorization rules are special for system entries so if the authorization action is one of these, I loop over the _SysEntryRoles list just like you normally do for the _Roles list; otherwise I loop over the _Roles list.

    Here's the code which might make things clearer:

    In my ColorBO:

        Protected Shared Sub AddObjectAuthorizationRules()
            Rules.BusinessRules.AddRule(GetType(ColorBO), New Rules.CommonRules.IsInRole(Rules.AuthorizationActions.GetObject, "User"))
            Rules.BusinessRules.AddRule(GetType(ColorBO), New Rules.CommonRules.IsInRole(Rules.AuthorizationActions.CreateObject, "User"))
            Rules.BusinessRules.AddRule(GetType(ColorBO), New SysEntryIsInRole(Rules.AuthorizationActions.EditObject, "System Admin", "User"))
            Rules.BusinessRules.AddRule(GetType(ColorBO), New SysEntryIsInRole(Rules.AuthorizationActions.DeleteObject, "**NoOne**", "User"))
        End Sub

    For the delete rule, I specify the fictitious role "**NoOne**" to indicate no one can perform this task.  I could just as well have passed an empty string.  Again only Edit and Delete require special checking so only those two actions reference my SysEntryIsInRole class.

     

    In my SysEntryIsInRole class:

    Public Class SysEntryIsInRole
        Inherits Csla.Rules.AuthorizationRule

        Private _Roles As List(Of String)
        Private _SysPptyRoles As List(Of String)

    'I actually pass in a single string for the SysPptyRole (System Property Role) because I know I will have at most one entry and it saves me

    'from having to pass a list

    Public Sub New(ByVal Action As Csla.Rules.AuthorizationActions, ByVal SysPptyRole As String, ByVal ParamArray roles() As String)
            MyBase.New(Action)

            _Roles = New List(Of String)
            For Each item As String In roles
                _Roles.Add(item)
            Next

            _SysPptyRoles = New List(Of String)
            _SysPptyRoles.Add(SysPptyRole)

        End Sub

        Protected Overrides Sub Execute(ByVal context As Csla.Rules.AuthorizationContext)
            If context.Target Is Nothing Then  'Entity level authorization check
                CheckAuthorization(context)
            Else  'Instance level authorization check
                Dim o As ColorBO = CType(context.Target, ColorBO)
                If o.IsSystemEntry AndAlso (Me.Action = Csla.Rules.AuthorizationActions.DeleteObject OrElse Me.Action = Csla.Rules.AuthorizationActions.EditObject) Then
                    context.HasPermission = False  'Default is NO
                    If _SysPptyRoles IsNot Nothing Then
                        For Each role As String In _SysPptyRoles
                            If Csla.ApplicationContext.User.IsInRole(role) Then
                                context.HasPermission = True
                                Exit For
                            End If
                        Next
                    End If
                Else
                    CheckAuthorization(context)
                End If
            End If

        End Sub

        Private Sub CheckAuthorization(ByVal context As Csla.Rules.AuthorizationContext)
            If _Roles.Count > 0 Then
                For Each role As String In _Roles
                    If Csla.ApplicationContext.User.IsInRole(role) Then
                        context.HasPermission = True
                        Exit For
                    End If
                Next
            Else
                context.HasPermission = True 'Default is to authorize unless explicitly stated
            End If
        End Sub

    Since I have many master tables that require this same authorization checking, I am going to create in Interface for the IsSystemEntry property so that I can cast any BO calling this rule and obtain the value of "IsSystemEntry".

     

    Top 500 Contributor
    16 Posts

    I need to correct something I stated above.  This following statement is false:

    Since Property level authorizations are already instance based, I did not have to worry about them in my custom class (hence there is no place holder for "element" in the Sub New).

    The business rules contained in "AddBusinessRules" apply at the BO level (i.e., per-type).  The "AddBusinessRules" sub is only called once so you cannot place instance based property level authorization in it.

    One of the property level authorization rules I omitted from my example above states:

    • The Name property can never be changed for system entries

    I mistakenly had code in "AddBusinessRules" as such:

            If IsSystemEntry Then
                'protect Name
                BusinessRules.AddRule(New Rules.CommonRules.IsInRole(Rules.AuthorizationActions.WriteProperty, NameProperty, "**NoOne**"))
            Else
                BusinessRules.AddRule(New Rules.CommonRules.IsInRole(Rules.AuthorizationActions.WriteProperty, NameProperty, "User"))
            End If

    Since the system entries are the first fetched from the database, the Name property was write protected for every ColorBO ... even those created by the user.

    I now realize that I need to handle the read/write authorization for the Name property in my SysEntryIsInRole class since the authorization is based on the IsSystemEntry flag like the instance level edit and delete authorizations.

    Page 1 of 1 (4 items) | RSS

    Copyright (c) 2006-2014 Marimer LLC. All rights reserved.
    Email admin@lhotka.net for support.
    Powered by Community Server (Non-Commercial Edition), by Telligent Systems