Asp.Net Identity, how to use only Roles or Claims?

17

Returning to the same subject: ASP.NET Identity with EntityFramework .

Regardless of the size or planning of my project, if I need to use only the Roles part or just the Claims , is this possible? And most of all, how?

Finally, inhibit the properties and methods related to one or the other and not create the tables in the database related to the one that was disabled.

    
asked by anonymous 15.09.2014 / 03:16

1 answer

20

Responding to the heart of the matter, yes. It is possible.

Using Roles

The default ASP.NET MVC5 project configured with ASP.NET Identity does not have an already configured Role provider deployment, but this is fairly simple to set up:

Step 1: Create a ApplicationRoleManager

ApplicationRoleManager is a Singleton that sets RoleManager in your application. The RoleManager is the ASP.NET Identity implementation for the Roles scheme, very similar to the RoleProvider of ASP.NET Membership.

There are several ways to do this. The one I did is implementing the class within App_Start/IdentityConfig.cs :

public class ApplicationRoleManager : RoleManager<IdentityRole, string>
{
    public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
        : base(roleStore)
    {
    }

    public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
    {
        return new ApplicationRoleManager(new RoleStore<IdentityRole>(new ApplicationDbContext()));
    }
}

(I intend to improve this implementation someday, but it's for the answer for now.)

Step 2: Create a CRUD for Roles

Controllers / RolesController.cs

namespace SeuProjeto.Controllers
{
    public class RolesController : Controller
    {
        private ApplicationDbContext db = new ApplicationDbContext();
        private readonly ApplicationRoleManager _roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole>(db));

        // GET: Roles
        public ActionResult Index()
        {
            var roles = _roleManager.Roles.ToList();
            return View(roles);
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public async Task<ActionResult> Create(IdentityRole role)
        {
            if (ModelState.IsValid)
            {
                await _roleManager.CreateAsync(role);
                return RedirectToAction("Index");
            }

            return View(role);
        }

        public ActionResult Edit(Guid id)
        {
            var role = _roleManager.Roles.SingleOrDefault(r => r.Id == id.ToString());
            return View(role);
        }

        [HttpPost]
        public async Task<ActionResult> Edit(IdentityRole role)
        {
            if (ModelState.IsValid)
            {
                await _roleManager.UpdateAsync(role);
                return RedirectToAction("Index");
            }

            return View(role);
        }

        // GET: Roles/Delete/5
        public ActionResult Delete(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var role = _roleManager.Roles.SingleOrDefault(r => r.Id == id.ToString());
            if (role == null)
            {
                return HttpNotFound();
            }
            return View(role);
        }

        // POST: Roles/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(Guid id)
        {
            var role = await _roleManager.Roles.SingleOrDefaultAsync(r => r.Id == id.ToString());
            _roleManager.Delete(role);
            return RedirectToAction("Index");
        }
    }
}

Views / Index.cshtml

@model IEnumerable<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>

<table class="table">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Users</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var role in Model)
        {
            <tr>
                <td>@role.Id</td>
                <td>@role.Name</td>
                <td>
                    @role.Users.Count
                </td>
            </tr>
        }
    </tbody>
</table>

Views / Create.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.Name)
    @Html.EditorFor(model => model.Name)

    <button type="submit">Enviar</button>
}

Views / Edit.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@using (Html.BeginForm())
{
    @Html.HiddenFor(model => model.Id)
    @Html.LabelFor(model => model.Name)
        @Html.EditorFor(model => model.Name)

    <button type="submit">Enviar</button>
}

Views / Delete.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Color</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
    </dl>

    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-actions no-color">
            <input type="submit" value="Delete" class="btn btn-default" /> |
            @Html.ActionLink("Back to List", "Index")
        </div>
    }
</div>

To insert a user in Role :

var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var currentUser = userManager.FindByName(User.Identity.GetUserName());

var roleresult = userManager.AddToRole(currentUser.Id, "Nome da Role");

To verify that a user is in a Role , the command is the same as the ASP.NET Membership:

if (User.IsInRole("Padrão")) { ... }

The command can be used in Razor, in any View

Using Claims

I do not know if it's worth describing the whole process because Claims is based on the concept of validation policies. It would be necessary to define a validation policy before I can draw up a response (and I would therefore deserve a separate question).

In any case, here is an example of Claims implementation using as policy the zip code of the address of the user .

Addendum: Why can not I change the structure of tables

Within Models\IdentityModels.cs of your project, we have the following class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { ... }

IdentityDbContext has the following implementation in its source code:

    /// <summary>
    ///     Maps table names, and sets up relationships between the various user entities
    /// </summary>
    /// <param name="modelBuilder"></param>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        if (modelBuilder == null)
        {
            throw new ArgumentNullException("modelBuilder");
        }

        // Needed to ensure subclasses share the same table
        var user = modelBuilder.Entity<TUser>()
            .ToTable("AspNetUsers");
        user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        user.Property(u => u.UserName)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") {IsUnique = true}));

        // CONSIDER: u.Email is Required if set on options?
        user.Property(u => u.Email).HasMaxLength(256);

        modelBuilder.Entity<TUserRole>()
            .HasKey(r => new {r.UserId, r.RoleId})
            .ToTable("AspNetUserRoles");

        modelBuilder.Entity<TUserLogin>()
            .HasKey(l => new {l.LoginProvider, l.ProviderKey, l.UserId})
            .ToTable("AspNetUserLogins");

        modelBuilder.Entity<TUserClaim>()
            .ToTable("AspNetUserClaims");

        var role = modelBuilder.Entity<TRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") {IsUnique = true}));
        role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
    }

To avoid constructing these tables, you would have to rewrite IdentityDbContext , failing to use what is already standard. Not that it is impossible, but that for that you would have to reimplement the entire package, which I do not think is reasonable for a desired effect so small.

    
16.09.2014 / 01:10