aemo_fr/archive/views.py

154 lines
6.2 KiB
Python
Raw Permalink Normal View History

2024-06-03 16:49:01 +02:00
import base64
from io import BytesIO
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.fernet import Fernet, InvalidToken
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import PermissionDenied
from django.core.files.base import ContentFile
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, reverse
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.text import slugify
from django.views.generic import FormView, TemplateView, View
from aemo.models import Famille
from aemo.utils import is_ajax
from .models import Archive
from .pdf import ArchivePdf
from .forms import ArchiveFilterForm, ArchiveKeyUploadForm
class ArchiveCreateView(View):
def post(self, request, *args, **kwargs):
unite = 'aemo'
temp = BytesIO()
famille = get_object_or_404(Famille, pk=kwargs['pk'], archived_at__isnull=True)
intervenants_list = '/'.join([f"{interv.nom} {interv.prenom[0].upper()}."
for interv in famille.suivi.intervenants.all()])
ope_list = famille.suivi.ope_referents
motif_fin = famille.suivi.get_motif_fin_suivi_display()
pdf = ArchivePdf(temp, famille)
if not famille.can_be_archived(self.request.user):
raise PermissionDenied("Vous n'avez pas les droits nécessaires pour accéder à cette page.")
famille.archived_at = timezone.now()
pdf.produce()
filename = f"{unite}/{pdf.get_filename()}"
temp.seek(0)
pdf = temp.read()
# Create a symmetric Fernet key to encrypt the PDF, and encrypt that key with asymmetric RSA key.
key = Fernet.generate_key()
fernet = Fernet(key)
pdf_crypted = fernet.encrypt(pdf)
with open(settings.CRNE_RSA_PUBLIC_KEY, "rb") as key_file:
public_key = serialization.load_ssh_public_key(key_file.read())
padd = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)
fernet_crypted = public_key.encrypt(key, padding=padd)
nom_famille = famille.nom
arch = Archive.objects.create(
nom=nom_famille,
unite=unite,
intervenant=intervenants_list,
ope=", ".join([ope.nom_prenom for ope in ope_list]),
motif_fin=motif_fin,
date_debut=famille.suivi.date_debut_suivi,
date_fin=famille.suivi.date_fin_suivi,
key=base64.b64encode(fernet_crypted).decode(),
pdf=None
)
arch.pdf.save(filename, ContentFile(pdf_crypted))
famille.archived_at = timezone.now()
famille.save()
famille.anonymiser()
if is_ajax(request):
return JsonResponse({'id': famille.pk}, safe=True)
messages.success(request, f"La famille «{nom_famille}» a bien été archivée.")
return HttpResponseRedirect(reverse("suivis-termines"))
class ArchiveListView(TemplateView):
template_name = 'archive/list.html'
model = Archive
def dispatch(self, request, *args, **kwargs):
self.unite = self.kwargs['unite']
self.filter_form = ArchiveFilterForm(data=request.GET or None)
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
if is_ajax(request) and self.unite == 'aemo':
if self.filter_form.is_bound and self.filter_form.is_valid():
archives = self.filter_form.filter(Archive.objects.filter(unite=self.unite))
else:
archives = Archive.objects.none()
response = render_to_string(
template_name='archive/list_partial.html',
context={
'archives': archives,
'can_download': request.user.has_perm('aemo.can_archive'),
}
)
return JsonResponse(response, safe=False)
return super().get(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
return {
**super().get_context_data(*args, **kwargs),
'unite': self.unite,
'form': self.filter_form,
'archives': Archive.objects.filter(unite=self.unite),
'can_download': self.request.user.has_perm('aemo.can_archive'),
}
class ArchiveDecryptView(PermissionRequiredMixin, FormView):
form_class = ArchiveKeyUploadForm
template_name = 'archive/key_upload.html'
permission_required = 'aemo.can_archive'
def form_valid(self, form):
arch = get_object_or_404(Archive, pk=self.kwargs['pk'])
try:
with open(arch.pdf.path, "rb") as fh:
pdf_crypted = fh.read()
except OSError as err:
messages.error(self.request, f"Erreur lors de la lecture du document ({str(err)})")
return HttpResponseRedirect(reverse('archive-list', args=[arch.unite]))
try:
private_key_file = self.request.FILES['file'].read()
private_key = serialization.load_pem_private_key(private_key_file, password=None)
padd = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)
sim_key = private_key.decrypt(base64.b64decode(arch.key), padding=padd)
fernet = Fernet(sim_key)
pdf_content = fernet.decrypt(pdf_crypted)
except ValueError as err:
messages.error(self.request, f"Erreur lors de la lecture de la clé ({str(err)})")
return HttpResponseRedirect(reverse('archive-list', args=[arch.unite]))
except InvalidToken:
messages.error(self.request, "Erreur lors du déchiffrement")
return HttpResponseRedirect(reverse('archive-list', args=[arch.unite]))
filename = f"{slugify(arch.nom)}.pdf"
response = HttpResponse(pdf_content, content_type='application/pdf')
response['Content-Disposition'] = "attachment; filename=%s" % filename
return response