Tip/Trick: Adding Authorization Rules to Business and Data Layers using PrincipalPermissionAttributes
Earlier this summer I posted two tutorials on using Windows Authentication with ASP.NET: Enabling Window Authentication within an Intranet ASP.NET Web Application and Implementing Role Based Security within ASP.NET using Windows Authentication and SQL Server. I also linked to Scott Mitchell's great ASP.NET 2.0 Security, Membership and Roles Tutorials that cover how to use Forms Authentication and the new Membership/Roles APIs in ASP.NET for Internet based web applications.
These tutorials covers how to implement authentication on your site, which is the process of identifying who an incoming user is. They also demonstrate how to implement role based management on your site, which allows you to logically group individual users into higher-level roles or groups (for example: "admins", "friends", "subscribers", etc). The tutorials also demonstrate how to implement authorization rules to grant or deny users/roles access to visit individual pages or URLs within a site (the roles tutorial above also demonstrates how to show/hide menu nodes based on the permissions of the incoming user).
Adding Security Authorization Rules to Business and Data Layers
When you authenticate a user within an ASP.NET application, the authenticated user's identity will be automatically flowed throughout that user's request on the server. What this means is that you don't need to manually pass a user's identity around from method to method or class to class. This makes it much easier to implement security authorization rules throughout your application.
One little known feature in .NET is the ability to have the CLR automatically use this identity information to authorize a user's capabilities before instantiating a class, or accessing a method/property on it. This makes it easy to add clean security authorization rules to your business and data layers without having to write much code.
All you need to do to implement this is to use the PrincipalPermissionAttribute within the "System.Security.Permissions" namespace and decorate it on the appropriate class or member on it. For example:
using System.Security.Permissions;
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
public class EmployeeManager
{
[PrincipalPermission(SecurityAction.Demand, Role = "Manager")]
public Employee LookupEmployee(int employeeID)
{
// todo
}
[PrincipalPermission(SecurityAction.Demand, Role = "HR")]
public void AddEmployee(Employee e)
{
// todo
}
}
In the above example, I have added a PrincipalPermission attribute to the "EmployeeManager" class. By adding it I am requiring that a user must be authenticated (logged-in) before this class can be instantiated during a web request (the Authenticated=true demand enforces this). I have also then added two additional security demands on the "LookupEmployee" and "AddEmployee" methods. With the LookupEmployee method I am requiring that the authenticated user for the request is within the "Manager" role in order for the method to be invoked. With the AddEmployee method I am requiring that the authenticated user for the request is within the "HR" role in order for this method to be invoked and have a new Employee added to the system.
And now if I accidentally introduce a security hole within my UI tier and have some code-path that allows a non-Manager/HR employee to cause these methods to be invoked, my business tier will automatically prevent this from happening and raise a security exception.
The PrincipalPermissionAttribute isn't tied to any specific authentication mode. It will work with Forms Authentication, Windows Authentication, Passport Authentication, or any custom authentication mode you want to invent. It will also work with any Role implementation I might use (so if you build or plug-in your own Role Provider in ASP.NET it will just work).
The PrincipalPermissionAttribute type is implemented in the standard CLR mscorlib assembly that all .NET projects compile against. So it isn't ASP.NET specific, and can be used within any application type (including Windows and Console applications). In addition to making it more generically useful, this makes it easier to unit-test business/data libraries built with it.
Using PrincipalPermissionAttributes within Pages and Controls
The PrincipalPermissionAttribute can be used on any class within an application. So in addition to using it within your business and data layers, you can also use it within ASP.NET pages or user-controls you author in your site as well. For example, to enforce that your "MyPage" page can only be used by those within the "Manager" role, you could add a PrincipalPermission attribute to the code-behind of it (below done in VB):
<PrincipalPermission(SecurityAction.Demand, Authenticated:=True, Role:="Manager")> _
Partial Class MyPage
Inherits System.Web.UI.Page
End Class
More on ASP.NET Security
To learn more about ASP.NET security I'd recommend checking out my ASP.NET Security Resources list as well as my ASP.NET Tips and Tricks Page.
There are also now two dedicated books on ASP.NET 2.0 Security out there: Stefan Schackow's Professional ASP.NET 2.0 Security, Membership and Role Management book and the new Developing More Secure Microsoft ASP.NET 2.0 Applications book by Dominick Baier (who runs the great http://www.leastprivilege.com/ blog) that just got published this week:
Hope this helps,
Scott