silentcross
silentcross

Reputation: 53

MVC 6 Group Permission Cannot Get Role

Im converting ASP.NET Identity 2.0: Implementing Group-Based Permissions Management to ASP.NET Core , all is good, but i cannot get the role from groups

heres the code :

ApplicationGroup Entity

public sealed class ApplicationGroup
{
    [Key]
    public string Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public ICollection<ApplicationGroupRole> ApplicationGroupRoles { get; set; }
    public ICollection<ApplicationGroupUser> ApplicationGroupUsers { get; set; }

    public ApplicationGroup()
    {
        Id = Guid.NewGuid().ToString();
        ApplicationGroupRoles = new List<ApplicationGroupRole>();
        ApplicationGroupUsers = new List<ApplicationGroupUser>();
    }

}

ApplicationGroupRole Entity

public class ApplicationGroupRole
{
    public string ApplicationRoleId { get; set; }

    public string ApplicationGroupId { get; set; }
    [ForeignKey("ApplicationGroupId")]
    public ApplicationGroup ApplicationGroup { get; set; }

}

ApplicationGroupUser Entity

public class ApplicationGroupUser
{
    public string ApplicationUserId { get; set; }

    public string ApplicationGroupId { get; set; }
    [ForeignKey("ApplicationGroupId")]
    public ApplicationGroup ApplicationGroup { get; set; }

}

DbContext OnModelCreating :

        modelBuilder.Entity<ApplicationGroup>()
            .HasMany(u => u.ApplicationGroupUsers);

        modelBuilder.Entity<ApplicationGroupUser>()
            .HasKey(r => new {r.ApplicationUserId, r.ApplicationGroupId});

        modelBuilder.Entity<ApplicationGroup>()
            .HasMany(g => g.ApplicationGroupRoles);

        modelBuilder.Entity<ApplicationGroupRole>()
            .HasKey(gr => new { gr.ApplicationRoleId, gr.ApplicationGroupId});

        modelBuilder.Entity<ApplicationGroupUser>().ToTable("ApplicationUserGroups");

        modelBuilder.Entity<ApplicationGroupRole>().ToTable("ApplicationRoleGroups");

        modelBuilder.Entity<ApplicationGroup>().ToTable("ApplicationGroups");

Controller :

    public ActionResult Details(string id)
    {
        if (id == null)
        {
            return new StatusCodeResult(400);
        }
        ApplicationGroup applicationgroup = _groupManager.Groups.FirstOrDefault(g => g.Id == id);
        if (applicationgroup == null)
        {
            return new NotFoundResult();
        }
        var groupRoles = _groupManager.GetGroupRoles(applicationgroup.Id);
        var RoleNames = groupRoles.Select(p => p.Name).ToArray();
        ViewBag.RolesList = RoleNames;
        ViewBag.RolesCount = RoleNames.Count();
        return View(applicationgroup);
    }

View :

@model ApplicationGroup

@{
    ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
    <h4>ApplicationGroup</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Description)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Description)
        </dd>
    </dl>
</div>
<h4>List of permissions granted this group</h4>
@if (ViewBag.PermissionsCount == 0)
{
    <hr />
    <p>No users found in this role.</p>
}
<table class="table">
    @foreach (var item in ViewBag.RolesList)
    {
        <tr>
            <td>
                @item
            </td>
        </tr>
    }
</table>
<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
    @Html.ActionLink("Back to List", "Index")
</p>

The result is when i got to the view, theres no roles, please help fix to fix this, where do i got wrong ? Screen Shoot

Update for solution:

Need to change the controller to this :

public ActionResult Details(string id)
{
    if (id == null)
    {
        return new StatusCodeResult(400);
    }
    var applicationgroup = _groupManager.Groups
            .Where(g => g.Id == id)
            .Select(g => new ApplicationGroup()
            {
                ApplicationGroupRoles = g.ApplicationGroupRoles
            }).FirstOrDefault();

    if (applicationgroup == null)
    {
        return new NotFoundResult();
    }
    var groupRoles = _groupManager.GetGroupRoles(applicationgroup.Id);
    var RoleNames = groupRoles.Select(p => p.Name).ToArray();
    ViewBag.RolesList = RoleNames;
    ViewBag.RolesCount = RoleNames.Count();
    return View(applicationgroup);
}

and need to change some query in other class to :

            var grp = _db.ApplicationGroups
            .Where(y => y.Id == groupId)
            .Select(g => new ApplicationGroup()
            {
                ApplicationGroupRoles = g.ApplicationGroupRoles
            })
            .FirstOrDefault();

Upvotes: 1

Views: 287

Answers (2)

Clint B
Clint B

Reputation: 4710

In Identity 2 I did something similar to the group permissions project you referenced. Except I named them Profiles instead of Groups. I started to do the same thing in Identity Core but then realized there is a new Identity table named UserRoleClaim that I can use instead. With this new table I accomplished the same thing without having to make modifications to the Identity Core database schema. Also, I don't have to write my own EF code. Everything can be done with the built-in methods in the UserManager and RoleManager classes.

You can equate a group to a role now. Each role can have many claims and many users can belong to a role. The key to authorizing users is the claims that are related to the role(s) the user belongs to. You cannot use the claims directly in the authorize attribute (that would be messy). You have to associate claims to a policy. Policies are the awesomeness in Identity Core! They make it so you can organize your claims into profiles. Then you only have to add a profile to your Authorize attribute. Which is much better than adding a list of roles and/or claims like you had to in Identity 2.

The profiles are created with the services.AddAuthorization() method in the ConfigureServices() method of class Startup.

services.AddAuthorization(options =>
{
    options.AddPolicy("Product", policy => policy.RequireClaim("Product"));
    options.AddPolicy("ProductEdit", policy => policy.RequireClaim("Product", "Edit"));
});

You can use these policies in an Authorize attribute.

[Authorize(Policy = "Product")]
public class ProductController : Controller
{
    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }

    [HttpGet]
    [Authorize(Policy = "ProductEdit")]
    public IActionResult Edit()
    {
        return View();
    }
}

I would read up on Authorization in Identity Core before making any design decisions. The documentation is actually pretty good and easy to read. I suggest at least reading these articles.
Claims-Based Authorization
Policy-Based Authorization

I've described the basics of claims and policy based authorization in Identity Core. But follow the rest of the articles in the documentation I linked to above and you will see that there are many more options available to you.

Upvotes: 2

Step 1 is to check your database directly, and make sure that you have some ApplicationGroupRole rows that have an ApplicationGroupId that corresponds to the Id guid that can be seen in that screenshot.

If you do, then your problem might be that you're not including the ApplicationGroupRoles in your query.

To include your ApplicationGroupRoles, you can use the Include Extension method. Make sure you have this using statement.

using System.Data.Entity

And then do your query like this

ApplicationGroup applicationgroup = _groupManager.Groups
    .Include(g => g.ApplicationGroupRoles)
    .FirstOrDefault(g => g.Id == id);

Just a note: You don't need Include statements, if you project your result onto a view model before you return from the db. I.E.

var VM = _groupManager.Groups
    .Where(g => g.Id == id)
    .Select(g => new MyGroupViewModel()
    {
        Roles = g.ApplicationGroupRoles
    })
    .FirstOrDefault();

Upvotes: 1

Related Questions