Pular para conteúdo

Guia: Como Adicionar Permissões em Menus

Data de Criação: 2025-11-17 Tipo: Guia Técnico Módulo: Administrativo / Cadastros Complexidade: Média


Índice

  1. Visão Geral
  2. Pré-requisitos
  3. Sistema de Permissões
  4. Passo a Passo
  5. Exemplo Prático
  6. Validação
  7. Troubleshooting
  8. Referências

Visão Geral

Este guia documenta o processo completo para adicionar controle de permissões em novos menus e funcionalidades do sistema SGE.

O que será configurado

  • ✅ Definição do tipo de permissão no enum
  • ✅ Registro das ações permitidas (Listar, Adicionar, Editar, etc.)
  • ✅ Proteção dos controllers com atributos de autorização
  • ✅ Integração com menu da aplicação
  • ✅ Disponibilização na interface administrativa de permissões

Quando usar este guia

  • Ao criar um novo módulo/funcionalidade
  • Ao adicionar uma nova tela que precisa controle de acesso
  • Ao migrar funcionalidades legadas para o novo sistema de permissões

Pré-requisitos

Conhecimentos Necessários

  • ASP.NET Core MVC e Controllers
  • Claims-based Authorization
  • Enums em C#
  • Razor Views

Arquivos Principais

src/Nelmetais.SGE.Business/Enums/
├── PermissaoTypeEnum.cs           # Define os tipos de permissão
├── PermissaoValueEnum.cs          # Define as ações (Listar, Editar, etc.)
└── PermissaoClaimEnum.cs          # Registra permissões disponíveis

src/Nelmetais.SGE.WebApp/
├── Areas/{Area}/Controllers/      # Controllers com autorização
├── Views/Shared/_Layout.cshtml    # Menu principal
└── Extensions/
    └── AuthorizeCustomAttribute.cs # Atributo de autorização

Sistema de Permissões

Arquitetura

O sistema de permissões do SGE usa Claims-based Authorization com três componentes:

  1. PermissaoTypeEnum: Define o tipo de permissão (ex: "Política de Corte")
  2. PermissaoValueEnum: Define a ação permitida (ex: Listar, Adicionar, Editar)
  3. PermissaoClaimEnum: Registra as combinações disponíveis na interface administrativa

Fluxo de Autorização

┌─────────────────────────────────────────────────────────────┐
│ 1. Usuário tenta acessar uma ação do Controller            │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. [AuthorizeCustom] verifica se usuário tem o Claim       │
│    Claim Type = "{PermissaoTypeId}"                         │
│    Claim Value = "{PermissaoValueId}"                       │
└─────────────────────┬───────────────────────────────────────┘
         ┌────────────┴────────────┐
         │                         │
         ▼                         ▼
    ✅ TEM CLAIM              ❌ NÃO TEM CLAIM
    Acesso permitido          Retorna 403 Forbidden

Como funciona na prática

// No Controller
[AuthorizeCustom(PermissaoTypeEnum.PoliticaCorte, PermissaoValueEnum.Listar)]
public async Task<IActionResult> Index()
{
    // Só executa se usuário tiver o claim:
    // Type = "33" (id de PoliticaCorte)
    // Value = "0" (id de Listar)
}

Passo a Passo

Passo 1: Adicionar Tipo de Permissão

Arquivo: src/Nelmetais.SGE.Business/Enums/PermissaoTypeEnum.cs

Adicione um novo valor ao enum com o Display Name apropriado:

public enum PermissaoTypeEnum
{
    // ... outros valores

    [Display(Name = "Nome Exibido no Menu")]
    MinhaNovaPermissao = 87,  // Use o próximo ID disponível

    // ... resto do enum
}

Observações importantes: - ✅ Use um ID único (verifique o último ID usado no enum) - ✅ O Display Name aparecerá no menu e nas permissões - ✅ Use nomes descritivos e claros - ⚠️ Se quiser nomes diferentes para menu e permissões, use texto hard-coded no menu

Exemplo:

[Display(Name = "Tipos de Serviços - NOVO")]  // Aparece nas permissões
TipoServico = 87,

// No menu, pode usar texto hard-coded:
// <li><a>Tipos de Serviços</a></li>  // Sem "- NOVO"


Passo 2: Registrar Permissões Disponíveis

Arquivo: src/Nelmetais.SGE.Business/Enums/PermissaoClaimEnum.cs

Adicione as permissões no método PopuparPermissaoEnum():

private static void PopuparPermissaoEnum()
{
    // ... código existente

    #region[Cadastros] // Ou outra seção apropriada

    // Adicione as 5 permissões básicas (CRUD):
    AddPermissaoEnum(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Listar);
    AddPermissaoEnum(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Detalhar);
    AddPermissaoEnum(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Adicionar);
    AddPermissaoEnum(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Editar);
    AddPermissaoEnum(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Excluir);

    #endregion
}

Permissões disponíveis (PermissaoValueEnum): - Listar = 0 - Visualizar lista - Detalhar = 1 - Ver detalhes - Adicionar = 2 - Criar novo - Editar = 3 - Modificar existente - Excluir = 4 - Remover registro - Outras: Imprimir, Cancelar, Finalizar, etc.


Passo 3: Proteger o Controller

Arquivo: src/Nelmetais.SGE.WebApp/Areas/{Area}/Controllers/{Controller}.cs

3.1 Adicionar Imports Necessários

using Nelmetais.SGE.Business.Enums;
using Nelmetais.SGE.WebApp.Extensions;

3.2 Adicionar Atributos nas Actions

Adicione [AuthorizeCustom] em TODAS as actions do controller:

[Authorize]
[Area("Cadastros")]
[Route("[area]")]
public class MeuController : MainController
{
    // Index - Listar
    [Route("lista")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Listar)]
    public async Task<IActionResult> Index()
    {
        // ...
    }

    // Details - Detalhar
    [Route("detalhes/{id:int}")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Detalhar)]
    public async Task<IActionResult> Details(int id)
    {
        // ...
    }

    // Create GET - Adicionar
    [Route("adicionar")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Adicionar)]
    public IActionResult Create()
    {
        // ...
    }

    // Create POST - Adicionar
    [HttpPost]
    [Route("adicionar")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Adicionar)]
    public async Task<IActionResult> Create(MeuViewModel model)
    {
        // ...
    }

    // Edit GET - Editar
    [Route("editar/{id:int}")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Editar)]
    public async Task<IActionResult> Edit(int id)
    {
        // ...
    }

    // Edit POST - Editar
    [HttpPost]
    [Route("editar/{id:int}")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Editar)]
    public async Task<IActionResult> Edit(int id, MeuViewModel model)
    {
        // ...
    }

    // Delete GET - Excluir
    [Route("excluir/{id:int}")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Excluir)]
    public async Task<IActionResult> Delete(int id)
    {
        // ...
    }

    // Delete POST - Excluir
    [HttpPost, ActionName("Delete")]
    [Route("excluir/{id:int}")]
    [AuthorizeCustom(PermissaoTypeEnum.MinhaNovaPermissao, PermissaoValueEnum.Excluir)]
    public async Task<IActionResult> DeleteConfirmed(int id)
    {
        // ...
    }
}

Observações: - ✅ Cada action GET e POST deve ter seu próprio atributo - ✅ Use a mesma permissão para GET e POST da mesma operação (Create, Edit, Delete) - ⚠️ Actions AJAX/auxiliares podem não precisar de permissões específicas


Passo 4: Atualizar Menu

Arquivo: src/Nelmetais.SGE.WebApp/Views/Shared/_Layout.cshtml

Opção A: Usar EnumHelper (Recomendado)

<li>
    <a class="dropdown-item"
       asp-area="Cadastros"
       asp-controller="MeuController"
       asp-action="Index">
        @(EnumHelper<PermissaoTypeEnum>.GetDisplayValue(PermissaoTypeEnum.MinhaNovaPermissao))
    </a>
</li>

Vantagens: - ✅ Consistente com o nome nas permissões - ✅ Atualização automática se mudar o Display Name - ✅ Padrão usado no resto do sistema

Opção B: Texto Hard-Coded

<li>
    <a class="dropdown-item"
       asp-area="Cadastros"
       asp-controller="MeuController"
       asp-action="Index">
        Meu Menu Personalizado
    </a>
</li>

Vantagens: - ✅ Nome diferente do usado em permissões - ✅ Mais curto/direto - ⚠️ Precisa atualizar manualmente se mudar

Quando usar cada opção: - Use EnumHelper quando o nome do menu = nome da permissão - Use Hard-Coded quando quiser nomes diferentes (ex: "Tipos de Serviços" no menu vs "Tipos de Serviços - NOVO" nas permissões)


Exemplo Prático

Vamos adicionar permissões para o módulo "Tipos de Serviços":

Exemplo Completo

1. PermissaoTypeEnum.cs

[Display(Name = "Tipos de Serviços - NOVO")]
TipoServico = 87,

2. PermissaoClaimEnum.cs

AddPermissaoEnum(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Listar);
AddPermissaoEnum(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Detalhar);
AddPermissaoEnum(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Adicionar);
AddPermissaoEnum(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Editar);
AddPermissaoEnum(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Excluir);

3. TipoServicoController.cs

using Nelmetais.SGE.Business.Enums;
using Nelmetais.SGE.WebApp.Extensions;

[Authorize]
[Area("Cadastros")]
public class TipoServicoController : MainController
{
    [Route("lista-de-tipos-servicos")]
    [AuthorizeCustom(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Listar)]
    public async Task<IActionResult> Index()
    {
        // ...
    }

    [Route("adicionar-tipo-servico")]
    [AuthorizeCustom(PermissaoTypeEnum.TipoServico, PermissaoValueEnum.Adicionar)]
    public IActionResult Create()
    {
        // ...
    }

    // ... outras actions
}

4. _Layout.cshtml

<li class="dropdown-submenu">
    <a href="#" role="button" data-toggle="dropdown" class="dropdown-item dropdown-toggle">
        Serviços
    </a>
    <ul class="dropdown-menu">
        <!-- Nome simples no menu -->
        <li>
            <a class="dropdown-item"
               asp-area="Cadastros"
               asp-controller="TipoServico"
               asp-action="Index">
                Tipos de Serviços
            </a>
        </li>
    </ul>
</li>

Validação

Checklist de Validação

Após implementar, verifique:

1. Código

  • [ ] PermissaoTypeEnum possui o novo valor com Display Name
  • [ ] PermissaoClaimEnum registra todas as permissões necessárias
  • [ ] Controller possui imports de PermissaoTypeEnum e AuthorizeCustomAttribute
  • [ ] Todas as actions do controller possuem [AuthorizeCustom]
  • [ ] Menu foi atualizado no _Layout.cshtml

2. Build e Compilação

dotnet build
# Deve compilar sem erros

3. Interface Administrativa

Acesse a área administrativa de permissões: 1. Login como administrador 2. Navegue para Administrativo > Permissões 3. Verifique se a nova permissão aparece na lista 4. Verifique se todas as ações (Listar, Adicionar, etc.) estão disponíveis

4. Menu

  1. Login como usuário sem permissão
  2. Menu deve aparecer mas acesso deve ser negado (403 Forbidden)
  3. Login como usuário com permissão
  4. Menu deve aparecer e acesso deve funcionar

5. Autorização

Teste cada ação: - [ ] Index (Listar) - Acessível apenas com permissão de Listar - [ ] Details (Detalhar) - Acessível apenas com permissão de Detalhar - [ ] Create (Adicionar) - Acessível apenas com permissão de Adicionar - [ ] Edit (Editar) - Acessível apenas com permissão de Editar - [ ] Delete (Excluir) - Acessível apenas com permissão de Excluir


Troubleshooting

Problema 1: Permissão não aparece na interface administrativa

Causa: Permissão não foi registrada no PermissaoClaimEnum.cs

Solução:

// Adicione no PopuparPermissaoEnum():
AddPermissaoEnum(PermissaoTypeEnum.SuaPermissao, PermissaoValueEnum.Listar);
// ... outras ações


Problema 2: Erro "Cannot implicitly convert"

Causa: Imports faltando no controller

Solução:

using Nelmetais.SGE.Business.Enums;
using Nelmetais.SGE.WebApp.Extensions;


Problema 3: Menu aparece mas acesso retorna 403

Causa esperada: Usuário não tem a permissão configurada

Solução: 1. Acesse área administrativa 2. Configure a permissão para o usuário ou role 3. Faça logout e login novamente


Problema 4: Todos os usuários conseguem acessar

Causa: Faltou adicionar [AuthorizeCustom] nas actions

Solução: Verifique se TODAS as actions possuem o atributo:

[AuthorizeCustom(PermissaoTypeEnum.SuaPermissao, PermissaoValueEnum.Acao)]


Problema 5: ID duplicado no enum

Erro: An item with the same key has already been added

Causa: Dois valores do enum com o mesmo ID

Solução:

// Verifique o último ID usado
[Display(Name = "Última Permissão")]
UltimaPermissao = 86,

// Use o próximo ID disponível
[Display(Name = "Nova Permissão")]
NovaPermissao = 87,  // ✅ Correto


Referências

Arquivos Relacionados

  • src/Nelmetais.SGE.Business/Enums/PermissaoTypeEnum.cs
  • src/Nelmetais.SGE.Business/Enums/PermissaoValueEnum.cs
  • src/Nelmetais.SGE.Business/Enums/PermissaoClaimEnum.cs
  • src/Nelmetais.SGE.WebApp/Extensions/AuthorizeCustomAttribute.cs
  • src/Nelmetais.SGE.WebApp/Views/Shared/_Layout.cshtml

Exemplos no Código

Controllers com permissões implementadas: - TipoUsoMercadoriaController.cs - Exemplo completo de CRUD protegido - TipoServicoController.cs - Exemplo recente - PoliticaCorteController.cs - Exemplo com actions AJAX

Documentação Relacionada

  • docs/features/migracao-politica-corte-preco-cortado.md - Migração que incluiu configuração de permissões
  • ASP.NET Core Authorization: https://docs.microsoft.com/aspnet/core/security/authorization

Notas Importantes

⚠️ Avisos

  1. Não criar migration: Alterações em enums e controllers não requerem migrations
  2. Claims no banco: Os claims são armazenados nas tabelas do ASP.NET Identity (AspNetUserClaims, AspNetRoleClaims)
  3. Cache: Após atribuir permissões, usuário pode precisar fazer logout/login para atualizar claims
  4. IDs únicos: Sempre use IDs únicos no PermissaoTypeEnum

✅ Boas Práticas

  1. Siga o padrão: Use o mesmo padrão de nomenclatura dos outros módulos
  2. Seja específico: Display Names devem ser claros sobre o que a permissão controla
  3. Proteja tudo: Sempre proteja GET e POST da mesma operação
  4. Documente: Atualize este guia se identificar novos casos ou problemas

🎯 Dicas

  • Use TipoUsoMercadoriaController.cs como referência - é um exemplo completo e bem implementado
  • Para permissões especiais (além de CRUD), verifique PermissaoValueEnum.cs para ações disponíveis
  • Teste sempre com usuário sem permissão primeiro para garantir que a proteção está funcionando

Última atualização: 2025-11-17 Autor: Equipe de Desenvolvimento SGE