IdHub/idhub/user/views.py

510 lines
16 KiB
Python
Raw Normal View History

2024-01-10 12:53:43 +00:00
import json
2024-01-08 19:11:41 +00:00
import base64
import qrcode
2024-01-10 12:53:43 +00:00
import datetime
2024-01-08 19:11:41 +00:00
import weasyprint
import qrcode.image.svg
from io import BytesIO
from pathlib import Path
from pyhanko.sign import fields, signers
from pyhanko import stamp
from pyhanko.pdf_utils import text
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
2023-10-10 08:54:13 +00:00
from django.utils.translation import gettext_lazy as _
from django.views.generic import View
2023-11-02 13:17:07 +00:00
from django.views.generic.edit import (
UpdateView,
CreateView,
DeleteView,
FormView
)
2023-10-11 07:52:05 +00:00
from django.views.generic.base import TemplateView
2023-10-27 09:19:10 +00:00
from django.shortcuts import get_object_or_404, redirect
2023-10-10 08:54:13 +00:00
from django.urls import reverse_lazy
2023-10-30 12:53:19 +00:00
from django.http import HttpResponse
2023-10-10 08:54:13 +00:00
from django.contrib import messages
from django_tables2 import SingleTableView
from idhub.user.tables import (
DashboardTable,
PersonalInfoTable,
RolesTable,
DIDTable,
CredentialsTable
)
2023-11-28 08:39:02 +00:00
from idhub.user.forms import (
RequestCredentialForm,
DemandAuthorizationForm,
TermsConditionsForm
2023-11-28 08:39:02 +00:00
)
2024-01-10 12:53:43 +00:00
from utils import certs
2023-10-10 08:54:13 +00:00
from idhub.mixins import UserView
from idhub.models import DID, VerificableCredential, Event, Membership
from idhub_auth.models import User
2023-10-10 08:54:13 +00:00
class MyProfile(UserView):
title = _("My profile")
section = "MyProfile"
2023-10-27 09:19:10 +00:00
class MyWallet(UserView):
2023-11-13 09:15:52 +00:00
title = _("My wallet")
2023-10-10 08:54:13 +00:00
section = "MyWallet"
class DashboardView(UserView, SingleTableView):
2023-10-17 15:42:48 +00:00
template_name = "idhub/user/dashboard.html"
table_class = DashboardTable
2023-10-10 08:54:13 +00:00
title = _('Dashboard')
2023-11-21 11:38:12 +00:00
subtitle = _('Events')
2023-10-10 08:54:13 +00:00
icon = 'bi bi-bell'
section = "Home"
def get_queryset(self, **kwargs):
queryset = Event.objects.select_related('user').filter(
user=self.request.user)
return queryset
2023-10-10 08:54:13 +00:00
class ProfileView(MyProfile, UpdateView, SingleTableView):
2023-10-17 15:42:48 +00:00
template_name = "idhub/user/profile.html"
table_class = PersonalInfoTable
2023-11-13 09:15:52 +00:00
subtitle = _('My personal data')
2023-11-22 14:05:05 +00:00
icon = 'bi bi-person-gear'
2023-10-11 07:52:05 +00:00
fields = ('first_name', 'last_name', 'email')
success_url = reverse_lazy('idhub:user_profile')
model = User
def get_queryset(self, **kwargs):
queryset = Membership.objects.select_related('user').filter(
user=self.request.user)
return queryset
2023-10-10 08:54:13 +00:00
2023-10-11 07:52:05 +00:00
def get_object(self):
return self.request.user
2023-10-10 08:54:13 +00:00
2023-11-09 17:09:10 +00:00
def get_form(self):
form = super().get_form()
form.fields['first_name'].disabled = True
form.fields['last_name'].disabled = True
form.fields['email'].disabled = True
return form
def form_valid(self, form):
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'lang': self.request.LANGUAGE_CODE,
})
return context
2023-11-09 17:09:10 +00:00
2023-10-11 07:52:05 +00:00
class RolesView(MyProfile, SingleTableView):
2023-10-17 15:42:48 +00:00
template_name = "idhub/user/roles.html"
table_class = RolesTable
2023-10-10 08:54:13 +00:00
subtitle = _('My roles')
icon = 'fa-brands fa-critical-role'
def get_queryset(self, **kwargs):
queryset = self.request.user.roles.all()
return queryset
2023-10-10 08:54:13 +00:00
2023-11-03 15:42:45 +00:00
class GDPRView(MyProfile, TemplateView):
2023-10-17 15:42:48 +00:00
template_name = "idhub/user/gdpr.html"
2024-02-05 11:25:02 +00:00
subtitle = _('Data protection')
2023-10-10 08:54:13 +00:00
icon = 'bi bi-file-earmark-medical'
class CredentialsView(MyWallet, SingleTableView):
2023-10-17 15:42:48 +00:00
template_name = "idhub/user/credentials.html"
table_class = CredentialsTable
2023-11-13 09:15:52 +00:00
subtitle = _('Credential management')
2023-10-10 08:54:13 +00:00
icon = 'bi bi-patch-check-fill'
def get_queryset(self):
queryset = VerificableCredential.objects.filter(
user=self.request.user)
return queryset
class TermsAndConditionsView(UserView, FormView):
template_name = "idhub/user/terms_conditions.html"
2024-02-05 11:25:02 +00:00
title = _("Data Protection")
section = ""
subtitle = _('Terms and Conditions')
icon = 'bi bi-file-earmark-medical'
form_class = TermsConditionsForm
success_url = reverse_lazy('idhub:user_dashboard')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
if self.request.user.accept_gdpr:
kwargs['initial'] = {
"accept_privacy": True,
"accept_legal": True,
"accept_cookies": True
}
return kwargs
def form_valid(self, form):
form.save()
return super().form_valid(form)
2023-10-30 12:53:19 +00:00
class WaitingView(UserView, TemplateView):
template_name = "idhub/user/waiting.html"
title = _("Comunication with admin")
subtitle = _('Service temporary close')
section = ""
icon = 'bi bi-file-earmark-medical'
success_url = reverse_lazy('idhub:user_dashboard')
def get(self, request, *args, **kwargs):
2024-02-21 09:46:33 +00:00
if self.admin_validated:
return redirect(self.success_url)
return super().get(request, *args, **kwargs)
2023-11-03 15:42:45 +00:00
class CredentialView(MyWallet, TemplateView):
2023-10-30 12:53:19 +00:00
template_name = "idhub/user/credential.html"
subtitle = _('Credential')
icon = 'bi bi-patch-check-fill'
def get(self, request, *args, **kwargs):
self.pk = kwargs['pk']
self.object = get_object_or_404(
VerificableCredential,
pk=self.pk,
user=self.request.user
)
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'object': self.object,
})
return context
class CredentialPdfView(MyWallet, TemplateView):
2024-01-08 19:11:41 +00:00
template_name = "certificates/4_Model_Certificat.html"
subtitle = _('Credential management')
icon = 'bi bi-patch-check-fill'
file_name = "certificate.pdf"
def get(self, request, *args, **kwargs):
if not self.admin_validated:
return redirect(reverse_lazy('idhub:user_dashboard'))
2024-01-10 12:53:43 +00:00
pk = kwargs['pk']
self.user = self.request.user
self.object = get_object_or_404(
VerificableCredential,
pk=pk,
2024-01-18 14:09:19 +00:00
eidas1_did__isnull=False,
2024-01-10 12:53:43 +00:00
user=self.request.user
)
2024-01-12 16:30:27 +00:00
self.url_id = "{}://{}/public/credentials/{}".format(
self.request.scheme,
self.request.get_host(),
self.object.hash
)
2024-01-08 19:11:41 +00:00
data = self.build_certificate()
2024-01-18 14:09:19 +00:00
if self.object.eidas1_did:
doc = self.insert_signature(data)
else:
doc = data
2024-01-08 19:11:41 +00:00
response = HttpResponse(doc, content_type="application/pdf")
response['Content-Disposition'] = 'attachment; filename={}'.format(self.file_name)
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
this_folder = str(Path.cwd())
path_img_sig = "idhub/static/images/4_Model_Certificat_html_58d7f7eeb828cf29.jpg"
img_signature = next(Path.cwd().glob(path_img_sig))
with open(img_signature, 'rb') as _f:
img_sig = base64.b64encode(_f.read()).decode('utf-8')
path_img_head = "idhub/static/images/4_Model_Certificat_html_7a0214c6fc8f2309.jpg"
img_header= next(Path.cwd().glob(path_img_head))
with open(img_header, 'rb') as _f:
img_head = base64.b64encode(_f.read()).decode('utf-8')
qr = self.generate_qr_code(self.url_id)
2024-01-10 12:53:43 +00:00
first_name = self.user.first_name and self.user.first_name.upper() or ""
last_name = self.user.first_name and self.user.last_name.upper() or ""
document_id = "0000000-L"
course = "COURSE 1"
address = "ADDRESS"
date_course = datetime.datetime.now()
n_hours = 40
n_lections = 5
issue_date = datetime.datetime.now()
2024-01-08 19:11:41 +00:00
context.update({
2024-01-10 12:53:43 +00:00
'object': self.object,
2024-01-08 19:11:41 +00:00
"image_signature": img_sig,
"image_header": img_head,
2024-01-10 12:53:43 +00:00
"first_name": first_name,
"last_name": last_name,
"document_id": document_id,
"course": course,
"address": address,
"date_course": date_course,
"n_hours": n_hours,
"n_lections": n_lections,
"issue_date": issue_date,
2024-01-08 19:11:41 +00:00
"qr": qr
})
return context
def build_certificate(self):
doc = self.render_to_response(context=self.get_context_data())
doc.render()
pdf = weasyprint.HTML(string=doc.content)
return pdf.write_pdf()
def generate_qr_code(self, data):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img_buffer = BytesIO()
img = qr.make_image(fill_color="black", back_color="white")
img.save(img_buffer, format="PNG")
return base64.b64encode(img_buffer.getvalue()).decode('utf-8')
2024-01-10 12:53:43 +00:00
def get_pfx_data(self):
2024-01-18 14:09:19 +00:00
did = self.object.eidas1_did
if not did:
2024-01-10 12:53:43 +00:00
return None, None
key_material = json.loads(did.get_key_material())
2024-01-10 12:53:43 +00:00
cert = key_material.get("cert")
passphrase = key_material.get("passphrase")
if cert and passphrase:
return base64.b64decode(cert), passphrase.encode('utf-8')
return None, None
2024-01-08 19:11:41 +00:00
def signer_init(self):
2024-01-10 12:53:43 +00:00
pfx_data, passphrase = self.get_pfx_data()
if not pfx_data or not passphrase:
return
2024-01-10 12:53:43 +00:00
s = certs.load_cert(
pfx_data, passphrase
2024-01-08 19:11:41 +00:00
)
return s
def insert_signature(self, doc):
sig = self.signer_init()
2024-01-10 12:53:43 +00:00
if not sig:
return
2024-01-08 19:11:41 +00:00
_buffer = BytesIO()
_buffer.write(doc)
w = IncrementalPdfFileWriter(_buffer)
fields.append_signature_field(
w, sig_field_spec=fields.SigFieldSpec(
'Signature', box=(150, 100, 450, 150)
)
)
meta = signers.PdfSignatureMetadata(field_name='Signature')
pdf_signer = signers.PdfSigner(
meta, signer=sig, stamp_style=stamp.TextStampStyle(
2024-01-08 19:11:41 +00:00
stamp_text='Signed by: %(signer)s\nTime: %(ts)s\nURL: %(url)s',
text_box_style=text.TextBoxStyle()
)
)
_bf_out = BytesIO()
pdf_signer.sign_pdf(w, output=_bf_out, appearance_text_params={'url': self.url_id})
2024-01-08 19:11:41 +00:00
return _bf_out.read()
2023-11-03 15:42:45 +00:00
class CredentialJsonView(MyWallet, TemplateView):
2023-10-30 12:53:19 +00:00
def get(self, request, *args, **kwargs):
pk = kwargs['pk']
self.object = get_object_or_404(
VerificableCredential,
pk=pk,
user=self.request.user
)
data = self.object.get_data()
2024-01-06 18:18:59 +00:00
response = HttpResponse(data, content_type="application/json")
2023-10-30 12:53:19 +00:00
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
return response
2023-10-10 08:54:13 +00:00
class PublicCredentialJsonView(View):
def get(self, request, *args, **kwargs):
pk = kwargs['pk']
self.object = get_object_or_404(
VerificableCredential,
hash=pk,
2024-01-18 14:09:19 +00:00
eidas1_did__isnull=False,
)
2024-02-21 09:16:32 +00:00
response = HttpResponse(self.object.get_data(), content_type="application/json")
2023-10-30 12:53:19 +00:00
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
return response
2023-10-10 08:54:13 +00:00
2023-11-03 15:42:45 +00:00
class CredentialsRequestView(MyWallet, FormView):
2023-10-30 17:29:21 +00:00
template_name = "idhub/user/credentials_request.html"
2023-11-13 09:15:52 +00:00
subtitle = _('Credential request')
2023-10-10 08:54:13 +00:00
icon = 'bi bi-patch-check-fill'
2023-11-02 13:17:07 +00:00
form_class = RequestCredentialForm
success_url = reverse_lazy('idhub:user_credentials')
2023-10-10 08:54:13 +00:00
def get(self, request, *args, **kwargs):
response = super().get(request, *args, **kwargs)
if not self.admin_validated:
return redirect(reverse_lazy('idhub:user_dashboard'))
return response
2023-11-02 13:17:07 +00:00
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
2024-01-15 18:11:22 +00:00
kwargs['lang'] = self.request.LANGUAGE_CODE
domain = "{}://{}".format(self.request.scheme, self.request.get_host())
kwargs['domain'] = domain
2023-11-02 13:17:07 +00:00
return kwargs
def form_valid(self, form):
cred = form.save()
if cred:
2023-11-13 09:15:52 +00:00
messages.success(self.request, _("The credential was issued successfully!"))
2023-11-09 16:58:06 +00:00
Event.set_EV_CREDENTIAL_ISSUED_FOR_USER(cred)
Event.set_EV_CREDENTIAL_ISSUED(cred)
2023-12-13 16:52:18 +00:00
url = self.request.session.pop('next_url', None)
if url:
return redirect(url)
2023-11-02 13:17:07 +00:00
else:
2023-11-13 09:15:52 +00:00
messages.error(self.request, _("The credential does not exist!"))
2023-11-02 13:17:07 +00:00
return super().form_valid(form)
2023-10-30 17:29:21 +00:00
2023-11-28 08:39:02 +00:00
class DemandAuthorizationView(MyWallet, FormView):
template_name = "idhub/user/credentials_presentation.html"
subtitle = _('Credential presentation')
icon = 'bi bi-patch-check-fill'
form_class = DemandAuthorizationForm
success_url = reverse_lazy('idhub:user_demand_authorization')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
try:
authorization = form.save()
except Exception:
txt = _("Problems connecting with {url}").format(
url=form.org.response_uri
)
messages.error(self.request, txt)
return super().form_valid(form)
2023-11-28 08:39:02 +00:00
if authorization:
2023-11-29 10:53:30 +00:00
return redirect(authorization)
2023-11-28 08:39:02 +00:00
else:
messages.error(self.request, _("Error sending credential!"))
return super().form_valid(form)
class DidsView(MyWallet, SingleTableView):
2023-10-27 09:19:10 +00:00
template_name = "idhub/user/dids.html"
table_class = DIDTable
2023-11-13 09:15:52 +00:00
subtitle = _('Identities (DIDs)')
2023-10-27 09:19:10 +00:00
icon = 'bi bi-patch-check-fill'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'dids': self.request.user.dids,
})
return context
def get_queryset(self, **kwargs):
queryset = DID.objects.filter(user=self.request.user)
return queryset
2023-11-02 16:13:49 +00:00
2023-11-03 15:42:45 +00:00
class DidRegisterView(MyWallet, CreateView):
2023-10-27 09:19:10 +00:00
template_name = "idhub/user/did_register.html"
2023-11-13 09:15:52 +00:00
subtitle = _('Add a new Identity (DID)')
2023-10-27 09:19:10 +00:00
icon = 'bi bi-patch-check-fill'
wallet = True
model = DID
2024-01-16 13:00:59 +00:00
fields = ('label', 'type')
2023-10-27 09:19:10 +00:00
success_url = reverse_lazy('idhub:user_dids')
object = None
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.set_did()
2023-10-27 09:19:10 +00:00
form.save()
messages.success(self.request, _('DID created successfully'))
2023-11-09 16:58:06 +00:00
Event.set_EV_DID_CREATED(form.instance)
Event.set_EV_DID_CREATED_BY_USER(form.instance)
2023-10-27 09:19:10 +00:00
return super().form_valid(form)
2023-11-03 15:42:45 +00:00
class DidEditView(MyWallet, UpdateView):
2023-10-27 09:19:10 +00:00
template_name = "idhub/user/did_register.html"
2023-11-13 09:15:52 +00:00
subtitle = _('Identities (DIDs)')
2023-10-27 09:19:10 +00:00
icon = 'bi bi-patch-check-fill'
wallet = True
model = DID
2023-11-03 15:27:40 +00:00
fields = ('label',)
2023-10-27 09:19:10 +00:00
success_url = reverse_lazy('idhub:user_dids')
def get(self, request, *args, **kwargs):
self.pk = kwargs['pk']
self.object = get_object_or_404(self.model, pk=self.pk)
return super().get(request, *args, **kwargs)
def form_valid(self, form):
form.save()
2023-10-27 09:19:10 +00:00
messages.success(self.request, _('DID updated successfully'))
return super().form_valid(form)
2023-11-03 15:42:45 +00:00
class DidDeleteView(MyWallet, DeleteView):
2023-11-13 09:15:52 +00:00
subtitle = _('Identities (DIDs)')
2023-10-27 09:19:10 +00:00
icon = 'bi bi-patch-check-fill'
wallet = True
model = DID
success_url = reverse_lazy('idhub:user_dids')
def get(self, request, *args, **kwargs):
self.pk = kwargs['pk']
self.object = get_object_or_404(self.model, pk=self.pk)
2023-11-09 16:58:06 +00:00
Event.set_EV_DID_DELETED(self.object)
2023-10-27 09:19:10 +00:00
self.object.delete()
messages.success(self.request, _('DID delete successfully'))
return redirect(self.success_url)
2023-11-09 16:58:06 +00:00