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
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
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
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
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
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
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
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
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
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