2024-01-08 19:11:41 +00:00
|
|
|
import os
|
2024-01-10 12:53:43 +00:00
|
|
|
import json
|
2024-01-08 19:11:41 +00:00
|
|
|
import base64
|
|
|
|
import qrcode
|
2023-10-10 08:54:13 +00:00
|
|
|
import logging
|
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 _
|
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
|
2023-11-28 08:39:02 +00:00
|
|
|
from idhub.user.forms import (
|
|
|
|
ProfileForm,
|
|
|
|
RequestCredentialForm,
|
|
|
|
DemandAuthorizationForm
|
|
|
|
)
|
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
|
2023-11-09 16:58:06 +00:00
|
|
|
from idhub.models import DID, VerificableCredential, Event
|
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"
|
|
|
|
|
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class DashboardView(UserView, TemplateView):
|
2023-10-17 15:42:48 +00:00
|
|
|
template_name = "idhub/user/dashboard.html"
|
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"
|
|
|
|
|
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class ProfileView(MyProfile, UpdateView):
|
2023-10-17 15:42:48 +00:00
|
|
|
template_name = "idhub/user/profile.html"
|
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
|
|
|
from_class = ProfileForm
|
|
|
|
fields = ('first_name', 'last_name', 'email')
|
|
|
|
success_url = reverse_lazy('idhub:user_profile')
|
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)
|
|
|
|
|
2023-10-11 07:52:05 +00:00
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class RolesView(MyProfile, TemplateView):
|
2023-10-17 15:42:48 +00:00
|
|
|
template_name = "idhub/user/roles.html"
|
2023-10-10 08:54:13 +00:00
|
|
|
subtitle = _('My roles')
|
|
|
|
icon = 'fa-brands fa-critical-role'
|
|
|
|
|
|
|
|
|
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"
|
2023-10-10 08:54:13 +00:00
|
|
|
subtitle = _('GDPR info')
|
|
|
|
icon = 'bi bi-file-earmark-medical'
|
|
|
|
|
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class CredentialsView(MyWallet, TemplateView):
|
2023-10-17 15:42:48 +00:00
|
|
|
template_name = "idhub/user/credentials.html"
|
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'
|
|
|
|
|
2023-10-30 12:53:19 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
2023-12-12 17:00:04 +00:00
|
|
|
creds = VerificableCredential.objects.filter(
|
|
|
|
user=self.request.user
|
|
|
|
)
|
2023-10-30 12:53:19 +00:00
|
|
|
context.update({
|
2023-12-12 17:00:04 +00:00
|
|
|
'credentials': creds,
|
2023-10-30 12:53:19 +00:00
|
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class CredentialJsonView(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"
|
|
|
|
_pss = '123456'
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
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,
|
|
|
|
user=self.request.user
|
|
|
|
)
|
2024-01-08 19:11:41 +00:00
|
|
|
|
|
|
|
data = self.build_certificate()
|
|
|
|
doc = self.insert_signature(data)
|
|
|
|
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("http://localhost")
|
2024-01-10 12:53:43 +00:00
|
|
|
if DID.objects.filter(eidas1=True).exists():
|
|
|
|
qr = ""
|
|
|
|
|
|
|
|
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):
|
|
|
|
did = DID.objects.filter(eidas1=True).first()
|
|
|
|
if not did:
|
|
|
|
return None, None
|
|
|
|
key_material = json.loads(did.key_material)
|
|
|
|
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()
|
|
|
|
s = certs.load_cert(
|
|
|
|
pfx_data, passphrase
|
2024-01-08 19:11:41 +00:00
|
|
|
)
|
|
|
|
return s
|
|
|
|
|
|
|
|
def insert_signature(self, doc):
|
2024-01-10 12:53:43 +00:00
|
|
|
# import pdb; pdb.set_trace()
|
2024-01-08 19:11:41 +00:00
|
|
|
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.QRStampStyle(
|
|
|
|
stamp_text='Signed by: %(signer)s\nTime: %(ts)s\nURL: %(url)s',
|
|
|
|
text_box_style=text.TextBoxStyle()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
_bf_out = BytesIO()
|
|
|
|
url = "https://localhost:8000/"
|
|
|
|
pdf_signer.sign_pdf(w, output=_bf_out, appearance_text_params={'url': url})
|
|
|
|
return _bf_out.read()
|
|
|
|
|
|
|
|
|
|
|
|
class CredentialJsonView2(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
|
|
|
|
)
|
|
|
|
response = HttpResponse(self.object.data, content_type="application/json")
|
|
|
|
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
|
|
|
|
2023-11-02 13:17:07 +00:00
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super().get_form_kwargs()
|
|
|
|
kwargs['user'] = self.request.user
|
|
|
|
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):
|
2023-11-29 12:22:13 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2023-11-03 15:42:45 +00:00
|
|
|
class DidsView(MyWallet, TemplateView):
|
2023-10-27 09:19:10 +00:00
|
|
|
template_name = "idhub/user/dids.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'
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context.update({
|
|
|
|
'dids': self.request.user.dids,
|
|
|
|
})
|
|
|
|
return context
|
|
|
|
|
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
|
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')
|
|
|
|
object = None
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
form.instance.user = self.request.user
|
2023-11-14 08:48:36 +00:00
|
|
|
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):
|
|
|
|
user = form.save()
|
|
|
|
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
|
|
|
|