From f95960aafef2f0b831609fffaf1a8930aaed4522 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Mar 2024 15:30:42 +0100 Subject: [PATCH 1/5] clean urlparse --- idhub/management/commands/send_mail_admins.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/idhub/management/commands/send_mail_admins.py b/idhub/management/commands/send_mail_admins.py index a878456..bb6a004 100644 --- a/idhub/management/commands/send_mail_admins.py +++ b/idhub/management/commands/send_mail_admins.py @@ -1,7 +1,5 @@ import logging -from urllib.parse import urlparse - from django.conf import settings from django.template import loader from django.core.mail import EmailMultiAlternatives From 230d9ca26ec321e46685ac88071e3df2f5b44df3 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Mar 2024 15:31:07 +0100 Subject: [PATCH 2/5] first step add email system --- oidc4vp/views.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/oidc4vp/views.py b/oidc4vp/views.py index 5da6e2e..eb624da 100644 --- a/oidc4vp/views.py +++ b/oidc4vp/views.py @@ -1,6 +1,9 @@ import json import base64 +import logging +from django.template import loader +from django.core.mail import EmailMultiAlternatives from django.conf import settings from django.views.generic.edit import View, FormView from django.http import HttpResponse, Http404, JsonResponse @@ -18,6 +21,9 @@ from idhub.models import Event from oidc4vp.forms import AuthorizeForm +logger = logging.getLogger(__name__) + + class AuthorizeView(UserView, FormView): title = _("My wallet") section = "MyWallet" @@ -101,6 +107,10 @@ class AuthorizeView(UserView, FormView): @method_decorator(csrf_exempt, name='dispatch') class VerifyView(View): + subject_template_name = 'idhub/admin/registration/start_app_admin_subject.txt' + email_template_name = 'idhub/admin/registration/start_app_admin_email.txt' + html_email_template_name = 'idhub/admin/registration/start_app_admin_email.html' + def get(self, request, *args, **kwargs): org = self.validate(request) presentation_definition = json.dumps(settings.SUPPORTED_CREDENTIALS) @@ -115,6 +125,7 @@ class VerifyView(View): def post(self, request, *args, **kwargs): code = self.request.POST.get("code") vp_tk = self.request.POST.get("vp_token") + self.verification = {} if not vp_tk or not code: raise Http404("Page not Found!") @@ -131,9 +142,11 @@ class VerifyView(View): vp_token.verifing() response = vp_token.get_response_verify() - vp_token.save() + for user in User.objects.filter(is_admin=True): + vp_token.save(user) + self.verification = json.loads(vp_token.result_verify) + self.send_email() response["response"] = "Validation Code {}".format(code) - return JsonResponse(response) def validate(self, request): @@ -152,6 +165,56 @@ class VerifyView(View): raise Http404("Page not Found!") + def send_email(self, user): + """ + Send a email when a user is activated. + """ + if not self.verification: + return + + if self.verification.get('errors') or self.verification.get('warnings'): + return + + email = self.get_email(user) + try: + if settings.ENABLE_EMAIL: + email.send() + return + + logger.warning(user.email) + logger.warning(email.body) + + except Exception as err: + logger.error(err) + return + + def get_verification(self): + return json.dumps(self.verification) + + def get_context(self): + url_domain = "https://{}/".format(settings.DOMAIN) + context = { + "domain": settings.DOMAIN, + "url_domain": url_domain, + "verification": self.get_verification(), + } + return context + + def get_email(self, user): + context = self.get_context() + subject = loader.render_to_string(self.subject_template_name, context) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + body = loader.render_to_string(self.email_template_name, context) + from_email = settings.DEFAULT_FROM_EMAIL + to_email = user.email + + email_message = EmailMultiAlternatives( + subject, body, from_email, [to_email]) + html_email = loader.render_to_string(self.html_email_template_name, context) + email_message.attach_alternative(html_email, 'text/html') + return email_message + class AllowCodeView(View): def get(self, request, *args, **kwargs): From 574fac40cabf2784aa75581af60dac4012db4c37 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Mar 2024 16:56:15 +0100 Subject: [PATCH 3/5] fix sendmail --- oidc4vp/models.py | 5 +++++ oidc4vp/views.py | 31 +++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/oidc4vp/models.py b/oidc4vp/models.py index 916e1d6..4062ddf 100644 --- a/oidc4vp/models.py +++ b/oidc4vp/models.py @@ -267,6 +267,11 @@ class OAuth2VPToken(models.Model): def verifing(self): self.result_verify = verify_presentation(self.vp_token) + def get_verifing(self): + if not self.result_verify: + return {} + return json.loads(self.result_verify) + def get_response_verify(self): response = { "verify": ',', diff --git a/oidc4vp/views.py b/oidc4vp/views.py index eb624da..d323b60 100644 --- a/oidc4vp/views.py +++ b/oidc4vp/views.py @@ -13,6 +13,7 @@ from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.urls import reverse_lazy from django.contrib import messages +from django.contrib.auth import get_user_model from oidc4vp.models import Authorization, Organization, OAuth2VPToken from idhub.mixins import UserView @@ -21,6 +22,8 @@ from idhub.models import Event from oidc4vp.forms import AuthorizeForm + +User = get_user_model() logger = logging.getLogger(__name__) @@ -107,9 +110,9 @@ class AuthorizeView(UserView, FormView): @method_decorator(csrf_exempt, name='dispatch') class VerifyView(View): - subject_template_name = 'idhub/admin/registration/start_app_admin_subject.txt' - email_template_name = 'idhub/admin/registration/start_app_admin_email.txt' - html_email_template_name = 'idhub/admin/registration/start_app_admin_email.html' + subject_template_name = 'verify_subject.txt' + email_template_name = 'verify_email.txt' + html_email_template_name = 'verify_email.html' def get(self, request, *args, **kwargs): org = self.validate(request) @@ -132,20 +135,22 @@ class VerifyView(View): org = self.validate(request) - vp_token = OAuth2VPToken( + self.vp_token = OAuth2VPToken( vp_token = vp_tk, organization=org, code=code ) + if not vp_token.authorization: raise Http404("Page not Found!") - vp_token.verifing() - response = vp_token.get_response_verify() + self.vp_token.verifing() + response = self.vp_token.get_response_verify() + self.vp_token.save() + for user in User.objects.filter(is_admin=True): - vp_token.save(user) - self.verification = json.loads(vp_token.result_verify) - self.send_email() + self.send_email(user) + response["response"] = "Validation Code {}".format(code) return JsonResponse(response) @@ -169,10 +174,11 @@ class VerifyView(View): """ Send a email when a user is activated. """ - if not self.verification: + verification = self.vp_token.get_result_verify() + if not verification: return - if self.verification.get('errors') or self.verification.get('warnings'): + if verification.get('errors') or verification.get('warnings'): return email = self.get_email(user) @@ -188,9 +194,6 @@ class VerifyView(View): logger.error(err) return - def get_verification(self): - return json.dumps(self.verification) - def get_context(self): url_domain = "https://{}/".format(settings.DOMAIN) context = { From d03c1b017e3d1a0df2afad6c47e25f8879ff27f4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Mar 2024 17:03:35 +0100 Subject: [PATCH 4/5] fix --- oidc4vp/models.py | 2 +- oidc4vp/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oidc4vp/models.py b/oidc4vp/models.py index 4062ddf..9037a8b 100644 --- a/oidc4vp/models.py +++ b/oidc4vp/models.py @@ -267,7 +267,7 @@ class OAuth2VPToken(models.Model): def verifing(self): self.result_verify = verify_presentation(self.vp_token) - def get_verifing(self): + def get_result_verify(self): if not self.result_verify: return {} return json.loads(self.result_verify) diff --git a/oidc4vp/views.py b/oidc4vp/views.py index d323b60..e5e8cec 100644 --- a/oidc4vp/views.py +++ b/oidc4vp/views.py @@ -141,7 +141,7 @@ class VerifyView(View): code=code ) - if not vp_token.authorization: + if not self.vp_token.authorization: raise Http404("Page not Found!") self.vp_token.verifing() From 7d88b1e0cb4e0f9500eef874eb9c913418523e36 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Mar 2024 17:52:39 +0100 Subject: [PATCH 5/5] add code into the email --- oidc4vp/models.py | 7 +++++++ oidc4vp/views.py | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/oidc4vp/models.py b/oidc4vp/models.py index 9037a8b..2ed3d30 100644 --- a/oidc4vp/models.py +++ b/oidc4vp/models.py @@ -264,6 +264,12 @@ class OAuth2VPToken(models.Model): self.authorization = Authorization.objects.filter(code=code).first() + @property + def code(self): + if not self.authorization: + return '' + return self.authorization.code + def verifing(self): self.result_verify = verify_presentation(self.vp_token) @@ -312,3 +318,4 @@ class OAuth2VPToken(models.Model): self.user_info = tk.get( "verifiableCredential", [{}] )[-1].get("credentialSubject") + return json.dumps(self.user_info, indent=2) diff --git a/oidc4vp/views.py b/oidc4vp/views.py index e5e8cec..359a638 100644 --- a/oidc4vp/views.py +++ b/oidc4vp/views.py @@ -110,9 +110,9 @@ class AuthorizeView(UserView, FormView): @method_decorator(csrf_exempt, name='dispatch') class VerifyView(View): - subject_template_name = 'verify_subject.txt' - email_template_name = 'verify_email.txt' - html_email_template_name = 'verify_email.html' + subject_template_name = 'email/verify_subject.txt' + email_template_name = 'email/verify_email.txt' + html_email_template_name = 'email/verify_email.html' def get(self, request, *args, **kwargs): org = self.validate(request) @@ -128,7 +128,6 @@ class VerifyView(View): def post(self, request, *args, **kwargs): code = self.request.POST.get("code") vp_tk = self.request.POST.get("vp_token") - self.verification = {} if not vp_tk or not code: raise Http404("Page not Found!") @@ -200,6 +199,7 @@ class VerifyView(View): "domain": settings.DOMAIN, "url_domain": url_domain, "verification": self.get_verification(), + "code": self.vp_token.code, } return context @@ -218,6 +218,8 @@ class VerifyView(View): email_message.attach_alternative(html_email, 'text/html') return email_message + def get_verification(self): + return self.vp_token.get_user_info() class AllowCodeView(View): def get(self, request, *args, **kwargs):