Browse Source

Aplica regras da página de detalhe e de sessões ocultas aos arquivos

No modo restritivo, os arquivos de documentos administrativos exigem a
mesma permissão da página de detalhe. Arquivos de sessões ocultas e de
seus oradores e justificativas não são servidos ao público.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pull/3865/head
Edward Ribeiro 3 weeks ago
parent
commit
605bcaf081
  1. 28
      sapl/base/media.py
  2. 8
      sapl/base/tests/test_media.py
  3. 8
      sapl/protocoloadm/models.py
  4. 4
      sapl/protocoloadm/tests/test_docadm_acesso.py
  5. 21
      sapl/sessao/tests/test_sessao_oculta_api.py

28
sapl/base/media.py

@ -10,6 +10,9 @@ from sapl.materia.models import Proposicao, pode_ver_proposicao
from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo, from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, DocumentoAdministrativo,
pode_ver_documento_adm) pode_ver_documento_adm)
from sapl.sessao.models import (JustificativaAusencia, Orador,
OradorExpediente, OradorOrdemDia,
SessaoPlenaria, restringe_sessoes_visiveis)
from sapl.utils import EXTENSOES_ATIVAS, tipo_ativo from sapl.utils import EXTENSOES_ATIVAS, tipo_ativo
X_ACCEL_PREFIX = '/media-interna/' X_ACCEL_PREFIX = '/media-interna/'
@ -31,6 +34,28 @@ RESOLVEDORES = {
} }
# Arquivos gravados em sapl/public/<model>/<pk>/..., que seguem a
# visibilidade da sessão plenária a que pertencem.
MODELOS_DE_SESSAO = {m._meta.model_name: m for m in (
SessaoPlenaria, Orador, OradorExpediente, OradorOrdemDia,
JustificativaAusencia)}
def _de_sessao_oculta(request, partes):
model = MODELOS_DE_SESSAO.get(partes[2])
if request.user.is_authenticated or not model or len(partes) < 5 or \
partes[1] != 'public' or not partes[3].isdigit():
return False
if model is SessaoPlenaria:
sessoes = SessaoPlenaria.objects.filter(pk=partes[3])
else:
sessoes = SessaoPlenaria.objects.filter(
pk__in=model.objects.filter(pk=partes[3]).values(
'sessao_plenaria_id'))
return sessoes.exists() and not restringe_sessoes_visiveis(
sessoes, request.user).exists()
def _nome_protegido(request, path): def _nome_protegido(request, path):
"""Devolve o nome do arquivo gravado no objeto dono, se o usuário puder """Devolve o nome do arquivo gravado no objeto dono, se o usuário puder
vê-lo. O caminho servido vem do banco, nunca da URL.""" vê-lo. O caminho servido vem do banco, nunca da URL."""
@ -56,6 +81,9 @@ def media(request, path):
protegido = path.startswith('sapl/private/') or bool( protegido = path.startswith('sapl/private/') or bool(
PROTEGIDO_RE.match(path)) PROTEGIDO_RE.match(path))
nome = _nome_protegido(request, path) if protegido else path nome = _nome_protegido(request, path) if protegido else path
if len(partes) > 2 and _de_sessao_oculta(request, partes):
raise Http404
nome_arquivo = os.path.basename(nome) nome_arquivo = os.path.basename(nome)
content_type = (mimetypes.guess_type(nome_arquivo)[0] or content_type = (mimetypes.guess_type(nome_arquivo)[0] or

8
sapl/base/tests/test_media.py

@ -71,9 +71,10 @@ def _assert_servido(response, path):
('O', True, None, False), ('O', True, None, False),
('O', True, 'logado', True), ('O', True, 'logado', True),
('R', False, None, False), ('R', False, None, False),
('R', False, 'logado', True), ('R', False, 'logado', False),
('R', True, None, False), ('R', True, None, False),
('R', True, 'logado', True), ('R', True, 'logado', False),
('R', True, 'com_permissao', True),
]) ])
def test_documento_administrativo(client, comportamento, restrito, perfil, def test_documento_administrativo(client, comportamento, restrito, perfil,
pode_ver): pode_ver):
@ -82,6 +83,9 @@ def test_documento_administrativo(client, comportamento, restrito, perfil,
texto_integral=DOC_PATH) texto_integral=DOC_PATH)
if perfil == 'logado': if perfil == 'logado':
_login(client, 'logado') _login(client, 'logado')
elif perfil == 'com_permissao':
_login(client, 'perm',
'protocoloadm.detail_documentoadministrativo')
response = _get(client, DOC_PATH) response = _get(client, DOC_PATH)

8
sapl/protocoloadm/models.py

@ -343,10 +343,10 @@ class DocumentoAdministrativo(models.Model):
def pode_ver_documento_adm(user, documento): def pode_ver_documento_adm(user, documento):
if user.is_authenticated: """Mesmas regras da página de detalhe do documento (CRUD)."""
return True if SaplAppConfig.attr('documentos_administrativos') == DOC_ADM_OSTENSIVO:
return not documento.restrito and SaplAppConfig.attr( return not (documento.restrito and user.is_anonymous)
'documentos_administrativos') == DOC_ADM_OSTENSIVO return user.has_perm('protocoloadm.detail_documentoadministrativo')
class DocumentoAcessorioAdministrativo(models.Model): class DocumentoAcessorioAdministrativo(models.Model):

4
sapl/protocoloadm/tests/test_docadm_acesso.py

@ -62,10 +62,10 @@ ACESSO = [
('O', True, 'sem_permissao', True), ('O', True, 'sem_permissao', True),
('O', True, 'com_permissao', True), ('O', True, 'com_permissao', True),
('R', False, 'anonimo', False), ('R', False, 'anonimo', False),
('R', False, 'sem_permissao', True), ('R', False, 'sem_permissao', False),
('R', False, 'com_permissao', True), ('R', False, 'com_permissao', True),
('R', True, 'anonimo', False), ('R', True, 'anonimo', False),
('R', True, 'sem_permissao', True), ('R', True, 'sem_permissao', False),
('R', True, 'com_permissao', True), ('R', True, 'com_permissao', True),
] ]

21
sapl/sessao/tests/test_sessao_oculta_api.py

@ -70,9 +70,20 @@ def test_api_mostra_sessao_previa_ao_usuario_autenticado(client, sessoes):
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
def test_media_de_sessao_e_publica(client, settings, sessoes): def test_media_de_sessao_oculta_do_anonimo(client, settings, sessoes):
settings.DEBUG = False settings.DEBUG = False
for sessao in sessoes: previa, iniciada = sessoes
response = client.get( orador_previa = Orador.objects.get(sessao_plenaria=previa)
'/media/sapl/public/sessaoplenaria/%s/pauta/p.pdf' % sessao.pk) orador_iniciada = Orador.objects.get(sessao_plenaria=iniciada)
assert response.status_code == 200
def get(path):
return client.get('/media/sapl/public/' + path).status_code
assert get('sessaoplenaria/%s/pauta/p.pdf' % previa.pk) == 404
assert get('sessaoplenaria/%s/pauta/p.pdf' % iniciada.pk) == 200
assert get('orador/%s/anexo/a.pdf' % orador_previa.pk) == 404
assert get('orador/%s/anexo/a.pdf' % orador_iniciada.pk) == 200
client.force_login(baker.make(get_user_model()))
assert get('sessaoplenaria/%s/pauta/p.pdf' % previa.pk) == 200
assert get('orador/%s/anexo/a.pdf' % orador_previa.pk) == 200

Loading…
Cancel
Save