Search code examples
asp.net-mvcpermissionsasp.net-identity.net-framework-versionasp.net-roles

C# ASP.NET Identity 2 (.NET Framework MVC) - Using permission AND roles


I'm trying to make a permission/role authorization app. My idea is to mix roles and permissions so that the admin can create different roles with differnet permissions to do some stuff.

Example,

I would like to put something like this in my controler:

namespace MyApp.Controllers
{
    public class SettingsController : Controller
    {
        // All the ASP.NET Identity UserManager, SignInManager
        and RoleManager configuration

       [HasPermission(Permission = "SensitiveData")]
       // GET: /Settings/SensitiveData
       public ActionResult SensitiveData()
       {
           // Collect some information to display
           return View()
       }

       [HasPermission(Permission = "ListUsers")]
       // GET: /Settings/ListUsers
       public ActionResult ListUsers()
       {
           // Collect some information to display
           return View()
       }
    }
}

My idea was to create two extra tables to the initial five that "come" with ASP.NET Identity, one called AspNetPermissions and another one called AspNetRolePermissions

AspNetPermissions would have Id and Permission fields. AspNetRolePermissions would have RoleId and PermissionId.

The Admin user is able to create, edit or delete roles. The roles would become just the friendly way to group permissions. So, for example, I want to allow all users under the Maintenance role to be allow to use "SensitiveData" and "ListUsers" actions from my previous controller and users under the Marketing role to be allow to use only the "ListUsers" action. I would create from an admin account the role "Maintenance" add set permissions to "SensitiveData" and "ListUsers". Then I would follow the same concept to create the role "Marketing", so de DBs would look something like:

-------------------------------
       AspNetPermissions*
-------------------------------
Id             | Permission
---------------|---------------
permissionId1  | SensitiveData
permissionId2  | ListUsers
...            | ...

*This one is preloaded, not even admins can create permissions

-------------------------------
       AspNetRolePermissions
-------------------------------
RoleId         | PermissionId
---------------|---------------
maintenanceId  | permissionId1
maintenanceId  | permissionId2
marketingId    | permissionId2

Finally I would add the roles to some users (that part I have it working).

What I can't achieve is to:

  1. Create the AspNetPermissions and AspNetRolePermissions
  2. Create the relation between AspNetRolePermissions with AspNetPermissions and AspNetRoles
  3. Create the filter HasPermission(Permission = "somepermission")
  4. Check if the the user trying to access has a role that is paired with the required permission in AspNetRolePermissions

I need some help with that, I've been reading the whole day and I couldn't find anything useful, just a couple of things that I can't tide with what I need.

I think that seeing this from this point is the best way, because the admin user can create a role, set the permissions for that role and then add that role to howmany users he or she wants to. If I set the permissions directly to the user is harder to change the permissions in the future when I have more than just a couple of users.


EDIT


I've managed to do this:

On IdentityModel.cs I've added ApplicationRole and UserPermission and edited ApplicationDbContext in this way:

public class ApplicationRole : IdentityRole
{
    public ApplicationRole()
    {
    }

    public ApplicationRole(string name)
        : base(name)
    {
    }

    //

    public virtual ICollection<UserPermission> Permissions { get; private set; }
}

public class UserPermission
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public string Permission { get; set; }
    public virtual ICollection<ApplicationRole> Roles { get; private set; }

}

public class UserRolePermissions
{
    public string RoleId { get; set; }
    public string PermissionId { get; set; }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    const string connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;Database=LocalTestingDB;trusted_connection=yes;";
    public ApplicationDbContext()
        :base(connectionString, throwIfV1Schema: false)
    {
    }

    public DbSet<UserPermission> Permissions;

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<UserPermission>()
            .ToTable("AspNetPermissions")
            .HasKey(x => x.Id);

        modelBuilder.Entity<ApplicationRole>()
            .HasMany<UserPermission>(ar => ar.Permissions)
            .WithMany(up => up.Roles)
            .Map(arup =>
                {
                    arup.MapLeftKey("RoleId");
                    arup.MapRightKey("PermissionId");
                    arup.ToTable("AspNetRolePermissions");
                });
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

That creates the AspNetPermissions table along with the AspNetRolePermissions table for the many to many relation and gives me more customization power over the Roles table (not using it that much but is good to know that I have it, I think).

On FilterConfig.cs, under the FilterConfig class I added:

public class HasPermissionAttribute : ActionFilterAttribute
{
    private string _permission;

    public HasPermissionAttribute(string permission)
    {
        Permission = permission;
    }

    public string Permission
    {
        get
        {
            return _permission;
        }
        private set
        {
            _permission = value;
        }
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!CheckIfRoleHasPermission(Permission))
        {                
            var url = new UrlHelper(filterContext.RequestContext);
            var loginUrl = url.Content("/Cuenta/Ingresar");
            filterContext.HttpContext.Response.Redirect(loginUrl, true);
        }
        //base.OnActionExecuting(filterContext);
    }

    public bool CheckIfRoleHasPermission(string perm)
    {
        return false;
    }
}

I need to create a method called CheckIfRoleHasPermission that can take the current user role and check if there is any permission asociated with that role. Any idea on how to do that? Or any correction to what I've already done?


Solution

  • I did it, if anyone is in the same situation, I just uploaded to GitHub my code. Here's the link: REPO-NO-LONGER-AVAILABLE. There are some parts in spanish but I'll try to make a guide explaining where is the core part of my code so it can be added to any project and later I'll also try to separate everything into a different project and then upload the code to NuGet. Anyone who wants to increase the functionality or collaborate with the repo is welcome!

    EDIT

    The Project is now way more advanced, I made a guide explaining what it does. Any reviews are welcomed (I'll take the negative ones to improve it)

    EDIT 2

    Obsolete by now.