Updating Roles On The Fly?

Updating Roles On The Fly?

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


Savij posted on Friday, March 02, 2007

Hello,

I am creating an application that uses Windows Authentication. I have two roles: Users & Administrators (this is a proof of concept application). Using CSLA authorizations, I restrict the Users role to read only on a text box (databound to a BusinessBase inherited object). The Administrator role has write permissions. I want to allow an Administrator to enter a domain login & password and allow temporary editing of the textbox. (like a temporary admin override)

I made this work by using a Dictionary<string,bool> in my identity to store the roles. The string is the role name, and the bool marks temporary roles. So if an Administrator walks up and enters their domain login info, the application will add the roles of that Admin and mark them true for temporary. That way at then end of editing, I can just call to remove the temporary roles and the User is left with their roles intact.

The problem is that when I add roles, the UI is not updating anything. The thread is updated with the new roles (using System.Threading.Thread.CurrentPrincipal.IsInRole() sees everything properly). How do I tell the business object to re-check so that it will see the new roles I just added?

I call back to the readWriteAuthorization1.ResetControlAuthorization(); , but it doesnt seem to see the newly added roles?? Am I missing something?

Thanks,

-Jeff

ajj3085 replied on Friday, March 02, 2007

This should be possible; the ptracker example lets you logout and login with a user with more privledges.. so it certainly should be possible.  Check out the sample PTWin, your solution should be in there.

RockfordLhotka replied on Friday, March 02, 2007

The key here is that your principal object's IsInRole() must answer true for the expected roles that have read/write permission to your properties. It doesn't matter how you do that, but that's the requirement.

Savij replied on Friday, March 02, 2007

I don't believe this is true in this case. In this case, I am adding and removing roles at runtime. The CanWriteProperty(true); is returning false all of the time. It seems to have something to do with the write cache. In BusinessBase the call to

VerifyAuthorizationCache() doesnt allow for updated write premissions unless the actual principal changes. The code looks like this:

private void VerifyAuthorizationCache()

{

if (_readResultCache == null)

_readResultCache = new Dictionary<string, bool>();

if (_writeResultCache == null)

_writeResultCache = new Dictionary<string, bool>();

if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal))

{

// the principal has changed - reset the cache

_readResultCache.Clear();

_writeResultCache.Clear();

_lastPrincipal = Csla.ApplicationContext.User;

}

}

Notice that unless the Csla.ApplicationContext.User has changed, it will not clear and update the _readCache and _writeCache. If I comment out the line if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal)) then it acts properly for me, however I am defeating the cache mechanism as it will clear with every property access.

I also tried Csla.ApplicationContext.Clear(); and then resetting the User to the current principal, but that still fails the if statement because it is checking by reference against the last User.

I'm not sure how to handle this yet. Any suggestions would be great. I will continue my investigation at the same time.

-Jeff 

ajj3085 replied on Friday, March 02, 2007

You'll probably want to set the current prinicpal to that of the administrator, run your check, then reset the princpial back to the unprivledged user.  That way you don't defeat the caching mechinism..

JoeFallon1 replied on Friday, March 02, 2007

Can someone please provide some more detail on this caching mechanism and its value?

If a user logs out and logs back in (and is assigned some new roles while logged out) then their principal should not be cached and the new roles should be ineffect. right?

Also, in a Web app, does this cache provide much value with hundreds of users hitting the site?

Joe

RockfordLhotka replied on Friday, March 02, 2007

Oh, I see what’s being talked about here.

 

Yes, it is true that the authorization results per-property are cached, and that the cache is only refreshed if the principal reference is changed to another object. This optimization is important because of the frequency at which CanReadProperty() and CanWriteProperty() are often called by Windows Forms data binding.

 

It isn’t the roles that are cached, it is the authorization results themselves – allowing BusinessBase to entirely short-circuit the looping-through-arrays and calling IsInRole() repeatedly that must occur to actually do the authorization.

 

If a user logs out and back in, it is almost certainly the case that you’d create a new principal object for them, and the result of doing that would trigger the cache to be flushed.

 

What the original poster is doing is different – trying to have their principal “switch modes”.

 

For the purpose of admin override, what I’ve always done is actually swapped principal objects, and so I’ve never encountered this scenario. I think swapping principal objects is a superior answer in any case, if for no other reason that it is the way .NET, Windows, Unix, Linux, Java and other platforms solve the problem. I figure if all the smart security people writing those platforms use a technique, it is probably a good technique.

 

The coding for this idea isn’t hard. You can model it after the WindowsPrincipal/WindowsIdentity approach, where they have a method that allows you to temporarily elevate yourself to another identity, and then to return to your original identity.

 

What you can do is add a couple methods to your principal class

 

  Private Shared _originalPrincipal As IPrincipal

 

  Public Shared Sub SwitchTo(ByVal username As String, ByVal password As String)

    If IsPrincipalElevated Then

      Throw New InvalidOperationException(“Already running as elevated user”)

    End If

    _originalPrincipal = Csla.ApplicationContext.User

    Login(username, password)

  End Sub

 

  Public Shared Sub SwitchBack()

    If Not IsPrincipalElevated Then

      Throw New InvalidOperationException(“Not running as elevated user”)

    End If

    Csla.ApplicationContext.User = _originalPrincipal

    _originalPrincipal = Nothing

  End Sub

 

  Public Shared Function IsPrincipalElevated() As Boolean

    Return (_originalPrincipal IsNot Nothing)

  End Function

 

Using these methods, you can easily create an “elevate user” dialog that gets the supervisor’s username/password and logs them in, but in a way where you can easily revert back to the original user’s identity without any muss or fuss.

 

Rocky

RockfordLhotka replied on Friday, March 02, 2007

You can’t change the roles required by the object, no. What you should be changing are the roles provided by the principal object’s IsInRole method. This is the widely accepted way role-based security is handled, and CSLA just conforms to that norm.

 

Rocky

 

 

From: Savij [mailto:cslanet@lhotka.net]
Sent: Friday, March 02, 2007 12:21 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Updating Roles On The Fly?

 

I don't believe this is true in this case. In this case, I am adding and removing roles at runtime. The CanWriteProperty(true); is returning false all of the time. It seems to have something to do with the write cache. In BusinessBase the call to

VerifyAuthorizationCache() doesnt allow for updated write premissions unless the actual principal changes. The code looks like this:

private void VerifyAuthorizationCache()

{

if (_readResultCache == null)

_readResultCache = new Dictionary<string, bool>();

if (_writeResultCache == null)

_writeResultCache = new Dictionary<string, bool>();

if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal))

{

// the principal has changed - reset the cache

_readResultCache.Clear();

_writeResultCache.Clear();

_lastPrincipal = Csla.ApplicationContext.User;

}

}

Notice that unless the Csla.ApplicationContext.User has changed, it will not clear and update the _readCache and _writeCache. If I comment out the line if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal)) then it acts properly for me, however I am defeating the cache mechanism as it will clear with every property access.

I also tried Csla.ApplicationContext.Clear(); and then resetting the User to the current principal, but that still fails the if statement because it is checking by reference against the last User.

I'm not sure how to handle this yet. Any suggestions would be great. I will continue my investigation at the same time.

-Jeff 



Savij replied on Friday, March 02, 2007

Rocky,

"You can’t change the roles required by the object, no. What you should be changing are the roles provided by the principal object’s IsInRole method. "

I am doing exactly that. I am getting the roles of the administrator and adding it to the current principal. As a matter of fact, I step through the code and I can test for the new roles by doing this:

System.Threading.Thread.CurrentPrincipal.IsInRole("Administrator")

and it does equal true after the principal gets loaded. However, the object still fails the CanWriteProperty() test because the roles are already cached. I am thinking about modifying the BusinessBase (or some inherited object) and allowing a cache clear method for the purpose of re-reading the roles from the principal.

What do you think?

-Jeff

RockfordLhotka replied on Friday, March 02, 2007

If you don’t like my proposal in my previous post (which I do think is the right answer), you can certainly add a ClearAuthorizationCache() method to BusinessBase to solve the problem with your current implementation.

 

I don’t think I’ll add such a thing to the core framework however, because I think that switching principal objects is the correct answer overall, and the framework handles that scenario as-is.

 

Rocky

 

 

From: Savij [mailto:cslanet@lhotka.net]
Sent: Friday, March 02, 2007 1:39 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Updating Roles On The Fly?

 

Rocky,

"You can’t change the roles required by the object, no. What you should be changing are the roles provided by the principal object’s IsInRole method. "

I am doing exactly that. I am getting the roles of the administrator and adding it to the current principal. As a matter of fact, I step through the code and I can test for the new roles by doing this:

System.Threading.Thread.CurrentPrincipal.IsInRole("Administrator")

and it does equal true after the principal gets loaded. However, the object still fails the CanWriteProperty() test because the roles are already cached. I am thinking about modifying the BusinessBase (or some inherited object) and allowing a cache clear method for the purpose of re-reading the roles from the principal.

What do you think?

-Jeff



Savij replied on Friday, March 02, 2007

Rocky,

Yes, you now understand it all correctly. I think I will do as you and others say (elevate the principal), afterall I don't intend to re-invent the wheel. I am however using Active Directory for my users, and the roles are stored in SQL. This is a requirement of the company I work for. What I need to do now is figure out how to create a new principal from an AD account. I am sure there are samples out there and I'll check it out on my own.

If anyone has a sample of code that shows how to create a principal object from a domain account it might short-cut my searching.

Thanks for taking the time Rocky, I understand how it all comes together now.

Jeff

Savij replied on Friday, March 02, 2007

I have it all working with the impersonation with rollback. I will blog the answer and post a link when it's done.

 

Thanks again,

 

Jeff

RockfordLhotka replied on Friday, March 02, 2007

I should say, however, that if you don’t like the way CSLA handles the roles for your objects, the CanReadProperty and CanWriteProperty methods are virtual. You can override them in your custom base class (which I always recommend people create) and entirely replace the way those methods are implemented in your application.

 

Rocky

 

From: Savij [mailto:cslanet@lhotka.net]
Sent: Friday, March 02, 2007 12:21 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Updating Roles On The Fly?

 

I don't believe this is true in this case. In this case, I am adding and removing roles at runtime. The CanWriteProperty(true); is returning false all of the time. It seems to have something to do with the write cache. In BusinessBase the call to

VerifyAuthorizationCache() doesnt allow for updated write premissions unless the actual principal changes. The code looks like this:

private void VerifyAuthorizationCache()

{

if (_readResultCache == null)

_readResultCache = new Dictionary<string, bool>();

if (_writeResultCache == null)

_writeResultCache = new Dictionary<string, bool>();

if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal))

{

// the principal has changed - reset the cache

_readResultCache.Clear();

_writeResultCache.Clear();

_lastPrincipal = Csla.ApplicationContext.User;

}

}

Notice that unless the Csla.ApplicationContext.User has changed, it will not clear and update the _readCache and _writeCache. If I comment out the line if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal)) then it acts properly for me, however I am defeating the cache mechanism as it will clear with every property access.

I also tried Csla.ApplicationContext.Clear(); and then resetting the User to the current principal, but that still fails the if statement because it is checking by reference against the last User.

I'm not sure how to handle this yet. Any suggestions would be great. I will continue my investigation at the same time.

-Jeff 



Copyright (c) Marimer LLC