Editors publish post/tag/media without PII or campaign access; existing users backfill as admin.
5.8 KiB
Roles no admin Payload (users.roles)
Status: entregue 2026-07-27
Atualizado em: 2026-07-27
Item do roadmap: docs/roadmap.md (Admin Payload / Bloqueadores — campo roles em users)
Impeccable: A — N/A sem superfície UI própria (campo renderiza no admin Payload)
Appetite: ~0,75–1 dia eng; migration + predicados + testes; 15–30 min docs
Responsável: —
Revisão 2026-07-27: auditoria do implement-roadmap-item. Unificação users+campaignUser rejeitada (25 FKs, isolamento estrutural → dado, janela de congelamento). Escopo mínimo: admin | editor; isPayloadAdmin exige papel admin; editor escreve post/tag/media. Entrega: migration 20260727_032523_add_users_roles + backfill; predicados hasPayloadPanelAccess/canManagePublishedContent/isPayloadEditor; canReadMunicipality nega ator users sem admin; testes unit+int (ator editor + pin payload.auth com roles); unificação documentada como rejeitada com gatilho Fase 2 white-label.
Dados → decisão → apresentação
Dados: N/A — RBAC de accounts do /admin; sem KPI/mapa/série/ranking nesta entrega.
Contexto
A collection users (src/collections/Users.ts) não tem roles: todo account admin é omnipotente. O hardening Fase 0 (2026-07-23) já trancou collections CMS/PII com payloadAdminOnly / isPayloadAdmin (type guard em user?.collection === 'users'), mas isso não diferencia papéis dentro do realm admin. O gap está em AGENTS Known Gap #1, roadmap "Bloqueadores atuais" e TECH-DEBT P1.
/campanha permanece em campaignUser (cookie campaign-token, roles coordinator|advisor|candidate|leader). As duas collections ficam separadas de propósito nesta entrega.
Objetivos
- Campo
roles(selecthasMany,admin|editor) emusers,required,defaultValue: ['admin'],saveToJWT: true, field access admin-only. - Migration com backfill
adminem todas as rows existentes +down()simétrico. isPayloadAdminexigerolescontendoadmin(falha fechado se ausente/vazio).- Editor entra em
/admine cria/edita/apagapost/tag/media; negado em PII, petições, campanha e gestão deusers(exceto a própria conta). - Guardrails:
push: false; Local API comuser→overrideAccess: falsenos testes; sem Consent novo; sem unificar collections.
Decisões travadas
- Vocabulário
admin+editor. Cobre a razão real de abrir/admin(comunicação publicando notícias) com um predicado novo. Rejeitado: sóadmin(campo inútil além de revogar); matriz finacontent/legal/campaign-admin(ninguém pede hoje). defaultValue: ['admin']. Bootstrap seguro: o "create first user" do Payload não expõeroles(field access admin-only); defaulteditordeixaria o primeiro usuário sem poder promover ninguém. Mantém fixtures de teste intactas. Rejeitado: defaulteditor; required sem default.petitionfora do escopo do editor na v1. Abaixo-assinados carregam assinaturas (PII) e o export CSV é admin-only. Rejeitado: editor empetitionnesta fatia.saveToJWT: true. Consistência comcampaignUser.role; revogação imediata porquepayload.authdevolve o documento fresco — pin de teste anti-lockout.- Não unificar
users+campaignUser. Decidido 2026-07-27. Números: 25 FKs →campaign_userem 22 tabelas (incl._vote_pledge_v,municipality_rels,activity_rels, internals Payload com ambas as colunas); isolamento hoje é estrutural (collection === 'users'); unificado vira checagem de dado sobre ~400leaderaccounts do outro lado de PII;loginWithUsername+ template de reset de campanha colidiriam com/admin. Janela: congelamento ~20/09. Gatilho para reabrir: Fase 2 white-label/multi-tenant, ou "uma pessoa precisa de dois logins" deixar de ser hipotético. - i18n e naming: identificadores em inglês (
roles,admin,editor,isPayloadEditor,canManagePublishedContent,hasPayloadPanelAccess); labels admin em pt-BR ("Papéis", "Administrador", "Editor").
Questões em aberto
Nenhuma bloqueante. Defaults acima carregados como decisão.
Abordagem proposta
flowchart LR
Users["users.roles<br/>admin | editor"]
Pred["isPayloadAdmin<br/>exige admin"]
Panel["hasPayloadPanelAccess<br/>admin ou editor"]
CMS["canManagePublishedContent<br/>post/tag/media"]
AdminUI["/admin"]
Users --> Pred
Users --> Panel
Panel --> AdminUI
Pred --> CMS
Panel --> CMS
- Schema + migration
add_users_roles(enum + tabelausers_roles+ backfill). - Apertar predicados em
shared.ts; Users.access; Post/Tag/Media; corrigircliReaderdo script de aggregates. - Testes: lockdown com ator editor; pin
payload.authdevolveroles; fixtures + unit dos predicados. - Gate AGENTS.md + Aikido + docs.
Não escopo
- Unificar
users+campaignUser. - UI de matriz de permissões por collection.
- RBAC a nível de campo dentro do
/admin. - Log de auditoria de mudança de papel.
- Papéis por tenant / white-label.
- Editor em
petition/signature/consent/ collections de campanha.
Dependências
Nenhuma dura. Soft: Onda 0 jurídica é independente (Consent keys). Soft: abrir /admin a comunicação só depois deste ship + smoke.
Verificação
pnpm migrate:status+ backfill localroles: ['admin'].- Conta
editor: entra/admin, publica post; negada em signature/users/municipality. - Conta
admin: comportamento pré-existente intacto. pnpm exec tsc --noEmit,pnpm lint,pnpm format:check,pnpm exec knip,pnpm check:cycles,pnpm test,pnpm test:e2e,pnpm buildcontra banco local.- Scan Aikido nos arquivos first-party editados.
Rabbit holes
Unificação de identidade; matriz fina de papéis; editor em petition; auditoria de role change.