Wednesday, 16 August 2017

Working with Roles in ASP.NET Identity for MVC

Introduction

ASP.NET Identity provides almost all feature required to perform authentication and authorization for an ASP.NET application however adding a new role, assigning it to a particular user seems to be lost in all these features. In this article, we will learn everything that is required to create a new role, modify role, delete it and manage a role for a particular user in ASP.NET MVC 5+.

Download the complete source code of this article is available at GitHub.

Assumption

Here we are assuming that reader has basic knowledge of ASP.NET MVC and he/she knows how the view, controller and Entity framework works. In case you do not know basics of ASP.NET MVC, please read beginners level articles from ASP.NET MVC articles of DotNetFunda.com.

Lets get started managing Roles in ASP.NET Identity 

By default when an ASP.NET MVC default application is run and auto migration is on, registering a user automatically creates following table (starting with Asp....) in the database where
  1. AspNetRoles - stores roles information contains Id and Name columns
  2. AspNetUsers - stores users information contains Id, UserName, PasswordHash, SecurityStamp and Discriminator columns
  3. AspNetUserRoles - stores user and role id contains UserId and RoleId columns
Explanations of other tables are not given here as those of out of context of this article.



As by default we do not get default Model class for ASP.NET Identity related database tables, so we may need to create our controller and view manually. In this case, we have a separate controller and view folder for Roles.

Application assumption

Here, our assumption is that we already have an IdentityModels.cs class in the Models folder whose code looks like below (in ASP.NET MVC 5, it gets created automatically).
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
In all the Action methods of the RolesController, no authorize attribute has been (to check for the admin user who is authorized to deal with Roles) used just for the shake of clarity and make the code snippet easily readable and understandable.

Creating a new Role with ASP.NET Identity

To create a new Role, we have below view and the code snippet for this is below.


@{
    ViewBag.Title = "Create";
}
<h2>Create Role</h2>
@Html.ActionLink("List Roles", "Index") | @Html.ActionLink("Manage User Role", "ManageUserRoles")
<hr/>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <div>
        Role name
    </div>
    <p>
        @Html.TextBox("RoleName")
    </p>
    <input type="submit" value="Save" />
}
In the above code snippet, we have a simple TextBox named "RoleName" and some general form related code, please note that we do not have a @model directive here. The controller methods looks like below.

Namespace required in order to work with Roles are below

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
Controller method for Create view looks like below
        // GET: /Roles/Create
        public ActionResult Create()
        {
            return View();
        }

        //
        // POST: /Roles/Create
        [HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            try
            {
                context.Roles.Add(new Microsoft.AspNet.Identity.EntityFramework.IdentityRole()
                {
                    Name = collection["RoleName"]
                });
                context.SaveChanges();
                ViewBag.ResultMessage = "Role created successfully !";
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
The first Create method simply returns the Create view and second Create method accepts FormCollection object as parameter and uses context object (instance of ApplicationDbContext) to add a Role to the Roles collection. Note that despite our roles table in the database name is AspNetRoles, the model class name is IdentityRole (part of ASP.NET Identity provider). Calling SaveChanges method saves the new role into the database.

Read my ASP.NET MVC How to Tips & Tricks eBook here.

Listing Roles with ASP.NET Identity


To list roles using ASP.NET Identity, we have below view code.
@model IEnumerable<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>
@{
    ViewBag.Title = "Index";
}
<h2>Roles Listing </h2>

@Html.ActionLink("Create New Role", "Create") | @Html.ActionLink("Manage User Role", "ManageUserRoles")
<hr/>
<div>
    @foreach (var role in Model)
{
    <p><strong>@role.Name | </strong> 
    <span onclick="return confirm('Are you sure to delete?')"><a href="/Roles/Delete?RoleName=@role.Name" class="delLink" style="color:red;">Delete</a></span> | 
    @Html.ActionLink("Edit", "Edit", new { roleName = @role.Name })
    </p>
}
</div>
In the above code snippet, the model of this view is IdentityRole and in foreach loop, all the role from the Roles collection are being listed. While listing roles, we have also created link to Delete and Edit a particular Role.

The controller method to list ASP.NET Identity role looks like below

        public ActionResult Index()
        {
            var roles = context.Roles.ToList();
            return View(roles);
        }

In the above code snippet, we are simply getting the Roles collection from ApplicationDbContext and returning to the View.

Deleting a Role using ASP.NET Identity

Clicking Delete link from the list of Roles deletes a particulrole from the database, and here is the action method of the RolesController.

public ActionResult Delete(string RoleName)
        {
            var thisRole = context.Roles.Where(r => r.Name.Equals(RoleName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
            context.Roles.Remove(thisRole);
            context.SaveChanges();
            return RedirectToAction("Index");
        }
Where we are getting the selected Role from the database and calling Remove method of the Roles collection. Calling SaveChanges method of the ApplicationDbContext object deletes the selected role from the database.

Editing Role using ASP.NET Identity




To edit role, we have above View and the code looks like below

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole
@{
    ViewBag.Title = "Edit";
}
<h2>Edit Role</h2>

@Html.ActionLink("List Roles", "Index") | @Html.ActionLink("Manage User Role", "ManageUserRoles")
<hr />
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.Id)
    <div>
        Role name
    </div>
    <p>
        @Html.TextBoxFor(model => model.Name)
    </p>
    <input type="submit" value="Save" />
}
In the above view code, IdentityRole class is our model whose field is getting populated in the form to edit the role. 

The controller Edit methods looks like below
        //
        // GET: /Roles/Edit/5
        public ActionResult Edit(string roleName)
        {
            var thisRole = context.Roles.Where(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

            return View(thisRole);
        }

        //
        // POST: /Roles/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Microsoft.AspNet.Identity.EntityFramework.IdentityRole role)
        {
            try
            {
                context.Entry(role).State = System.Data.Entity.EntityState.Modified;
                context.SaveChanges();

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
In the above code snippet, the first method accept roleName as parameter and based on that we retrieve the role from the database and returns to the View.

The second method accept IdentityRole as parameter and update the record to the database.

Managing roles for a particular User in ASP.NET Identity


To manage role for a particular user in ASP.NET Identity, we have below view that has more than one form to 
  1. Add a role to the user
  2. Get roles for a user and
  3. Delete/Detach a user from a particular role


The code for the above view looks like below

@{
    ViewBag.Title = "ManageUserRoles";
}
<h2>Manage User Roles</h2>
@Html.ActionLink("Create New Role", "Create") | @Html.ActionLink("Manage User Role", "ManageUserRoles")
<hr/>
<h2>Role Add to User</h2>

@using (Html.BeginForm("RoleAddToUser", "Roles"))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <p>
        Username : @Html.TextBox("UserName")
        Role Name: @Html.DropDownList("RoleName", (IEnumerable <SelectListItem>) ViewBag.Roles, "Select ...")

    </p>

    <input type="submit" value="Save" />
}
<hr/>
<h3>Get Roles for a User</h3>
@using (Html.BeginForm("GetRoles", "Roles"))
{
    @Html.AntiForgeryToken()
    <p>
        Username : @Html.TextBox("UserName")
        <input type="submit" value="Get Roles for this User" />
    </p>
}

@if (ViewBag.RolesForThisUser != null)
{
    <div style="background-color:yellow;">
        <h3>Roles for this user </h3>
        <ol>
            @foreach (string s in ViewBag.RolesForThisUser)
            {
                <li>@s</li>
            }
        </ol>
    </div>
}

<hr />
<h3>Delete A User from a Role</h3>

@using (Html.BeginForm("DeleteRoleForUser", "Roles"))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <p>
        Username : @Html.TextBox("UserName")
        Role Name: @Html.DropDownList("RoleName", (IEnumerable<SelectListItem>)ViewBag.Roles, "Select ...")

    </p>

    <input type="submit" value="Delete this user from Role" />
}
The controller ManageUserRoles method looks like this

 public ActionResult ManageUserRoles()
        {
            // prepopulat roles for the view dropdown
            var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => 
new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
            ViewBag.Roles = list;   
            return View();
        }
Where we are simply getting the Roles list and setting into the Roles ViewBag that will be used to populate the Roles DropDown in the view.

Adding a role to the User using ASP.NET Identity


(DO not get confused and do not worry too much about sentence formation, grammar or proper use of words :), you can also say Adding a user to the Role. Just have fun with this childish title :D)

Where the first Form has simply a UserName textbox and a RoleName dropdown list that contains current Roles from the database. Submitting the form sends form data to RoleAddToUser action method of the RolesController.

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult RoleAddToUser(string UserName, string RoleName)
        {
            ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
            var account = new AccountController();
            account.UserManager.AddToRole(user.Id, RoleName);
            
            ViewBag.ResultMessage = "Role created successfully !";
            
            // prepopulat roles for the view dropdown
            var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
            ViewBag.Roles = list;   

            return View("ManageUserRoles");
        }
In the above method, we are getting UserName and RoleName as parameter.

UserName is being used to get the ApplicationUser from context.Users collection. Then AccountController object is being used to access the UserManager object property and call its AddToRole method by passing UserId from the ApplicationUser object and RoleName coming in from the form.

Next few lines of codes are just to list the roles in the DropDown list again as we are returning to the same (ManageUserRoles) view again.

Getting Roles for a user in ASP.NET Identity



In the second form, we have a UserName textbox and a button. Clicking button submits the form to the GetRoles action method of the RolesController.

The GetRoles controller method looks like below

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult GetRoles(string UserName)
        {            
            if (!string.IsNullOrWhiteSpace(UserName))
            {
                ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
                var account = new AccountController();

                ViewBag.RolesForThisUser = account.UserManager.GetRoles(user.Id);

                // prepopulat roles for the view dropdown
                var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
                ViewBag.Roles = list;            
            }

            return View("ManageUserRoles");
        }
In the above code snippet, we are getting the ApplicationUser object using the UserName. Then AccountController object is being used to get the Roles for that particular UserName using its id.

Deleting a User from a Role

(Another title, have fun :D)

In the third form, we have a UserName textbox and  RoleName dropdown list. Clicking button submits the form to DeleteRoleForUser action method and below is the code snippet for that.

       [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteRoleForUser(string UserName, string RoleName)
        {
            var account = new AccountController();
            ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

            if (account.UserManager.IsInRole(user.Id, RoleName))  
            {
                account.UserManager.RemoveFromRole(user.Id, RoleName);
                ViewBag.ResultMessage = "Role removed from this user successfully !";
            }
            else
            {
                ViewBag.ResultMessage = "This user doesn't belong to selected role.";
            }
            // prepopulat roles for the view dropdown
            var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
            ViewBag.Roles = list;

            return View("ManageUserRoles");
        }
In the above code snippet, we are getting ApplicationUser and then checking whether this user  belongs to the selected Role or not (using AccountController object), if it is then calling the RemoveFromRole method by passing UserId and RoleName parameter that removes the user from the role.

Source code: The entire source code for this article can be downloaded from GitHub directly, also you are most welcome to enhance or improve it by participating in it on GitHub.  

Conclusion

In this article, we learnt how to develop User Interface to create a new Role, Edit a role, Delete a role, View roles attached to a particular user and manage a role for a particular user using ASP.NET Identity in ASP.NET MVC.

Hope this article would be useful. 

If you liked this article, please share this to your friends and colleagues and do let us know your comment or feedback. Thanks for reading.

Reference


  • http://www.asp.net/identity

No comments:

Post a Comment