Lets start off with Why bother to use declarative security attributes? It’s only a few more lines of code to write:
public void DoSomethind()
{
IAuthorizationProvider prov = AuthorizationFactory.GetAuthorizationProvider();
if(!prov.Authorize(Thread.CurrentPrincipal, "important stuff"))
{
throw new SecurityException("Not allowed to do this");
}
}
Versus:
[AuthorizationPermission(SecurityAction.Demand, Context = "important stuff")]
public void DoSomethind()
{
}
But there are two main advantages to the attribute approach (as well as reduced LOC, and cleaner code):
1. The ability to read the metadata. Permview is an example of an application that can read the meta data from an assembly, and spits out the following for the method above:
Method AuthTest.Class1::DoSomethind() NonCasDemand permission set:
<PermissionSet class="System.Security.PermissionSet"
version="1">
<IPermissionclass="Readify.EnterpriseLibrary.Permissions.AuthorizationPermissionXml, Readify.Entlib.Permissions"
version="1"
unrestricted="False"
context="important stuff"/>
< span></PermissionSet>
This then allows us to scan all the assemblies in the application, and build up a manifest of which methods are protected by which authorities.
2. Flexibility – If we want to add additional logging to our attribute, or change the factory behaviour, we have one level of isolation from the underlying authorization source (in this case enterprise library).
OK, well how do we build one? Have a look at the source from here to see a finished custom non-code access security attribute. See this post for an overview of the process.
There are a few tricks to know:
1. The attribute must derive from CodeAccessSecurityAttribute, even if it is not a CAS attribute. If it just derives from SecurityAttribute, then the C# compiler will ignore it (although the VB.NET one does the right thing…)
2. The ToXml() method must create an element with the name IPermission, and there must be a class attribute with the assembly qualified name.
3. The assembly must be strongly named, otherwise you won’t get the compiler to emit the permission.
4. The assembly must either be added to the GAC, or to the Visual Studio devenv.exe directory, for the VS.NET compiler to find it (although the command line compiler does not have the same problem).
5. The attribute must have a single constructor which takes a single SecurityAction parameter. Don’t be tempted to provide multiple parameters to initialize the attribute instead.
6. The debugger is your friend. Debug visual studio by launching it in the program arguments of the project debugging properties, and step through what your permission and attribute is doing.
7. Reflector is critical. When things start going wrong, the error messages are usually totally irrelevant. Look at the implementation of PermissionSet in reflector, and use permview to dump the XML that your attribute created.
8. Unit tests. Unit tests. Unit tests. Unit tests. Did I mention unit tests?
Now, point number 3 was that the assembly had to be strong named. Since we want to call the authorization interface in enterprise library, and the default installation of enterprise library is not strong named, this is a problem.
Well… back to reflection land. I’ve created three wrapper classes, that on class constructor dynamically load the weakly named enterprise library using an assembly string, using reflection to grab MethodInfo instances for each the types, and then has some public wrapper methods that invoke the method infos.
Performance should not be too bad, and should be outweighed by the real authorization costs being performed anyway. From a security perspective (Trojan succeptability), this is no worse than using the IAuthorizationProvider interfaces directly, if the enterprise library assemblies being used are not strongly named.
If you do strongly name your enterprise library, then you can rip out the wrappers, and the calls to the wrappers, and replace with direct calls to the enterprise library factory and interfaces.