Friday, December 17, 2010

Enterprise Library Security Application block – Rule based authorization

Enterprise library security application block's Authorization provider can be used to create an effective solution for rule based security implementation in your application code. The security application block can be used to configure an Authorization provider and use this to map task based authorization to complex combination of roles. Later you can use these rules to verify permissions on the current user to perform an action.
In this post, I’ll show a simple implementation of a rule based authorization provider and its uses. The data model used in our sample looks like

I have created some SQL scripts to insert data into the relevant tables.  
INSERT INTO [dbo].[Role]([Id] ,[Name]) VALUES (NEWID() ,'SuperUser')
INSERT INTO [dbo].[Role]([Id] ,[Name]) VALUES (NEWID() ,'Guest')
INSERT INTO [dbo].[Role]([Id] ,[Name]) VALUES (NEWID() ,'NormalUser')
GO


INSERT INTO [dbo].[User]([Id] ,[FirstName] ,[LastName] ,[Email] ,[Password])
     VALUES (NEWID() ,'SuperUserFName' ,'SuperUserLName' , 'SuperUser@Company.com' , 'SuperUserPassword')
INSERT INTO [dbo].[User]([Id] ,[FirstName] ,[LastName] ,[Email] ,[Password])
     VALUES (NEWID() ,'GuestFName' ,'GuestLName' , 'Guest@Company.com' , 'GuestPassword')
INSERT INTO [dbo].[User]([Id] ,[FirstName] ,[LastName] ,[Email] ,[Password])
     VALUES (NEWID() ,'NormalUserFName' ,'NormalUserLName' , 'NormalUser@Company.com' , 'NormalUserPassword')
INSERT INTO [dbo].[User]([Id] ,[FirstName] ,[LastName] ,[Email] ,[Password])
     VALUES (NEWID() ,'ITDeptFName' ,'ITDeptLName' , 'ITDept@Company.com' , 'ITDeptPassword')
GO


DECLARE @SuperUserId uniqueidentifier
DECLARE @SuperRoleId uniqueidentifier
SELECT @SuperUserId = Id FROM [User] WHERE [FirstName] = 'SuperUserFName'
SELECT @SuperRoleId = Id FROM [Role] WHERE [Name] = 'SuperUser'

INSERT INTO [dbo].[UserRole]([UserId], [RoleId] ,[Id])
     VALUES(@SuperUserId ,@SuperRoleId ,NEWID())
GO
Next we’ll configure rules using the Enterprise Library security application block.
1.       Open the App.Config file in the Enterprise Library Configuration editor and select Add Security Sections from the Blocks menu.
2.       Add a new Authorization rule provider as shown in the image below.

3.       Give an appropriate name and add a new Authorization rule to the provider. In this sample I have used the name as MyBusinessRule
4.       We will create two rules (AddProfile and ApproveProfileChanges).

5.       For the AddProfile rule add the Rule Expression as R:SuperUser AND R:NormalUser
6.       For the ApproveProfile rule the rule expression is R:SuperUser
7.       Save and close your config file.
8.       The final output looks like
<configSections>
  <section name="securityConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Security.Configuration.SecuritySettings, Microsoft.Practices.EnterpriseLibrary.Security, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" requirePermission="true" />
configSections>
<securityConfiguration defaultAuthorizationInstance="MyBusinessRule">
  <authorizationProviders>
    <add type="Microsoft.Practices.EnterpriseLibrary.Security.AuthorizationRuleProvider, Microsoft.Practices.EnterpriseLibrary.Security, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null"
      name="MyBusinessRule">
      <rules>
        <add expression="R:SuperUser" name="Approve Profile Changes" />
        <add expression="R:SuperUser AND R:NormalUser" name="Add Profile" />
      rules>
    add>
  authorizationProviders>
securityConfiguration>
9.       In your business layer add references to the following assemblies
·         Microsoft.Practices.EnterpriseLibrary.Common.dll
·         Microsoft.Practices.EnterpriseLibrary.Security.dll
·         Microsoft.Practices.ServiceLocation.dll
10.   Create a class RuleAuthorizationManager and add the authorization logic for the user role for a configured rule.
public class RuleAuthorizationManager
{
    public static bool IsUserAuthorized(string ruleName)
    {
        var ruleProvider = EnterpriseLibraryContainer.Current.GetInstance<IAuthorizationProvider>();
        return ruleProvider.Authorize(Thread.CurrentPrincipal, ruleName);
    }
}
11.      In the service class you can use the IsUserAuthorized method to check for permissions.
public void AddRoleToUser(Guid userId, Role role)
{
    if(!RuleAuthorizationManager.IsUserAuthorized(RuleNames.AddProfile))
        throw new SecurityException("User not authorized to add profile");

    var repository = new Repository(new UserModelContainer());
    var user = repository.GetSingle<User>(u => u.Id == userId);

    //Code to add a role to user.
}
Unit tests
[TestMethod()]
[ExpectedException(typeof(SecurityException))]
public void ApproveProfileChanges_should_throw_security_exception_if_user_does_not_have_permission_to_add_profile()
{
    UserService target = new UserService();
    User user = new Repository(new UserModelContainer()).GetSingle<User>(u => u.FirstName == "GuestFName");
    IIdentity identity = new GenericIdentity("Guest@Company.com");
    string[] roles = new string[] { "Guest" };

    IPrincipal principal = new GenericPrincipal(identity, roles);
    Thread.CurrentPrincipal = principal;

    target.ApproveProfileChanges(user);
}

No comments: