feat: scope Editor user administration by group (#990 slice 2) - #1047
Merged
Conversation
…ew of #990 slice 2) The scope filter excluded admin accounts with `role <> 'admin'`. That holds today only because `users.role` is plain TEXT with no CHECK and every writer goes through `Role::as_str`, which emits lowercase. A row stored as `Admin` — by hand, or by a future writer; #934 (OIDC/LDAP) adds one — would slip past the single boundary that keeps an Editor away from admin accounts, and only when that admin happened to share the Editor's group. Now `lower(role) <> 'admin'` in both dialect arms. There is no index on `role` to lose, and the users table is small (hugo runs ~120 accounts). The test seeds an admin row with `Admin` casing sharing the Editor's group and asserts it stays out of the list. I verified it FAILS with the old predicate and passes with the new one, so it pins the hole rather than merely describing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fatia 2 de 5 da #990. Implementada por agente
codex exec(gpt-5.6-sol, high); diff e gate revisados e reexecutados por mim, com uma correção minha de segurança (bb2fcfc).O que muda
A seção Usuários abre para Editor, escopada pelos grupos dele. Ele nunca vê conta Admin. Cria usuário restrito a Viewer/Editor e aos grupos dele, edita perfil e reseta senha de quem está no escopo. Seguem Admin-only: apagar conta, importar CSV e resetar MFA. Rota com username fora do escopo responde 404.
O agente acertou a armadilha da paginação
Foi o risco que eu tinha destacado no prompt:
list_page/count_filteredpaginam no SQL, então filtrar em Rust depois da página faria a contagem mentir e a página 2 vir vazia — bug que passa em teste unitário e aparece em produção com 120 contas, que é o volume do hugo.Ele empurrou o filtro para dentro do SQL, nos dois dialetos, e o teste é da forma certa: semeia 49 usuários visíveis e 60 estrangeiros mais novos, de modo que filtragem pós-página deixaria a página 1 vazia.
O casamento de grupo também está correto onde é fácil errar: usa
instr/strpossobre',' || groups || ','com o padrão,time-a,, entãotime-anão casa comtime-ab, um%ou_no nome do grupo não vira curinga, e a comparação segue case-sensitive como oSpec::access_allowsque já governa landing e proxy. Conferi que a coluna é de fato separada por vírgula (join_groups/parse_groups), e escopo vazio virou1=0— Editor sem grupo não vê ninguém.Minha correção: exclusão de Admin era sensível a caixa
role <> 'admin'só funciona porqueusers.roleéTEXTsem CHECK e todo escritor passa porRole::as_str, que emite minúsculas. Uma linha gravada comoAdmin— à mão, ou por um escritor futuro: o #934 (OIDC/LDAP) adiciona um — passaria pela única fronteira que mantém Editor longe de conta Admin. E só se manifestaria quando esse Admin compartilhasse grupo com o Editor, o que torna a falha rara e silenciosa.Agora é
lower(role) <> 'admin'nos dois braços. Não há índice emrolea perder e a tabela é pequena.O teste prende o buraco, não apenas o descreve: verifiquei que ele falha com o predicado antigo e passa com o novo.
Outras verificações da revisão
edit,groups,role,password,profile,delete,mfa/reset) e o teste cobre as 7 para alvo de outro time.auth.rs+28,scope.rs+13): só"users"entrando no nível Editor docan_access_section, com o teste existente estendido, e o helperuser_filterque mapeia irrestrito→Nonee escopado→Some(groups)— com a distinção documentada de queSome([])não casa nada, para um lookup falho nunca virar a consulta irrestrita. Nenhum predicado afrouxado.Gate reexecutado por mim:
cargo testlimpo,clippy --all-targets -- -D warningssem warning,i18n-checkOK.🤖 Generated with Claude Code