Impersonation In custom Security

Impersonation In custom Security

Old forum URL: forums.lhotka.net/forums/t/2425.aspx


Mozts2007 posted on Sunday, February 25, 2007

I Have Read Many of The Threads About doing Method Based Authorization. And I Have implanted this. I need to add the capability to Do what My Client calls Overrides

 

Where if the user is In a Role that Can’t run Or Execute The Method say Add A Coupon To An Invoice.  It Would Ask For a User That Can (aka A Manager) To Login for that only.   I Have Add To Access Type enumeration Requires Override. What would be  the best Why To Have the manger Login check Their Roles And the But the Identity Back to the Original user . I have thought about adding a  Ad method To the Identity  and Principal class For Override Login That Takes User Name , Password, And Then old Principal  But how Would I make sure That the old  Principal Is put back.

hurcane replied on Sunday, February 25, 2007

How about writing a "standard" AddCoupon method that does the normal validation. If the user happens to have the ability to add the coupon, then it works.

For the override, overload the AddCoupon method with a version that takes an additional parameter of IPrincipal. Something like this (in pseudo-VB.NET):

Public Sub AddCoupon(ByVal coupon As CouponInfo)
    If UserIsValid() Then
       DoAddCoupon(coupon)
    End If
End Sub

Public Sub AddCoupon(ByVal coupon As CouponInfo, ByVal OverrideUser As IPrincipal)
    Dim origUser as IPrincipal = (Set to the current thread's principal)
    (Set the thread's user to OverrideUser)
    ' Call "standard" add coupon method.
    Me.AddCoupon(coupon)
    (Set the thread's user to origUser)
End Sub

Mozts2007 replied on Sunday, February 25, 2007

I am looking for a Central Why do do this For ALL of my Methods.

Something  Like. ???

Public Sub AddCoupon(ByVal coupon As CouponInfo)
    CanExecuteMethod()      

DoAddCoupon(coupon)
   RestPrincipal()
End Sub

What I Don't  Know How to do Is to get the UI to Know that The User Trying to Execute the Method Can't and Needs to show a Login Window Then Rerun the Method Would I Need to Use a Exception and add this code to the catch block. If So is there a way to this for the whole app. so that i can wright the code once.

 

ajj3085 replied on Monday, February 26, 2007

Moz,

Yes, you can do that for methods, and I've added it myself.  Its fairly easy to add yourself.

The UI can figure out if the user can execute a method by calling CanExecuteMethod( false ) which simply returns true or false, but throws no exception.  In your code above, you'd want to call CanExecuteMethod( true ) to throw an exception if the user attempts to execute the restricted method.

Mozts2007 replied on Tuesday, February 27, 2007

what I am asking or rather try tos ask is how do I Put this code in  the BO not the UI

I want to know how or If  I can call the Method and if  the Method tells me to  ask the user  to login with a user that can execute the method.  how do I that. or do I Just run:

UI code:

what I am asking or rather try tos ask is how do I Put this code in  the BO not the UI

I want to know how or If  I can call the Method and if  the Method tells me to  ask the user  to login with a user that can execute the method.  how do I that. or do I Just run:

UI code:
Private Sub BtnFreeBurgerCoupon_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnFreeBurgerCoupon.Click

        Dim btn As Button = CType(sender, Button)

        Try

            If Invoice.CanExecuteMethod("AddCoupon", False) Then
                Dim coupon As CouponInfo = CouponInfo.GetCoupon(btn.Text)
                InVoice.AddCoupon(coupon)

            Else

                DoOverrideLogIn()

                If Invoice.CanExecuteMethod("AddCoupon", False) Then
                    Dim coupon As CouponInfo = CouponInfo.GetCoupon(btn.Text)
                    Invoice.AddCoupon(coupon)

                End If
            End If
        Finally
            RestPrincipal()
        End Try

    End Sub

But I want my UI code to Be

Private Sub BtnFreeBurgerCoupon_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnFreeBurgerCoupon.Click
 Dim btn As Button = CType(sender, Button)
 Dim coupon As CouponInfo = CouponInfo.GetCoupon(btn.Text)
 InVoice.AddCoupon(coupon)
End Sub

 

And Everything Else Is done in the addCoupon Method.

ajj3085 replied on Tuesday, February 27, 2007

The code you have currently seems to be the best way.. I imagine DoOverrideLogin would require some username or password and thus the UI would have to display a form to gather that information.  You can't display a UI from within your business objects (well you could, but that would be putting UI code in  your BO, which you want to avoid).

In order to do what you want, DoOverrideLogin couldn't display a UI and would always have to succeed... at that point then, there's no reason to restrict access to the addcoupon method, because it would always succeed.

hurcane replied on Tuesday, February 27, 2007

To prompt for the override requires a UI. Your business object should be designed to assist the UI, but it should not be designed to *require* the UI.

I would suggest defining an event to handle retrieving an override user. You mentioned that this override is needed in multiple objects, so I would declare the event separately and also create a custom EventArgs (e.g. LoingOverrideEventArgs) object that includes a property to set the override user (IPrincipal data type).

Your Invoice business object could then have this method:
Public Sub AddCoupon(ByVal coupon As CouponInfo)

    Dim originalUser As IPrincipal = nothing

    If Not Me.CanExecuteMethod("AddCoupon", False) Then
       ' Give the UI a chance to change the user.
       dim eventArgs as New LoginOverrideEventArgs
       RaiseEvent LoginOverride(Me, eventArgs)

       ' Put code here to the assign the thread's principal to the override user.
       ' Use the eventArgs.OverrideUser property, or whatever you decided to call the property.
       ' Save the current principal first, so it can be reset later.
    End If

    If Me.CanExecuteMethod("AddCoupon", False) Then
       ' Here is your existing code that you have in the AddCoupon method.
       ' This is where the invoice adds the coupon to itself.
    Else
       ' What are you going to do if the user doesn't provide a valid login?
       ' Do nothing?
       ' Throw an exception?
    End If
    If originalUser IsNot Nothing Then
       ' Put code here to reset the thread's principal back to the original user.
    End If
End Sub

Your UI code can now look like you want it to look. However, it will need another block of code to handle the LoginOverrideEvent. The event handler should display the login dialog to the user and set the OverrideUser property of the event args object from the login dialog.

The business object wouldn't have to change the thread's principal if your CanExecuteMethod accepted an IPrincipal object as an argument and then checked the roles of the user passed in.

CanExecuteMethod could look like this (shortened pseudo-code version)...

Function CanExecuteMethod( MethodName as String, RaiseEvents As Boolean) As Boolean
  CanExecuteMethod(MethodName, RaiseEvents, CSLA.ApplicationContext.User)
End Function

Function CanExecuteMethod( MethodName as String, RaiseEvents As Boolean, User As IPrincipal) As Boolean
    ' Check the roles here, but using the passed in User.
End Function

In this case, you shouldn't have to track the user and doing any setting or resetting of the principal anywhere.

Does this make any sense?

ajj3085 replied on Wednesday, February 28, 2007

That's also a good solution.  The only drawback as you point out is what to do if the login doesn't get overridden.  You'd probably have to throw an exception at that point... but that could have been avoided by having the UI call CanExecuteMethod before calling AddCoupon in the first place. 

Copyright (c) Marimer LLC