![]() |
Show Changes |
![]() |
Edit |
![]() |
|
![]() |
Recent Changes |
![]() |
Subscriptions |
![]() |
Lost and Found |
![]() |
Find References |
![]() |
Rename |
| Search |
History
| 7/26/2004 12:02:57 PM |
![]() |
List all versions |
There's a simple pattern for using this property. Plumbing sets it, and application code reads it. The most common example of this is the ASP.NET plumbing that sets Thread.CurrentPrincipal1. The page (application code) can then read the value any time it needs to make an authorization check against its client. Here's the basic idea.
// plumbing provided by a framework like ASP.NET
class Plumbing {
ApplicationCode appCode;
public void DoHeavyLiftingThenCallAppCode() {
// plumbing...
Thread.CurrentPrincipal = _authenticateUserSomehow();
// more plumbing...
appCode.RunBusinessLogic();
}
}
// application code provided by developer (e.g., ASPX page)
class ApplicationCode {
public void RunBusinessLogic() {
_methodOne();
if (Thread.CurrentPrincipal.IsInRole("Staff")) {
_methodTwo();
}
_methodThree();
_methodFour();
}
void _methodOne() {}
void _methodTwo() {}
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
void _methodThree() {}
[PrincipalPermission(SecurityAction.Demand, Role="Managers")]
void _methodFour() {}
}
Note that Thread.CurrentPrincipal can be tested two ways: imperatively and declaratively. The application apparently doesn't care about the client's identity when calling _@methodOne@, but in this particular case it wants to restrict the call to _@methodTwo@ so that it's only called if the client principal is in a role called Staff. Contrast this to _@methodThree@, which is always protected by a declarative attribute. This is logically the same as writing the following code at the beginning of _@methodThree@.
if (!Thread.CurrentPrincipal.Identity.IsAuthenticated) {
throw new SecurityException();
}
In our case the CLR handles this check automatically because of the attribute on the method.
Finally _@methodFour@, which is also declarative, tests for the presence of a particular role. Keep in mind that if you're using a WindowsPrincipal, the roles are based on fully qualified group names, which include either domain names (for domain groups) or machine names (for local groups). You really don't want to be hardcoding machine or domain names into an application. But remember what this turns into.
if (!Thread.CurrentPrincipal.IsInRole("Managers")) {
throw new SecurityException();
}
Nothing stops you from providing a custom implementation of IsInRole by simply implementing IPrincipal yourself. I discussed this idea in Item 15 (WhatIsSecurityContext). If you're using Forms Authentication in ASP.NET, you're already using a different implementation called GenericPrincipal, and you can configure the roles however you like. In fact, version 2.0 of the .NET Framework provides an entire role management infrastructure for Forms Authentication (Brown 2004) and yet another implementation of IPrincipal.
Note that you can also place PrincipalPermissionAttribute on classes, which means that the restriction applies to all methods of the class, as shown in Figure 34.1.
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public class AuthenticatedUsersOnlyClass {
[PrincipalPermission(SecurityAction.Demand, Role="Managers")]
public void ManagersOnly() {}
[PrincipalPermission(SecurityAction.Demand, Role="Contractors")]
[PrincipalPermission(SecurityAction.Demand, Role="Employees")]
public void ContractorsOrEmployees() {}
}
Figure 34.1 More sophisticated uses of PrincipalPermissionAttribute
Be careful about using this attribute at the class level. If the class to which you apply it happens to have a static constructor (or, even worse, if it may get one in the future), realize that this attribute attribute applies to the static constructor as well! Why is this a problem? Well, if a static constructor throws an exception, the class is latched into a mode where each future attempt to call the static constructor leads to the previous exception being rethrown (Brumme, 2003). So, if the first caller to use the class doesn't satisfy the permission demand, no callers in the entire AppDomain will be able to use that class!
Figure 34.1 also demonstrates another gotcha: When you stack up multiple instances of PrincipalPermissionAttribute on a method (or even a class), the effect is an "OR," not an "AND." So the ContractorsOrEmployees method can be called by anyone in the Contractors role or in the Employees role. You don't have to be in both roles to call this method. Watch out for this, because a misunderstanding here could lead to a security hole!
It's not rocket science, but this is what the .NET Framework calls role-based security. There's not a whole lot to it, as you can see. But the hooks are there to build some very interesting plumbing on top of what the CLR provides.
1 The heavy lifting is done by various modules, such as WindowsAuthenticationModule and friends, during the AuthenticateRequest event; then the DefaultAuthenticationModule sets up this property during the undocumented DefaultAuthentication event that immediately follows.
Keith's first book-in-a-wiki. If you would like to read the book online or order a physical copy to throw at annoying coworkers, surf to the HomePage. Please note that due to overwhelming wikispam, this particular wiki is no longer editable.
About FlexWiki.
Recent Topics