Skip to main content

Módulo Authorization — visão geral

O módulo de autorização implementa o RBAC (controle de acesso por papéis) do Portal 2.0: o catálogo de permissões, os papéis (system roles canônicos e papéis customizados por tenant), a atribuição de papéis a usuários em um tenant ou em uma unidade, políticas de negação/permissão adicionais (ABAC), a consulta das próprias permissões efetivas e as rotas de inspeção/diagnóstico usadas por suporte e automação. Ele não autentica ninguém — quem chega até este módulo já passou pela autenticação (identity/authentication e identity/oauth, ver Authentication) e traz um access token válido. O módulo de autorização decide o que esse usuário pode fazer, e o AuthorizationGuard compartilhado (@mobilemed/shared-module/iam) é o ponto único onde essa decisão é aplicada em toda rota protegida do backend — não só nas rotas deste módulo.

Ele vive em um pacote do backend (NestJS): identity/authorization (core/http/persistence). O guard e os modelos de permissão (AuthorizationGuard, Authorize, IamClaim, PermissionMask, PermissionCatalog) vivem em shared/module/iam e são consumidos por este e por outros módulos do backend (exame, laudo, empresa etc.) através do decorator @Authorize(...).

Fonte de verdade. Este levantamento foi feito inteiramente a partir do código atual (branch integration/with-fixlaudo) e dos testes em __test__. Não existe rascunho anterior deste módulo. Os cartões do Bitrix (funil 562, F-001 a F-008 e F-S01) descrevem o sistema legado (mm-pacs-portal-api, Express, perfis por empresa em MongoDB UsuarioPerfil) e têm o campo "status vs. código real" vazio em todos os cartões consultados — foram usados só como contexto de intenção de produto (o modelo de "papel por empresa", a exigência de MFA para perfis privilegiados, a obrigação de trilha de auditoria), nunca como fonte do comportamento atual. O RBAC atual não é uma migração 1:1 do legado: trocou perfil numérico único por tenant/empresa por um bitmask de permissões por tenant e por unidade, e acrescentou um mecanismo de políticas (ABAC) que o legado não tinha.

Prefixo de rotas e versionamento​

Como em authentication e user, a API usa versionamento por URI (VersioningType.URI, versão padrão 1): toda rota deste módulo é servida sob /v1/... (ex.: /v1/authorization/permissions, /v1/tenants/:tenantId/roles). Em desenvolvimento local, o serviço sobe na porta 3000 (MAIN_API_PORT, default 3000) e expõe o Swagger em http://localhost:3000/docs.

Modelo de permissões​

  • Catálogo de permissões: um conjunto fixo de permissões nomeadas (PermissionName, ex.: exam:read, role:manage, policy:write), cada uma com um resource, uma action e uma posição de bit (bit_position) fixa e nunca reaproveitada — permissões descontinuadas ficam marcadas como retired no catálogo em vez de terem o bit reaproveitado. PermissionCatalog.all() inclui as retired; PermissionCatalog.assignable() as exclui — é essa segunda lista que pode ser atribuída a um papel ou usada em uma política. Ver Catálogo de permissões e papéis do sistema.
  • Papel (role): um conjunto de permissões, representado internamente como um bitmask decimal (permission_mask, inteiro grande serializado como string — soma de 2^bit_position de cada permissão do papel). Um papel é system (canônico, sem tenant, imutável via API) ou tenant_custom (criado por um tenant, mutável). Ver Papéis.
    • Nove papéis de sistema são semeados no boot da aplicação (BootstrapIdentityAuthorizationCatalogService → ApplyIdentitySystemRoleCatalogService), reconciliando o catálogo fixo em código com o banco: administrator, manager, doctor, technician, resident, read_only, typist, requestor, financial — cada um mapeado a um legacy_profile_id numérico (1 a 9) que corresponde ao perfil do sistema legado (PROPRIETARIO, GESTOR, MEDICO, TECNICO, RESIDENTE, SOMENTE_LEITURA, DIGITADOR, SOLICITANTE, FINANCEIRO, respectivamente, pelos cartões do Bitrix).
    • O papel manager é o único "reconciliado" de forma especial: ele recebe todo o catálogo atribuível, exceto report:sign — assinar um laudo é ato clínico, e um gestor administra a unidade sem necessariamente ser médico (comentário no código, decisão datada de 22/09/2026). O mesmo raciocínio remove report:sign do conjunto embutido de administrator.
  • Atribuição de papel a usuário: liga um userId a um roleId num tenant (papel tenant-wide) ou numa unidade específica daquele tenant — nunca as duas coisas na mesma atribuição. Tem grantedBy, grantedAt, e opcionalmente expiresAt. Ver Atribuição de papéis.
  • Permissão efetiva: a união (OR bit a bit) dos bitmasks de todo papel ativo e não expirado atribuído a um usuário num escopo. Um grant de tenant vale para todas as unidades daquele tenant; um grant de unidade vale só naquela unidade. Isso é resolvido pelo EffectivePermissionScopeResolver (shared/module/iam) a partir de uma claim cacheada por usuário (ver "Como o guard decide", abaixo).
  • Política (policy): uma regra adicional de allow/deny por tenant, amarrada a uma permissão, com prioridade numérica, condições opcionais (comparação de atributos por caminho a.b.c, com suporte a referenciar outro atributo via $a.b.c) e uma lista opcional de papéis- alvo (se vazia, aplica-se a qualquer papel). Políticas nunca ampliam o que o RBAC já concedeu — elas só podem negar uma permissão que o RBAC concederia, ou confirmar explicitamente uma permissão já concedida; não existe caminho de política que conceda uma permissão sem o bit correspondente no papel. Ver Políticas de autorização.

Como o guard decide (AuthorizationGuard)​

Toda rota protegida (deste módulo ou de qualquer outro) declara sua exigência com o decorator @Authorize({ permissions, match, scope }) e é protegida por IdentityOAuthAccessTokenGuard (que autentica o access token) seguido de AuthorizationGuard (que autoriza). O guard:

  1. Lê os metadados de @Authorize do handler (ou lança erro 403 AUTHORIZATION_METADATA_INVALID se a rota não declarou nenhum — falha fechada por construção).
  2. Carrega a claim de autorização do usuário (AuthorizationClaimReader.findForUser) — um snapshot com isPlatformAdmin, roleIds e os bitmasks por tenant/unidade, versionado por um contador (authorization_version) que é incrementado a cada mutação relevante (papel atribuído/revogado, permissões de um papel alteradas etc.). A leitura tenta primeiro um cache distribuído (Redis) e cai para o Postgres se ele estiver indisponível ou desatualizado — confirmado em teste (identity-own-permissions.e2e.spec.ts: "continues serving least-privilege permissions from PostgreSQL when the distributed claim read fails"). Sem claim → 403 AUTHORIZATION_CLAIM_UNAVAILABLE (não 404 — a rota não revela se o recurso existe).
  3. Resolve o escopo da exigência (tenant, unit, resource, current-user, global-catalog, tenant-list, unit-list, organization-list ou platform-admin) a partir de parâmetros da própria requisição (path/query/body) e verifica se o bitmask do usuário nesse escopo concede toda (match: 'all') ou alguma (match: 'any') das permissões exigidas. Sem concessão suficiente → 403 ForbiddenAction com requiredPermission no corpo do erro.
  4. Se a permissão foi concedida pelo RBAC e o usuário não é platformAdmin, avalia as políticas ativas daquele tenant que citam a mesma permissão contra o escopo resolvido. Uma política deny que combine (papel-alvo e condições) derruba uma concessão RBAC já validada → 403 AUTHORIZATION_POLICY_DENIED. platformAdmin pula essa etapa inteira.
  5. Se tudo passou, popula request.authorizationContext (usado pelos handlers para saber quais tenants/unidades o ator pode enxergar numa listagem) e deixa a requisição prosseguir.

Um usuário sem permissão nunca recebe 404 para esconder a existência de um recurso — o padrão observado neste módulo é sempre 403 (às vezes com errorCode específico), com uma exceção: platform-admin ou unit/tenant inexistentes/inativos também resultam em 403 AUTHORIZATION_SCOPE_UNAVAILABLE, não em 404. Isso é deliberado — ver insufficientPermission/scopeUnavailable em AuthorizationGuard.

platformAdmin (IamClaim.isPlatformAdmin) contorna toda checagem de bitmask e de política — é a única forma de bypass confirmada no código.

Relação com outros módulos de identidade​

  • identity/user: dono do cadastro do usuário. A autorização não cria nem edita usuários — ela só lê (IdentityUserRepository.findActiveUserById) para confirmar que o usuário de uma atribuição existe e está ativo, e usa o diretório de usuários (FindIdentityUserDirectoryService) para montar a lista de "membros de unidade" (ver Membros de unidade).
  • identity/session: a claim de autorização é resolvida por userId, não por sessão — a sessão e o nível de confiança da autenticação (assurance: PASSWORD/MFA) são geridos por identity/session e checados independentemente pelo módulo de autenticação. Este módulo não eleva nem consulta assurance.
  • shared/module/audit: toda mutação (criar/atualizar/retirar papel, políticas, atribuições) grava um evento no outbox de auditoria (IdentityOutboxEventEntity) com autor, IP, clientId, antes/depois — confirmado em cada *.service.ts de escrita deste módulo.
  • shared/module/iam (o guard compartilhado): os módulos de exame, laudo, empresa etc. usam o mesmo AuthorizationGuard e o mesmo @Authorize(...) — este módulo é dono do catálogo e das atribuições, não do enforcement em si, que é transversal a todo o backend.

Rate limiting​

Não foi identificado @Throttle/RateLimitGuard dedicado em nenhuma rota deste módulo neste levantamento — diferente de authentication, que aplica rate limit a rotas sensíveis a força bruta. As rotas de autorização são protegidas apenas pelo AuthorizationGuard (RBAC/ABAC).

Páginas deste módulo​

PáginaCobre
Catálogo de permissões e papéis do sistemaGET /authorization/permissions, /resources, /system-roles, /permission-bit-catalog
PapéisCRUD de papéis customizados por tenant, clonagem e gestão de permissões de um papel — tenants/:tenantId/roles/*
Atribuição de papéisConceder, listar, atualizar expiração e revogar papéis de um usuário num tenant ou unidade; permissões efetivas
Minhas permissõesGET /me/permissions, GET /authorization/my-permissions, GET /authorization/my-permissions/changed
Políticas de autorizaçãoCRUD de políticas allow/deny por tenant e avaliação pontual — tenants/:tenantId/policies/*
Inspeção de autorizaçãoPOST checks/bulk-checks, GET users/:userId/effective, GET consistency
Membros de unidadeGET units/:unitId/members — diretório de usuários com papel numa unidade
Exportação de permissõesGET units/:unitId/users/permissions-export — CSV de permissões efetivas

:::tip OpenAPI A documentação interativa (schemas + "Try it out") está disponível em /docs no ambiente onde a API está rodando (local: http://localhost:3000/docs). :::