Centraliza a autorização por equipes e atualiza a administração para o Wagtail 7.1 - #1064
Conversation
Implementação: atualiza somente o Wagtail e as extensões administrativas diretamente necessárias.
Implementação: exibe a contagem somente quando o contexto fornece os totais necessários.
Implementação: centraliza os nomes dos grupos controlados pelo sistema em uma definição única.
Implementação: adiciona migração que cria os grupos e consolida o grupo empresarial legado.
Implementação: corrige valores nulos existentes e torna explícito o estado padrão dos vínculos editoriais.
Implementação: adiciona restrição de banco que impede término contratual anterior ao início.
Implementação: filtra contratos pela situação e pelo intervalo de vigência na data consultada.
Implementação: combina coleções, periódicos e empresas ativos sem prioridade que descarte vínculos simultâneos.
Implementação: move os identificadores de permissão para um módulo próprio do domínio de artigos.
Implementação: declara as ações de pacote e adiciona a migração das permissões do modelo.
Implementação: declara aplicações, modelos e ações permitidos aos administradores e membros de coleção.
Implementação: declara aplicações, modelos e ações permitidos aos administradores e membros de periódico.
Implementação: declara aplicações, modelos e ações permitidos aos administradores e membros de empresa.
Implementação: calcula níveis de acesso, ações de modelo e permissões Django a partir dos grupos ativos.
Implementação: converte o módulo de testes em pacote e preserva integralmente os casos existentes.
Implementação: cobre a estrutura completa e a precedência do maior acesso entre grupos simultâneos.
Implementação: registra sinais idempotentes que refletem papéis ativos nos grupos canônicos do usuário.
Implementação: reconcilia usuários substituídos e membros afetados pela exclusão de equipes ou coleções.
Implementação: bloqueia alterações manuais de nomes, vínculos, permissões e exclusão dos grupos canônicos.
Implementação: cobre mudanças de papel, inativação, substituição, exclusão e mutações manuais proibidas.
Implementação: cria comando idempotente baseado na matriz, incorpora o grupo legado e sincroniza vínculos existentes.
Implementação: cobre a matriz exata de permissões, idempotência e remoção de grupos sem vínculo ativo.
Implementação: adiciona alvos para migrar a base e sincronizar grupos, permissões e membresias.
Implementação: substitui a configuração padrão do Wagtail por viewsets administrativos protegidos.
Implementação: oculta grupos reservados e bloqueia atribuição, edição, renomeação e exclusão manual.
Implementação: filtra querysets por coleção, periódico ou empresa conforme os vínculos ativos do usuário.
Implementação: integra a matriz ao Wagtail e aplica escopo de queryset e objeto aos viewsets protegidos.
Implementação: relaciona itens administrativos às aplicações acessíveis pelos grupos ativos do usuário.
Implementação: restringe membresias, empresas e contratos aos vínculos que cada perfil pode administrar.
Implementação: filtra coleções, periódicos, empresas e usuários conforme o escopo do responsável.
| def scope_queryset(user, queryset): | ||
| if user and user.is_superuser: | ||
| return queryset | ||
| if not user or not user.is_authenticated: |
| return queryset.none() | ||
|
|
||
| group_names = set(user.groups.values_list("name", flat=True)) | ||
| if TeamGroups.COLLECTION_ADMIN in group_names and ( |
There was a problem hiding this comment.
Sim, COLLECTION_ADMIN.
Essa é a constante interna de TeamGroups, cujo valor é "COLLECTION_TEAM_ADMIN".
Conforme definido no issue #863, esse grupo pode realizar CRUD de Company. Como Company é um cadastro global e não possui vínculo direto com coleção, o administrador de uma coleção com membership ativa acessa o cadastro de empresas; usuários de empresa continuam limitados à própria empresa.
| ) | ||
|
|
||
|
|
||
| class ManagedRelationAdminForm(CoreAdminModelForm): |
There was a problem hiding this comment.
@pitangainnovare com o desuso de modeladmin ainda faz sentdio o uso de coreadminmodelform? Viu o core.views?
There was a problem hiding this comment.
Ajustado. Removi a dependência de CoreAdminModelForm desses componentes. Os formulários agora usam diretamente WagtailAdminModelForm, e os ViewSets de equipe reutilizam CommonControlFieldViewSet, seguindo o fluxo nativo de snippets.
| if ( | ||
| not user | ||
| or not getattr(user, "is_authenticated", False) | ||
| or ( | ||
| not user.is_superuser | ||
| and ( | ||
| not journal | ||
| or journal.id not in membership.get("journal_list_ids", []) | ||
| ) | ||
| ) | ||
| ): | ||
| journal_title = journal.title if journal else _("Unknown") |
There was a problem hiding this comment.
@pitangainnovare se este código for recorrente poderia ter um método em User para passar o journal e realizar as checagens
There was a problem hiding this comment.
Verifiquei e, atualmente, a checagem de acesso a um journal específico ocorre somente neste fluxo. Os demais usos trabalham com escopo de querysets ou apenas verificam a existência de algum journal autorizado. Preferi não adicionar o método em User, mantendo o modelo de core desacoplado das regras de equipe e contratos. Se a checagem específica se repetir, podemos centralizá-la em team.authorization.
| response.update( | ||
| pp.is_registered_xml_with_pre(xml_with_pre, xml_with_pre.filename) | ||
| ) | ||
| logging.info(f"is_registered_xml_with_pre: {response}") |
There was a problem hiding this comment.
@pitangainnovare pode eliminar estes vários logging info e idealmente convertê-lo para uma lista que adicionada a algum modelo para visualizar o passo-a-passo pela interface. Mas isso pode ser um novo issue
There was a problem hiding this comment.
OK. Vamos deixar isso para outro PR específico para essa parte de logs.
| ) | ||
|
|
||
| if not self.for_user or not self.for_user.is_superuser: | ||
| if self.for_user: |
There was a problem hiding this comment.
@pitangainnovare if not self.for_user a linha 77 nunca vai acontecer
There was a problem hiding this comment.
Concordo. Nas views administrativas do Wagtail, for_user é sempre preenchido com request.user, e não há instanciação desse formulário sem o usuário no projeto. Vou remover o ramo inalcançável e manter apenas a verificação de superusuário.
| if user and user.is_superuser: | ||
| return queryset | ||
| if not user or not user.is_authenticated: | ||
| return queryset.none() |
| if request.method != "POST": | ||
| return render( | ||
| request, | ||
| "modeladmin/upload/package/confirm_action.html", |
There was a problem hiding this comment.
@pitangainnovare tem que trocar o modeladmin porque vai falhar
There was a problem hiding this comment.
Ajustado. Removi o namespace legado modeladmin e movi o template para upload/admin/package/confirm_action.html. A herança de wagtailadmin/base.html foi mantida, pois continua válida: wagtailadmin é o namespace do painel administrativo, enquanto o legado removido é o ModelAdmin.
| if request.method != "POST": | ||
| return render( | ||
| request, | ||
| "modeladmin/upload/package/confirm_action.html", |
There was a problem hiding this comment.
Ajustado também. Esta referência agora utiliza upload/admin/package/confirm_action.html
|
|
||
| return render( | ||
| request, | ||
| "modeladmin/upload/package/republish_selected.html", |
There was a problem hiding this comment.
Ajustado. Removi o namespace legado modeladmin e movi o template para upload/admin/package/republish_selected.html
|
|
||
| params = {} | ||
| try: | ||
| params["pkg_zip__id"] = request.GET["pkg_zip_id"] |
There was a problem hiding this comment.
@pitangainnovare trocar params["pkg_zip__id"] = por params["pkg_zip_id"] =
| qs = super().get_queryset(request) | ||
| return qs.filter(package__in=get_scoped_package_queryset(request.user)) |
There was a problem hiding this comment.
@pitangainnovare parece repetitivo, poderia ser feito no BaseUploadViewSet?
There was a problem hiding this comment.
Sim.
O filtro se repete nos ViewSets de relatórios e resultados.
Vou centralizá-lo no BaseUploadViewSet, deixando configurável o caminho até o pacote, pois alguns modelos usam package e outros report__package.
| @staticmethod | ||
| def is_free(pid_v2): | ||
| logging.info(f"PidV2Generator.is_free?") | ||
| logging.info("PidV2Generator.is_free?") |
There was a problem hiding this comment.
@pitangainnovare idealmente trocar logging.info por uma lista e guardar em algum modelo para consulta do passo-a-passo
There was a problem hiding this comment.
OK. Vamos deixar isso para outro PR específico para essa parte de logs.
robertatakenaka
left a comment
There was a problem hiding this comment.
@pitangainnovare fazer correções e verificar comentários
a32c93c
into
scieloorg:rc-team-authorization
O que esse PR faz?
Centraliza as regras de autorização administrativa com base nas equipes de coleção, periódico e empresa, aplicando o princípio de menor privilégio às telas e operações do Wagtail. Além da validação manual indicada em uma das seções seguintes, pode ser interessante testar manualmente a partir do roteiro disponibilizado neste link.
Este PR:
Onde a revisão poderia começar?
A revisão pode começar pelos seguintes módulos:
team/authorization_matrix.pyteam/authorization.pyteam/policies.pycore/users/permission_policies.pyupload/querysets.pyeupload/permission_policies.pyupload/controller.pyteam/signals.pyeteam/management/commands/create_user_groups.pyAs migrações relacionadas estão em:
team/migrations/0005_create_team_groups.py;team/migrations/0006_normalize_active_members.py;team/migrations/0007_validate_contract_date_range.py;upload/migrations/0011_alter_package_options.py.Como este poderia ser testado manualmente?
Preparação
Administração de grupos e equipes
Escopo administrativo
Delegação da análise de qualidade
Autorização de upload
bn.mr.unexpected.mr.bne confirmar que o processamento segue normalmente.Validação automatizada realizada
Foram executados em Docker:
Também foram executados:
O Django não identificou novas migrações pendentes. O
manage.py checkapresentou apenas avisos já conhecidos do projeto, sem erros bloqueantes.Algum cenário de contexto que queira dar?
Antes desta alteração, parte da autorização era baseada em helpers específicos de cada aplicação, filtros de interface e verificações de grupo espalhadas pelo código.
Esse modelo permitia divergências entre:
Um caso identificado permitia que um usuário enviasse um pacote pertencente a outro periódico. Embora o pacote não aparecesse normalmente na listagem administrativa, o processamento aceitava o arquivo e podia publicá-lo em QA.
A nova estrutura centraliza a decisão de autorização, aplica o mesmo escopo às diferentes camadas e adota negação por padrão quando não existe usuário ou membresia válida.
Este PR tem como base a branch
rc-team-authorization, criada a partir derc. Depois da homologação, a integração comrcdeverá ocorrer em um PR separado.Screenshots
N/A
Quais são os tickets relevantes?
#393 #851 #863
Referências
Segurança da informação (NSI.04)
Este PR manipula dados sensíveis ou pessoais (LGPD)?
Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?
Este PR introduz, atualiza ou remove dependências de terceiros?
wagtailde 6.4.2 para 7.1.1,wagtail-django-recaptchade 2.1.1 para 2.2.0 ewagtail-modeladminde 2.0.0 para 2.2.0.Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?
Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?
Este PR expõe novos endpoints, telas ou serviços?
Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?