Permissões de funções de usuários usando o ASP.NET Core Identity 3

Estou preso a uma solução que gostaria de fornecer em um aplicativo principal do asp.net mvc. Gostaria de fornecer uma solução para os usuários, funções e permissões padrão em um aplicativo da Web, aproveitando a nova abordagem baseada em declarações.

Eu tenho seguido a lógica de Ben Foster aqui (http://benfoster.io/blog/asp-net-identity-role-claims) No código abaixo (qualidade da demonstração), ilustro minha metodologia, que comentarei para ajudar a mostrar minha solução de teste rápida e suja.

O desafio que tenho é que não está funcionando.

// NOTA: Encontrei o bug e comentarei onde errei para futuros usuários que procuram uma solução semelhante.

Classe de propagação: Esta é uma solução rápida e suja para propagar o banco de dados com dois novos usuários, duas funções e algumas reivindicações para uma das funções. Fiz isso como um aplicativo de teste para aprender a abordagem de reivindicações para gerenciar a autorização do meu aplicativo. Minha solução completa fornecerá uma maneira para cada inquilino criar suas próprias funções por meio da interface do usuário, associar 1 ou várias reivindicações às funções e atribuir uma função a um usuário. Eu queria oferecer aos inquilinos uma maneira de gerenciar seus próprios usuários e o que eles podem ou não fazer. Essa é uma implementação simples da abordagem baseada em declarações, pois as reivindicações têm muito mais poder do que as relações 1: 1 com as políticas.

public class DbInitializer
{
    private ApplicationDbContext _context;
    private RoleManager<ApplicationRole> _roleManager;
    private UserManager<ApplicationUser> _userManager;

    public DbInitializer(ApplicationDbContext context,RoleManager<ApplicationRole> roleManager, UserManager<ApplicationUser> userManager)
    {
        _roleManager = roleManager;
        _userManager = userManager;
        _context = context;

    }

    public async Task Initialize()
    {
        //RoleManager<IdentityRole> roleManager = new RoleManager<IdentityRole>();
        //UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>();

        _context.Database.EnsureCreated();

        // Look for any students.
        if (!_context.Users.Any())
        {
            //create user and admin role

            ApplicationUser adminUser = new ApplicationUser();

            adminUser.Email = "[email protected]";
            adminUser.UserName = "Admin";

            var result = await _userManager.CreateAsync(adminUser, "Password-1");

            var newAdminUser = await _userManager.FindByEmailAsync(adminUser.Email);

            ApplicationRole adminRole = new ApplicationRole();

            adminRole.Name = "Admin";
            adminRole.Description = "This is the admin role.";

            await _roleManager.CreateAsync(adminRole);

            await _roleManager.AddClaimAsync(adminRole, new Claim("Can add roles", "add.role"));
            await _roleManager.AddClaimAsync(adminRole, new Claim("Can delete roles", "delete.role"));
            await _roleManager.AddClaimAsync(adminRole, new Claim("Can edit roles", "edit.role"));

            await _userManager.AddToRoleAsync(newAdminUser, adminRole.Name);

            //create user and basic role

            ApplicationUser basicUser = new ApplicationUser();

            basicUser.Email = "[email protected]";
            basicUser.UserName = "Basic";

            var resultBasic = await _userManager.CreateAsync(basicUser, "Password-1");

            var newBasicUser = await _userManager.FindByEmailAsync(basicUser.Email);

            ApplicationRole basicRole = new ApplicationRole();

            basicRole.Name = "Basic";
            basicRole.Description = "This is the basic role.";

            await _roleManager.CreateAsync(basicRole);

            //await _roleManager.AddClaimAsync(basicRole, new Claim("Can add roles", "add.role"));
            //await _roleManager.AddClaimAsync(basicRole, new Claim("Can delete roles", "delete.role"));
            //await _roleManager.AddClaimAsync(basicRole, new Claim("Can edit roles", "edit.role"));

            await _userManager.AddToRoleAsync(newBasicUser, basicRole.Name);

            await _context.SaveChangesAsync();
        }

    }
 }
}

Startup.CS: Depois de criar meus usuários, funções e reivindicações (e associá-los), eu precisava registrar as 'Políticas' no método Confirgure Services da classe Startup.cs. Isso me permite mapear as reivindicações para uma política ou políticas.

 public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();


        services.AddAuthorization(options =>
        {
            options.AddPolicy("Add Role",
                policy => policy.RequireClaim("Can add roles", "add.role"));
            options.AddPolicy("Edit Role",
                policy => policy.RequireClaim("Can edit roles", "edit.role"));
            options.AddPolicy("Delete Role",
                policy => policy.RequireClaim("Can delete roles", "delete.role"));
        });

        services.AddMvc();

        services.AddTransient<DbInitializer>();

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }

Visualização: No meu caso de uso, eu queria restringir o botão 'Adicionar função' a qualquer usuário que não tenha a declaração "Pode adicionar funções" associada à função à qual está atribuída. O restante do código da visualização não é relevante. O problema com o qual deparei é que eu passei o nome da reivindicação para o AuthorizationService.AuthorizeAsync como um segundo parâmetro versus o nome da 'Política' que possui a reivindicação associada. Eu já corrigi-lo abaixo.

@model IEnumerable<ApplicationRoleListViewModel>
@using HailMarry.Models
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService

<br />
<div class="top-buffer"></div>
<div class="panel panel-primary">
<div class="panel-heading panel-head">Application Roles</div>
<div class="panel-body">
    <div class="btn-group">

         //Mistake
        //@if (await AuthorizationService.AuthorizeAsync(User, "Can add roles"))
         //Fix
         @if (await AuthorizationService.AuthorizeAsync(User, "Add Role"))
        {
            <a id="createRoleButton" asp-action="AddRole" asp-controller="ApplicationRole" class="btn btn-primary">
                <i class="glyphicon glyphicon-plus"></i>  Add Role
            </a>
        }
....

Resultado final: eu tenho um usuário "[email protected]" atribuído a uma função "Admin" que possui uma declaração "Pode adicionar funções". Uma função pode ter qualquer número de reivindicações. Criei uma política com a mesma afirmação "Pode adicionar funções" que verifiquei na exibição por meio do IAuthorizationService AuthorizationService injetável. Se o usuário não tiver essa reivindicação atribuída à sua função, a verificação da política que retorna true ou false não mostrará o botão para adicionar a função. Essa mesma lógica de verificação de política pode ser adicionada a um controlador ou a qualquer outro recurso via DI graças ao novo middleware DI de núcleo .net. Durante todo esse exercício, aprendi o poder da Identidade 3, que pode aproveitar coisas como verificação da lógica de negócios. Coisas muito doces, embora os escritores por aí realmente precisem de mais exemplos para nos ajudar a chegar à carne mais rapidamente. De qualquer forma, espero que isso ajude futuros desenvolvedores a procurar uma solução semelhante.

questionAnswers(1)

yourAnswerToTheQuestion