feat: "itens sem permissão não aparecem" — filtro de menu por permission (secao 169)
getUserPermissionKeys(userId, tenantId?) (novo, packages/auth) devolve todas as permission keys do usuário no contexto atual, mesma query de userHasPermission só que retornando o conjunto inteiro. GET /auth/me agora inclui permissionKeys — leitura adicional só pra UX, nunca decisão de autorização (isso continua sendo o PermissionGuard em cada endpoint). NavLeaf/NavSection ganham campo opcional permission; todos os ~35 itens de menu (tenant e platform) anotados com a permission key mínima que o endpoint GET correspondente já exige. filterNavByPermissions() (novo, nav-types.ts) esconde o item, ou a seção inteira se nenhum filho sobrar. TenantSidebar/PlatformSidebar/*Topbar são client components que importam TENANT_NAV/PLATFORM_NAV direto (não recebem como prop do server) porque os ícones (LucideIcon, funções) não são serializáveis através da fronteira RSC — restrição já documentada desde a PHASE 23. O filtro roda dentro desses wrappers, client-side, recebendo só permissionKeys: string[] (serializável) como prop. Testado ponta a ponta com um supervisor de teste de verdade (convidado, senha trocada, logado via UI): menu perde Telefonia > Dialplan e a seção Administração inteira (nenhum permission bate). Confirmado que a autorização real continua valendo sem o item no menu: acesso direto a /app/administracao/usuarios continua batendo 403 no backend. Regressão: tenant_admin e platform_super_admin continuam vendo todo item do próprio menu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
35
TODO.md
35
TODO.md
@@ -1700,6 +1700,41 @@ secao 96-103, 124, 169) + achado real de autorização em `/ai/models`
|
||||
aplicado (0 chamadas — não existe tráfego real neste tenant ainda,
|
||||
resultado honesto)
|
||||
|
||||
## PHASE 48 — "Itens sem permissão não aparecem" (agente.md secao 169)
|
||||
- [x] `getUserPermissionKeys(userId, tenantId?)` (novo, `packages/auth`) —
|
||||
todas as permission keys do usuário no contexto atual, mesma query
|
||||
de `userHasPermission` só que devolvendo o conjunto inteiro em vez
|
||||
de checar uma. `GET /auth/me` agora devolve `permissionKeys` (só
|
||||
leitura adicional, não é decisão de autorização — isso continua
|
||||
sendo o `PermissionGuard` em cada endpoint, secao 31/146: esconder
|
||||
um item de menu nunca substitui o check no backend)
|
||||
- [x] `NavLeaf`/`NavSection` ganham campo opcional `permission`; cada um
|
||||
dos ~35 itens de menu (tenant e platform) anotado com a permission
|
||||
key mínima que o endpoint GET correspondente já exige (ex.: Discador
|
||||
usa `campaigns.view`, Administração usa `users.manage`,
|
||||
Infraestrutura usa `freeswitch.view`). `filterNavByPermissions()`
|
||||
(novo, `nav-types.ts`) esconde o item, ou a seção inteira se
|
||||
nenhum filho sobrar
|
||||
- [x] achado de arquitetura ao implementar (não um bug, uma restrição já
|
||||
documentada): `TenantSidebar`/`PlatformSidebar`/`*Topbar` são
|
||||
client components que importam `TENANT_NAV`/`PLATFORM_NAV`
|
||||
DIRETO (em vez de receber como prop do server layout) porque os
|
||||
ícones (`LucideIcon`, funções) não são serializáveis através da
|
||||
fronteira RSC — bug real já documentado desde a PHASE 23. Por isso
|
||||
o filtro roda dentro desses wrappers client-side, sobre o array já
|
||||
no bundle, recebendo só `permissionKeys: string[]` (serializável)
|
||||
como prop
|
||||
- [x] Testado ponta a ponta com um supervisor de teste de verdade
|
||||
(convidado, senha trocada, logado via UI): menu mostra só Discador/
|
||||
Call Center/Telefonia (sem Dialplan, que exige `freeswitch.view` —
|
||||
supervisor não tem)/Monitoramento/Gravações/IA/Relatórios — a seção
|
||||
Administração inteira some (nenhum dos 3 filhos exige menos que
|
||||
`users.manage`). Confirmado que a autorização de verdade continua
|
||||
valendo mesmo sem o item no menu: acesso direto a
|
||||
`/app/administracao/usuarios` continua batendo 403 no backend.
|
||||
Regressão: tenant_admin e platform_super_admin continuam vendo
|
||||
TODO item do próprio menu (sem perder nada)
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user