Sunday 13 May 2012

Domain Based Security Part 1

While I believe some elements of security should be in a centralised Security Service (such as general details about a user, what areas of the enterprise a user is allowed access to), I also believe some elements of security belong in the domain of a specific system, because the rules that surround the security are tied up with the business rules of the application. I'm going to use a task logging system as an example, and focus on a user's ability to edit a task.
So here are the business rules:
  • A basic user can edit their own tasks, but not the tasks of another user.
  • An admin user can edit their own tasks and the tasks of other users.
The user entity should have a method to test it they can edit a task, which takes the task in question as a parameter and returns a boolean that is true if they can. The user has a reference to it's own role, and consequently it's own permissions, and thus knows if it is allowed to edit its own or other user's task. The Task object has a reference to the user it is assigned to. So the CanEditTask method has access to all this information, and thus can work out whether the edit can take place. Here are the entites:
  
public class User : Entity
{
    protected string _username;
    protected Role _role;

    public virtual string Username
    {
        get { return _username; }
        set { _username = value; }
    }
               
    public virtual Role Role
    {
        get { return _role; }
        set { _role = value; }
    }

    public virtual bool CanEditTask(Task task)
    {
        if (task.AssignedTo == this && _role.Permissions.Select(permission => permission.Key).Contains(PermissionKey.EditOwnTask))
            return true;

        if (_role.Permissions.Select(permission => permission.Key).Contains(PermissionKey.EditAnotherUsersTask))
            return true;

        return false;
    }
}

public class Role : Entity
{
    protected string _rolename;
    private IList _permissions;

    public virtual string RoleName
    {
        get { return _rolename; }
        set { _rolename = value; }
    }

    public virtual IList Permissions
    {
        get { return _permissions; }
        set { _permissions = value; }
    }
}

public class Permission : Entity
{
    protected string _key;
    protected string _description;

    public virtual string Key
    {
        get { return _key; }
        set { _key = value; }
    }

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }
 }

 public class Task : Entity
 {
    private string _description;
    private User _assignedTo;

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }

    public virtual User AssignedTo
    {
        get { return _assignedTo; }
        set { _assignedTo = value; }
    }
}

public static class PermissionKey 
{
    public const string EditAnotherUsersTask = "EditAnotherUsersTask";
    public const string EditOwnTask = "EditOwnTask";
    public const string EditTaskForRole = "EditTaskForRole";
}
So the section to focus on is the CanEditTask(Task) method of the User entity. Here is the code for the test, which creates the permissions for editing tasks, creates the basic and admin roles with the required permissions, creates the users with their roles and creates tasks assigned to those user. It then asserts that the correct users can edit the required users tasks.
  
[TestClass]
public class UserTests
{
    [TestMethod]
    public void CheckUserCannotEditAnotherUsersTask()
    {
        Permission editAnotherUsersTaskPermission = new Permission()
        {
            Key = PermissionKey.EditAnotherUsersTask,
            Description = "Edit another user's task"
        };

        Permission editOwnTaskPermission = new Permission()
        {
            Key = PermissionKey.EditOwnTask,
            Description = "Edit own task"
        };

        Role basicRole = new Role()
        {
            RoleName = "Basic User",
            Permissions = new List()
            {
                editOwnTaskPermission
            }
        };

        Role adminRole = new Role()
        {
            RoleName = "Admin User",
            Permissions = new List()
            {
                editAnotherUsersTaskPermission
            }
        };

        User barryBasic = new User()
        {
            Username = "Barry Basic",
            Role = basicRole
        };

        User arnoldAdmin = new User()
        {
            Username = "Arnold Admin",
            Role = adminRole
        };

        Task arnoldsTask = new Task()
        {
            Description = "Arnold's Task",
            AssignedTo = arnoldAdmin
        };

        Task barrysTask = new Task()
        {
            Description = "Barry's Task",
            AssignedTo = barryBasic
        };

        Assert.IsTrue(barryBasic.CanEditTask(barrysTask));
        Assert.IsFalse(barryBasic.CanEditTask(arnoldsTask));
        Assert.IsTrue(arnoldAdmin.CanEditTask(barrysTask));
    }
}
And finally, here is the NHibernate mapping files to map all of this:
  


  
    
      
    
    
    
  





  
    
      
    
    
    
      
      
    
  





  
    
    
      
    
    
    
    

    
      
      
    
    
  




  
    
      
    
    
    
  

No comments:

Post a Comment