Sistema de Apoio ao Processo Legislativo
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

124 lines
4.7 KiB

import mimetypes
import os
import re
from urllib.parse import quote
from django.conf import settings
from django.http import FileResponse, Http404, HttpResponse
from sapl.materia.models import Proposicao, pode_ver_proposicao
from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,
DocumentoAdministrativo,
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
X_ACCEL_PREFIX = '/media-interna/'
# O nginx do container só envia para cá os caminhos de PROTEGIDO_RE e de
# MODELOS_DE_SESSAO; o resto de /media/ sai direto do disco. Ao incluir um
# modelo aqui, incluí-lo também em docker/config/nginx/sapl.conf.
# Arquivos que dependem de autorização, em private/ ou em qualquer outra
# pasta (public/ guarda os enviados antes da criação de private/).
PROTEGIDO_RE = re.compile(
r'^sapl/[^/]+/(?:documentoadministrativo|'
r'documentoacessorioadministrativo|proposicao)/')
RESOLVEDORES = {
'documentoadministrativo': (
DocumentoAdministrativo, 'texto_integral', pode_ver_documento_adm),
'documentoacessorioadministrativo': (
DocumentoAcessorioAdministrativo, 'arquivo',
lambda user, obj: pode_ver_documento_adm(user, obj.documento)),
'proposicao': (
Proposicao, 'texto_original', pode_ver_proposicao),
}
# 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 re.fullmatch(r'[0-9]+', partes[3]):
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):
"""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."""
resolvedor = RESOLVEDORES.get(path.split('/')[2])
if not resolvedor:
raise Http404
model, campo, pode_ver = resolvedor
obj = model.objects.filter(
**{campo + '__in': [path, './' + path]}).first()
if not obj:
raise Http404
arquivo = getattr(obj, campo)
if not arquivo or not pode_ver(request.user, obj):
raise Http404
return arquivo.name[2:] if arquivo.name.startswith('./') else arquivo.name
def _caminho_em_media(nome):
raiz = os.path.realpath(settings.MEDIA_ROOT)
caminho = os.path.realpath(os.path.join(raiz, nome))
if not caminho.startswith(raiz + os.sep):
raise Http404
return caminho
def media(request, path):
partes = path.split('/')
if any(p in ('', '.', '..') for p in partes) or '\x00' in path:
raise Http404
protegido = path.startswith('sapl/private/') or bool(
PROTEGIDO_RE.match(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)
content_type = (mimetypes.guess_type(nome_arquivo)[0] or
'application/octet-stream')
disposicao = 'inline'
if os.path.splitext(nome_arquivo)[1].lower() in EXTENSOES_ATIVAS or \
tipo_ativo(content_type):
content_type, disposicao = 'application/octet-stream', 'attachment'
caminho = _caminho_em_media(nome)
if settings.DEBUG:
try:
response = FileResponse(open(caminho, 'rb'),
content_type=content_type)
except (FileNotFoundError, IsADirectoryError):
raise Http404
else:
response = HttpResponse(content_type=content_type)
response['X-Accel-Redirect'] = X_ACCEL_PREFIX + quote(nome)
response['Content-Disposition'] = "%s; filename*=UTF-8''%s" % (
disposicao, quote(nome_arquivo))
response['X-Content-Type-Options'] = 'nosniff'
if protegido or (len(partes) > 2 and partes[2] in MODELOS_DE_SESSAO):
response['Cache-Control'] = 'private'
return response