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
- Visão Geral
- Pré-requisitos
- Sistema de Permissões
- Passo a Passo
- Exemplo Prático
- Validação
- Troubleshooting
- 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:
- PermissaoTypeEnum: Define o tipo de permissão (ex: "Política de Corte")
- PermissaoValueEnum: Define a ação permitida (ex: Listar, Adicionar, Editar)
- 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
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
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
PermissaoTypeEnumeAuthorizeCustomAttribute - [ ] Todas as actions do controller possuem
[AuthorizeCustom] - [ ] Menu foi atualizado no
_Layout.cshtml
2. Build e Compilação
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
- Login como usuário sem permissão
- Menu deve aparecer mas acesso deve ser negado (403 Forbidden)
- Login como usuário com permissão
- 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:
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:
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.cssrc/Nelmetais.SGE.Business/Enums/PermissaoValueEnum.cssrc/Nelmetais.SGE.Business/Enums/PermissaoClaimEnum.cssrc/Nelmetais.SGE.WebApp/Extensions/AuthorizeCustomAttribute.cssrc/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
- Não criar migration: Alterações em enums e controllers não requerem migrations
- Claims no banco: Os claims são armazenados nas tabelas do ASP.NET Identity (
AspNetUserClaims,AspNetRoleClaims) - Cache: Após atribuir permissões, usuário pode precisar fazer logout/login para atualizar claims
- IDs únicos: Sempre use IDs únicos no
PermissaoTypeEnum
✅ Boas Práticas
- Siga o padrão: Use o mesmo padrão de nomenclatura dos outros módulos
- Seja específico: Display Names devem ser claros sobre o que a permissão controla
- Proteja tudo: Sempre proteja GET e POST da mesma operação
- Documente: Atualize este guia se identificar novos casos ou problemas
🎯 Dicas
- Use
TipoUsoMercadoriaController.cscomo referência - é um exemplo completo e bem implementado - Para permissões especiais (além de CRUD), verifique
PermissaoValueEnum.cspara 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