diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index aec6605..b1300fc 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -23,6 +23,33 @@ from idhub.models import ( from idhub_auth.models import User +class TermsConditionsForm(forms.Form): + accept = forms.BooleanField( + label=_("Accept terms and conditions of the service"), + required=False + ) + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) + super().__init__(*args, **kwargs) + + def clean(self): + data = self.cleaned_data + if data.get("accept"): + self.user.accept_gdpr = True + else: + self.user.accept_gdpr = False + return data + + def save(self, commit=True): + + if commit: + self.user.save() + return self.user + + return + + class ImportForm(forms.Form): did = forms.ChoiceField(label=_("Did"), choices=[]) eidas1 = forms.ChoiceField( diff --git a/idhub/admin/views.py b/idhub/admin/views.py index eb150e1..cebca94 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -9,7 +9,7 @@ from django_tables2 import SingleTableView from django.conf import settings from django.utils.translation import gettext_lazy as _ -from django.views.generic.base import TemplateView +from django.views.generic.base import TemplateView, View from django.views.generic.edit import ( CreateView, DeleteView, @@ -29,6 +29,7 @@ from idhub.email.views import NotifyActivateUserByEmail from idhub.admin.forms import ( ImportForm, MembershipForm, + TermsConditionsForm, SchemaForm, UserRolForm, ImportCertificateForm, @@ -49,6 +50,41 @@ from idhub.models import ( ) +class TermsAndConditionsView(AdminView, FormView): + template_name = "idhub/admin/terms_conditions.html" + title = _("GDPR") + section = "" + subtitle = _('Accept Terms and Conditions') + icon = 'bi bi-file-earmark-medical' + form_class = TermsConditionsForm + success_url = reverse_lazy('idhub:admin_dashboard') + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['user'] = self.request.user + kwargs['initial'] = {"accept": self.request.user.accept_gdpr} + return kwargs + + def form_valid(self, form): + user = form.save() + return super().form_valid(form) + + +class DobleFactorAuthView(AdminView, View): + url = reverse_lazy('idhub:admin_dashboard') + + def get(self, request, *args, **kwargs): + self.check_valid_user() + if not self.request.session.get("2fauth"): + return redirect(self.url) + + if self.request.session.get("2fauth") == str(kwargs.get("admin2fauth")): + self.request.session.pop("2fauth", None) + return redirect(self.url) + + return redirect(reverse_lazy("idhub:login")) + + class DashboardView(AdminView, SingleTableView): template_name = "idhub/admin/dashboard.html" table_class = DashboardTable @@ -119,6 +155,7 @@ class PeopleView(People, TemplateView): class PeopleActivateView(PeopleView): def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) @@ -140,6 +177,7 @@ class PeopleActivateView(PeopleView): class PeopleDeleteView(PeopleView): def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) @@ -304,6 +342,7 @@ class PeopleMembershipDeleteView(PeopleView): model = Membership def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) @@ -391,6 +430,7 @@ class PeopleRolDeleteView(PeopleView): model = UserRol def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) user = self.object.user @@ -454,6 +494,7 @@ class RolDeleteView(AccessControl): model = Rol def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) @@ -527,6 +568,7 @@ class ServiceDeleteView(AccessControl): model = Service def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) @@ -571,6 +613,7 @@ class CredentialView(Credentials): class CredentialJsonView(Credentials): def get(self, request, *args, **kwargs): + self.check_valid_user() pk = kwargs['pk'] self.object = get_object_or_404( VerificableCredential, @@ -585,6 +628,7 @@ class RevokeCredentialsView(Credentials): success_url = reverse_lazy('idhub:admin_credentials') def get(self, request, *args, **kwargs): + self.check_valid_user() pk = kwargs['pk'] self.object = get_object_or_404( VerificableCredential, @@ -604,6 +648,7 @@ class DeleteCredentialsView(Credentials): success_url = reverse_lazy('idhub:admin_credentials') def get(self, request, *args, **kwargs): + self.check_valid_user() pk = kwargs['pk'] self.object = get_object_or_404( VerificableCredential, @@ -683,6 +728,7 @@ class DidDeleteView(Credentials, DeleteView): success_url = reverse_lazy('idhub:admin_dids') def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(self.model, pk=self.pk) Event.set_EV_ORG_DID_DELETED_BY_ADMIN(self.object) @@ -737,6 +783,7 @@ class SchemasView(SchemasMix): class SchemasDeleteView(SchemasMix): def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(Schemas, pk=self.pk) self.object.delete() @@ -747,6 +794,7 @@ class SchemasDeleteView(SchemasMix): class SchemasDownloadView(SchemasMix): def get(self, request, *args, **kwargs): + self.check_valid_user() self.pk = kwargs['pk'] self.object = get_object_or_404(Schemas, pk=self.pk) @@ -825,6 +873,7 @@ class SchemasImportView(SchemasMix): class SchemasImportAddView(SchemasMix): def get(self, request, *args, **kwargs): + self.check_valid_user() file_name = kwargs['file_schema'] schemas_files = os.listdir(settings.SCHEMAS_DIR) if not file_name in schemas_files: diff --git a/idhub/email/views.py b/idhub/email/views.py index 72e0daa..f14e2a5 100644 --- a/idhub/email/views.py +++ b/idhub/email/views.py @@ -13,7 +13,11 @@ logger = logging.getLogger(__name__) class NotifyActivateUserByEmail: - def get_email_context(self, user): + subject_template_name = 'idhub/admin/registration/activate_user_subject.txt' + email_template_name = 'idhub/admin/registration/activate_user_email.txt' + html_email_template_name = 'idhub/admin/registration/activate_user_email.html' + + def get_email_context(self, user, token): """ Define a new context with a token for put in a email when send a email for add a new password @@ -22,35 +26,35 @@ class NotifyActivateUserByEmail: current_site = get_current_site(self.request) site_name = current_site.name domain = current_site.domain + if not token: + token = default_token_generator.make_token(user) + context = { 'email': user.email, 'domain': domain, 'site_name': site_name, 'uid': urlsafe_base64_encode(force_bytes(user.pk)), 'user': user, - 'token': default_token_generator.make_token(user), + 'token': token, 'protocol': protocol, } return context - def send_email(self, user): + def send_email(self, user, token=None): """ Send a email when a user is activated. """ - context = self.get_email_context(user) - subject_template_name = 'idhub/admin/registration/activate_user_subject.txt' - email_template_name = 'idhub/admin/registration/activate_user_email.txt' - html_email_template_name = 'idhub/admin/registration/activate_user_email.html' - subject = loader.render_to_string(subject_template_name, context) + context = self.get_email_context(user, token) + 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(email_template_name, context) + 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(html_email_template_name, context) + html_email = loader.render_to_string(self.html_email_template_name, context) email_message.attach_alternative(html_email, 'text/html') try: if settings.DEVELOPMENT: diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py deleted file mode 100644 index bc9f011..0000000 --- a/idhub/migrations/0001_initial.py +++ /dev/null @@ -1,367 +0,0 @@ -# Generated by Django 4.2.5 on 2024-01-18 11:32 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='DID', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'type', - models.PositiveSmallIntegerField( - choices=[(1, 'Key'), (2, 'Web')], verbose_name='Type' - ), - ), - ('created_at', models.DateTimeField(auto_now=True)), - ('label', models.CharField(max_length=50, verbose_name='Label')), - ('did', models.CharField(max_length=250)), - ('key_material', models.TextField()), - ('eidas1', models.BooleanField(default=False)), - ('didweb_document', models.TextField()), - ( - 'user', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='dids', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name='File_datas', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('file_name', models.CharField(max_length=250)), - ('success', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now=True)), - ], - ), - migrations.CreateModel( - name='Rol', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('name', models.CharField(max_length=250, verbose_name='name')), - ( - 'description', - models.CharField( - max_length=250, null=True, verbose_name='Description' - ), - ), - ], - ), - migrations.CreateModel( - name='Schemas', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('type', models.CharField(max_length=250)), - ('file_schema', models.CharField(max_length=250)), - ('data', models.TextField()), - ('created_at', models.DateTimeField(auto_now=True)), - ], - ), - migrations.CreateModel( - name='Service', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('domain', models.CharField(max_length=250, verbose_name='Domain')), - ( - 'description', - models.CharField(max_length=250, verbose_name='Description'), - ), - ('rol', models.ManyToManyField(to='idhub.rol')), - ], - ), - migrations.CreateModel( - name='VCTemplate', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('wkit_template_id', models.CharField(max_length=250)), - ('data', models.TextField()), - ], - ), - migrations.CreateModel( - name='VerificableCredential', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('id_string', models.CharField(max_length=250)), - ('verified', models.BooleanField()), - ('created_on', models.DateTimeField(auto_now=True)), - ('issued_on', models.DateTimeField(null=True)), - ('data', models.TextField()), - ('csv_data', models.TextField()), - ('hash', models.CharField(max_length=260)), - ( - 'status', - models.PositiveSmallIntegerField( - choices=[ - (1, 'Enabled'), - (2, 'Issued'), - (3, 'Revoked'), - (4, 'Expired'), - ], - default=1, - ), - ), - ( - 'eidas1_did', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to='idhub.did', - ), - ), - ( - 'issuer_did', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='vcredentials', - to='idhub.did', - ), - ), - ( - 'schema', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='vcredentials', - to='idhub.schemas', - ), - ), - ( - 'subject_did', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='subject_credentials', - to='idhub.did', - ), - ), - ( - 'user', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='vcredentials', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name='Membership', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'type', - models.PositiveSmallIntegerField( - choices=[(1, 'Beneficiary'), (2, 'Employee'), (3, 'Member')], - verbose_name='Type of membership', - ), - ), - ( - 'start_date', - models.DateField( - blank=True, - help_text='What date did the membership start?', - null=True, - verbose_name='Start date', - ), - ), - ( - 'end_date', - models.DateField( - blank=True, - help_text='What date will the membership end?', - null=True, - verbose_name='End date', - ), - ), - ( - 'user', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='memberships', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name='Event', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('created', models.DateTimeField(auto_now=True, verbose_name='Date')), - ( - 'message', - models.CharField(max_length=350, verbose_name='Description'), - ), - ( - 'type', - models.PositiveSmallIntegerField( - choices=[ - (1, 'User registered'), - (2, 'User welcomed'), - (3, 'Data update requested by user'), - ( - 4, - 'Data update requested. Pending approval by administrator', - ), - (5, "User's data updated by admin"), - (6, 'Your data updated by admin'), - (7, 'User deactivated by admin'), - (8, 'DID created by user'), - (9, 'DID created'), - (10, 'DID deleted'), - (11, 'Credential deleted by user'), - (12, 'Credential deleted'), - (13, 'Credential issued for user'), - (14, 'Credential issued'), - (15, 'Credential presented by user'), - (16, 'Credential presented'), - (17, 'Credential enabled'), - (18, 'Credential available'), - (19, 'Credential revoked by admin'), - (20, 'Credential revoked'), - (21, 'Role created by admin'), - (22, 'Role modified by admin'), - (23, 'Role deleted by admin'), - (24, 'Service created by admin'), - (25, 'Service modified by admin'), - (26, 'Service deleted by admin'), - (27, 'Organisational DID created by admin'), - (28, 'Organisational DID deleted by admin'), - (29, 'User deactivated'), - (30, 'User activated'), - ], - verbose_name='Event', - ), - ), - ( - 'user', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='events', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.CreateModel( - name='UserRol', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'service', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='users', - to='idhub.service', - verbose_name='Service', - ), - ), - ( - 'user', - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='roles', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - 'unique_together': {('user', 'service')}, - }, - ), - ] diff --git a/idhub/mixins.py b/idhub/mixins.py index a87d821..f101f0e 100644 --- a/idhub/mixins.py +++ b/idhub/mixins.py @@ -1,14 +1,48 @@ from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth import views as auth_views -from django.urls import reverse_lazy, resolve from django.utils.translation import gettext_lazy as _ +from django.contrib.auth import views as auth_views +from django.core.exceptions import PermissionDenied +from django.urls import reverse_lazy, resolve from django.shortcuts import redirect from django.core.cache import cache +class Http403(PermissionDenied): + status_code = 403 + default_detail = _('Permission denied. User is not authenticated') + default_code = 'forbidden' + + def __init__(self, detail=None, code=None): + if detail is not None: + self.detail = details or self.default_details + if code is not None: + self.code = code or self.default_code + + class UserView(LoginRequiredMixin): login_url = "/login/" wallet = False + path_terms = [ + 'admin_terms_and_conditions', + 'user_terms_and_conditions', + 'user_gdpr', + ] + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + url = self.check_gdpr() + if url: + return url + + return response + + def post(self, request, *args, **kwargs): + response = super().post(request, *args, **kwargs) + url = self.check_gdpr() + if url: + return url + + return response def get(self, request, *args, **kwargs): self.admin_validated = cache.get("KEY_DIDS") @@ -32,12 +66,29 @@ class UserView(LoginRequiredMixin): }) return context + def check_gdpr(self): + if not self.request.user.accept_gdpr: + url = reverse_lazy("idhub:user_terms_and_conditions") + if self.request.user.is_admin: + url = reverse_lazy("idhub:admin_terms_and_conditions") + if resolve(self.request.path).url_name not in self.path_terms: + return redirect(url) + class AdminView(UserView): def get(self, request, *args, **kwargs): - if not request.user.is_admin: - url = reverse_lazy('idhub:user_dashboard') - return redirect(url) - + self.check_valid_user() return super().get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.check_valid_user() + return super().post(request, *args, **kwargs) + + def check_valid_user(self): + if not self.request.user.is_admin: + raise Http403() + + if self.request.session.get("2fauth"): + raise Http403() + diff --git a/idhub/templates/auth/2fadmin.html b/idhub/templates/auth/2fadmin.html new file mode 100644 index 0000000..4dc2ae6 --- /dev/null +++ b/idhub/templates/auth/2fadmin.html @@ -0,0 +1,19 @@ +{% extends "auth/login_base.html" %} +{% load i18n django_bootstrap5 %} + +{% block login_content %} + +
+
+

{% trans 'Doble Factor of Authentication' %}

+
+
+ +
+
+
+ {% trans "We have sent an email with a link that you have to select in order to login." %} +
+
+
+{% endblock %} diff --git a/idhub/templates/auth/2fadmin_email.html b/idhub/templates/auth/2fadmin_email.html new file mode 100644 index 0000000..d2253c5 --- /dev/null +++ b/idhub/templates/auth/2fadmin_email.html @@ -0,0 +1,26 @@ +{% load i18n %}{% autoescape off %} +

+{% blocktrans %}You're receiving this email because you try to access in {{ site_name }}.{% endblocktrans %} +

+ +

+{% trans "Please go to the following page" %} +

+ +

+{% block reset_link %} + +{{ protocol }}://{{ domain }}{% url 'idhub:admin_2fauth' admin2fauth=token %} + +{% endblock %} +

+ +

+{% trans "Thanks for using our site!" %} +

+ +

+{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +

+ +{% endautoescape %} diff --git a/idhub/templates/auth/2fadmin_email.txt b/idhub/templates/auth/2fadmin_email.txt new file mode 100644 index 0000000..a9ef3e5 --- /dev/null +++ b/idhub/templates/auth/2fadmin_email.txt @@ -0,0 +1,14 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}You're receiving this email because you try to access in {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}{% url 'idhub:admin_2fauth' admin2fauth=token %} +{% endblock %} +{% trans "Your username, in case you've forgotten:" %} {{ user.username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} + +{% endautoescape %} diff --git a/idhub/templates/auth/2fadmin_email_subject.txt b/idhub/templates/auth/2fadmin_email_subject.txt new file mode 100644 index 0000000..6d3bb21 --- /dev/null +++ b/idhub/templates/auth/2fadmin_email_subject.txt @@ -0,0 +1,3 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}Authentication in {{ site_name }}{% endblocktrans %} +{% endautoescape %} \ No newline at end of file diff --git a/idhub/templates/idhub/admin/terms_conditions.html b/idhub/templates/idhub/admin/terms_conditions.html new file mode 100644 index 0000000..51f8b6c --- /dev/null +++ b/idhub/templates/idhub/admin/terms_conditions.html @@ -0,0 +1,57 @@ +{% extends "idhub/base_admin.html" %} +{% load i18n %} + +{% block content %} +

+ + {{ subtitle }} +

+{% load django_bootstrap5 %} +
+{% csrf_token %} +{% if form.errors %} + +{% endif %} +
+
+ You must read the terms and conditions of this service and accept the + Read GDPR +
+
+
+
+ {% bootstrap_form form %} +
+
+
+ {% translate "Cancel" %} + +
+ +
+ + +{% endblock %} diff --git a/idhub/templates/idhub/base.html b/idhub/templates/idhub/base.html index 52f8f33..e579a81 100644 --- a/idhub/templates/idhub/base.html +++ b/idhub/templates/idhub/base.html @@ -140,9 +140,6 @@

{{ title }}

-
- -
diff --git a/idhub/templates/idhub/base_admin.html b/idhub/templates/idhub/base_admin.html index c97f165..271d6d5 100644 --- a/idhub/templates/idhub/base_admin.html +++ b/idhub/templates/idhub/base_admin.html @@ -170,9 +170,6 @@

{{ title }}

-
- -
diff --git a/idhub/templates/idhub/user/gdpr.html b/idhub/templates/idhub/user/gdpr.html index 1ac25fd..16476b8 100644 --- a/idhub/templates/idhub/user/gdpr.html +++ b/idhub/templates/idhub/user/gdpr.html @@ -6,4 +6,7 @@ {{ subtitle }} +Gdpr info
+If you want accept or revoke the Gdpr go to: + Terms and conditions {% endblock %} diff --git a/idhub/templates/idhub/user/terms_conditions.html b/idhub/templates/idhub/user/terms_conditions.html new file mode 100644 index 0000000..8a02175 --- /dev/null +++ b/idhub/templates/idhub/user/terms_conditions.html @@ -0,0 +1,57 @@ +{% extends "idhub/base.html" %} +{% load i18n %} + +{% block content %} +

+ + {{ subtitle }} +

+{% load django_bootstrap5 %} +
+{% csrf_token %} +{% if form.errors %} + +{% endif %} +
+
+ You must read the terms and conditions of this service and accept the + Read GDPR +
+
+
+
+ {% bootstrap_form form %} +
+
+
+ {% translate "Cancel" %} + +
+ +
+ + +{% endblock %} diff --git a/idhub/templates/templates/musician/address_check_delete.html b/idhub/templates/templates/musician/address_check_delete.html deleted file mode 100644 index 27981d4..0000000 --- a/idhub/templates/templates/musician/address_check_delete.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} -
- {% csrf_token %} -

{% blocktrans with address_name=object.full_address_name %}Are you sure that you want remove the address: "{{ address_name }}"?{% endblocktrans %}

-

{% trans 'WARNING: This action cannot be undone.' %}

- - {% trans 'Cancel' %} -
-{% endblock %} diff --git a/idhub/templates/templates/musician/address_form.html b/idhub/templates/templates/musician/address_form.html deleted file mode 100644 index de21067..0000000 --- a/idhub/templates/templates/musician/address_form.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "musician/base.html" %} -{% load bootstrap4 i18n %} - -{% block content %} -

{{ service.verbose_name }}

- -
- {% csrf_token %} - {% bootstrap_form form %} - {% buttons %} - {% trans "Cancel" %} - - {% if form.instance %} -
- {% trans "Delete" %} -
- {% endif %} - {% endbuttons %} -
-{% endblock %} diff --git a/idhub/templates/templates/musician/addresses.html b/idhub/templates/templates/musician/addresses.html deleted file mode 100644 index 1ebc8b7..0000000 --- a/idhub/templates/templates/musician/addresses.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "musician/mail_base.html" %} -{% load i18n %} - -{% block tabcontent %} -
- - - - - - - - - - - - - - - - - {% for obj in object_list %} - - - - - - - {% endfor %} - - {% include "musician/components/table_paginator.html" %} -
{% trans "Email" %}{% trans "Domain" %}{% trans "Mailboxes" %}{% trans "Forward" %}
{{ obj.full_address_name }}{{ obj.domain.name }} - {% for mailbox in obj.mailboxes %} - {{ mailbox.name }} - {% if not forloop.last %}
{% endif %} - {% endfor %} -
{{ obj.forward }}
- {% trans "New mail address" %} - -
-{% endblock %} diff --git a/idhub/templates/templates/musician/billing.html b/idhub/templates/templates/musician/billing.html deleted file mode 100644 index 75f2580..0000000 --- a/idhub/templates/templates/musician/billing.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n l10n %} - -{% block content %} - -

{% trans "Billing" %}

-

{% trans "Billing page description." %}

- - - - - - - - - - - - - - - - - - - - {% for bill in object_list %} - - - - - - - - {% endfor %} - -{# TODO: define proper colspan #} -{% include "musician/components/table_paginator.html" %} -
{% trans "Number" %}{% trans "Bill date" %}{% trans "Type" %}{% trans "Total" %}{% trans "Download PDF" %}
{{ bill.number }}{{ bill.created_on|date:"SHORT_DATE_FORMAT" }}{{ bill.type }}{{ bill.total|floatformat:2|localize }}€
- -{% endblock %} diff --git a/idhub/templates/templates/musician/components/paginator.html b/idhub/templates/templates/musician/components/paginator.html deleted file mode 100644 index 9a7a406..0000000 --- a/idhub/templates/templates/musician/components/paginator.html +++ /dev/null @@ -1,29 +0,0 @@ -{# #} -
-
{{ page_obj.paginator.count }} items in total
-
- {% if page_obj.has_previous %} - « - - {% endif %} - Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} - {% if page_obj.has_next %} - - » - {% endif %} -
-
-
- Showing - - per page - -
-
-
diff --git a/idhub/templates/templates/musician/components/table_paginator.html b/idhub/templates/templates/musician/components/table_paginator.html deleted file mode 100644 index 913d5ae..0000000 --- a/idhub/templates/templates/musician/components/table_paginator.html +++ /dev/null @@ -1,50 +0,0 @@ -{# #} -{% load i18n %} - - - - {{ page_obj.paginator.count }} items in total - - - - -
- Showing - - per page - -
- - - diff --git a/idhub/templates/templates/musician/components/usage_progress_bar.html b/idhub/templates/templates/musician/components/usage_progress_bar.html deleted file mode 100644 index b35e84c..0000000 --- a/idhub/templates/templates/musician/components/usage_progress_bar.html +++ /dev/null @@ -1,22 +0,0 @@ -{% comment %} -Resource usage rendered as bootstrap progress bar - -Expected parameter: detail -Expected structure: dictionary or object with attributes: - - usage (int): 125 - - total (int): 200 - - unit (string): 'MB' - - percent (int: [0, 25, 50, 75, 100]: 75 -{% endcomment %} - -
- {% if detail %} - {{ detail.usage }} {{ detail.unit }} - {% else %} - N/A - {% endif %} -
-
-
-
diff --git a/idhub/templates/templates/musician/dashboard.html b/idhub/templates/templates/musician/dashboard.html deleted file mode 100644 index d359fcb..0000000 --- a/idhub/templates/templates/musician/dashboard.html +++ /dev/null @@ -1,161 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} - -

{% trans "Welcome back" %} {{ profile.username }}

-{% if profile.last_login %} -

{% blocktrans with last_login=profile.last_login|date:"SHORT_DATE_FORMAT" %}Last time you logged in was: {{ last_login }}{% endblocktrans %}

-{% else %} -

{% trans "It's the first time you log into the system, welcome on board!" %}

-{% endif %} - -
- {% for resource, usage in resource_usage.items %} -
-
-
{{ usage.verbose_name }}
- {% include "musician/components/usage_progress_bar.html" with detail=usage.data %} - {% if usage.data.alert %} -
- {{ usage.data.alert }} -
- {% endif %} -
-
- {% endfor %} -
-
-
{% trans "Notifications" %}
- {% for message in notifications %} -

{{ message }}

- {% empty %} -

{% trans "There is no notifications at this time." %}

- {% endfor %} -
-
-
- - -

{% trans "Your domains and websites" %}

-

{% trans "Dashboard page description." %}

- -{% for domain in domains %} -
-
-
-
- {{ domain.name }} -
-
- {% with domain.websites.0 as website %} - {% with website.contents.0 as content %} - - {% endwith %} - {% endwith %} -
-
- {% comment "@slamora: orchestra doesn't have this information [won't fix] See issue #2" %} - {% trans "Expiration date" %}: {{ domain.expiration_date|date:"SHORT_DATE_FORMAT" }} - {% endcomment %} -
-
-
-
-
-

{% trans "Mail" %}

-

-

- {{ domain.addresses|length }} {% trans "mail addresses created" %} -

- -
-
-

{% trans "Mail list" %}

-

- -
-
-

{% trans "Software as a Service" %}

-

-

{% trans "Nothing installed" %}

- -
-
-
-

{% trans "Disk usage" %}

-

-
- {% include "musician/components/usage_progress_bar.html" with detail=domain.usage %} -
-
-
-
-
- -{% endfor %} - - - -{% endblock %} -{% block extrascript %} - -{% endblock %} diff --git a/idhub/templates/templates/musician/databases.html b/idhub/templates/templates/musician/databases.html deleted file mode 100644 index cf71d1f..0000000 --- a/idhub/templates/templates/musician/databases.html +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} - -

{{ service.verbose_name }}

-

{{ service.description }}

- -{% for database in object_list %} -
-
-
-
- {{ database.name }} -
-
- {% trans "Type" %}: {{ database.type }} -
-
- {% comment "@slamora: orchestra doesn't provide this information [won't fix] See issue #3" %} - {% trans "associated to" %}: {{ database.domain|default:"-" }} - {% endcomment %} -
-
-
-
-
-

Database users

-
    - {% for user in database.users %} - {# TODO(@slamora) render in two columns #} -
  • {{ user.username }}
  • - {% empty %} -
  • {% trans "No users for this database." %}
  • - {% endfor %} -
-
-
-

Database usage

-

- {% include "musician/components/usage_progress_bar.html" with detail=database.usage %} -
- -
-
- -{% empty %} -
-
-
-
-

- {# Translators: database page when there isn't any database. #} -
{% trans "Ooops! Looks like there is nothing here!" %}
-
-
-
-
-{% endfor %} - - {% if object_list|length > 0 %} - {% include "musician/components/paginator.html" %} - {% endif %} -{% endblock %} diff --git a/idhub/templates/templates/musician/domain_detail.html b/idhub/templates/templates/musician/domain_detail.html deleted file mode 100644 index 761c331..0000000 --- a/idhub/templates/templates/musician/domain_detail.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} -{% trans "Go back" %} - -

{% trans "DNS settings for" %} {{ object.name }}

-

{% trans "DNS settings page description." %}

- - - - - - - - - - - - - - {% for record in object.records %} - - - - - {% endfor %} - -
{% trans "Type" %}{% trans "Value" %}
{{ record.type }}{{ record.value }}
-{% endblock %} diff --git a/idhub/templates/templates/musician/mail_base.html b/idhub/templates/templates/musician/mail_base.html deleted file mode 100644 index 9445f7f..0000000 --- a/idhub/templates/templates/musician/mail_base.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} -{% if active_domain %} -{% trans "Go to global" %} -{% endif %} - -

{{ service.verbose_name }} - {% if active_domain %}{% trans "for" %} {{ active_domain.name }}{% endif %} -

-

{{ service.description }}

- -{% with request.resolver_match.url_name as url_name %} - - -{% endwith %} - -
- {% block tabcontent %} - {% endblock %} - -{% endblock %} diff --git a/idhub/templates/templates/musician/mailbox_change_password.html b/idhub/templates/templates/musician/mailbox_change_password.html deleted file mode 100644 index e18b95a..0000000 --- a/idhub/templates/templates/musician/mailbox_change_password.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "musician/base.html" %} -{% load bootstrap4 i18n %} - -{% block content %} -

{% trans "Change password" %}: {{ object.name }}

- -
- {% csrf_token %} - {% bootstrap_form form %} - {% buttons %} - {% trans "Cancel" %} - - {% endbuttons %} -
-{% endblock %} diff --git a/idhub/templates/templates/musician/mailbox_check_delete.html b/idhub/templates/templates/musician/mailbox_check_delete.html deleted file mode 100644 index 18b9249..0000000 --- a/idhub/templates/templates/musician/mailbox_check_delete.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} -
- {% csrf_token %} -

{% blocktrans with name=object.name %}Are you sure that you want remove the mailbox: "{{ name }}"?{% endblocktrans %}

- -

{% trans 'WARNING: This action cannot be undone.' %}

- - {% trans 'Cancel' %} -
-{% endblock %} diff --git a/idhub/templates/templates/musician/mailbox_form.html b/idhub/templates/templates/musician/mailbox_form.html deleted file mode 100644 index 5fb9465..0000000 --- a/idhub/templates/templates/musician/mailbox_form.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "musician/base.html" %} -{% load bootstrap4 i18n %} - -{% block content %} -

{{ service.verbose_name }}

- -{% if extra_mailbox %} - -{% endif %} - -
- {% csrf_token %} - {% bootstrap_form form %} - {% buttons %} - {% trans "Cancel" %} - - {% if form.instance %} - - {% endif %} - {% endbuttons %} -
-{% endblock %} diff --git a/idhub/templates/templates/musician/mailboxes.html b/idhub/templates/templates/musician/mailboxes.html deleted file mode 100644 index a9f3700..0000000 --- a/idhub/templates/templates/musician/mailboxes.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "musician/mail_base.html" %} -{% load i18n %} - -{% block tabcontent %} -
- - - - - - - - - - - - - - - {% for mailbox in object_list %} - {# #} - {% if mailbox.is_active %} - - - - - - {% endif %}{# #} - {% endfor %} - - {% include "musician/components/table_paginator.html" %} -
{% trans "Name" %}{% trans "Filtering" %}{% trans "Addresses" %}
- {{ mailbox.name }} - - {% trans "Update password" %} - {{ mailbox.filtering }} - {% for addr in mailbox.addresses %} - - {{ addr.full_address_name }} -
- {% endfor %} -
- {% trans "New mailbox" %} - -
-{% endblock %} diff --git a/idhub/templates/templates/musician/mailinglists.html b/idhub/templates/templates/musician/mailinglists.html deleted file mode 100644 index 6ff509e..0000000 --- a/idhub/templates/templates/musician/mailinglists.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} -{% if active_domain %} -{% trans "Go to global" %} -{% endif %} - -

{{ service.verbose_name }}{% if active_domain %} {% trans "for" %} {{ active_domain.name }}{% endif %}

-

{{ service.description }}

- - - - - - - - - - - - - - - - - - - - {% for resource in object_list %} - - - {% if resource.is_active %} - - {% else %} - - {% endif %} - - - - - {% endfor %} - - {% include "musician/components/table_paginator.html" %} -
NameStatusAddressAdmin emailConfigure
{{ resource.name }}{% trans "Active" %}{% trans "Inactive" %}{{ resource.address_name}}{{ resource.admin_email }}Mailtrain
-{% endblock %} diff --git a/idhub/templates/templates/musician/profile.html b/idhub/templates/templates/musician/profile.html deleted file mode 100644 index f6bdbce..0000000 --- a/idhub/templates/templates/musician/profile.html +++ /dev/null @@ -1,65 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} - -

{% trans "Profile" %}

-

{% trans "Little description on profile page." %}

- -
-
-
{% trans "User information" %}
-
-
-
- user-profile-picture -
-
-
-

{{ profile.username }}

-

{{ profile.type }}

-

{% trans "Preferred language:" %} {{ profile.language|language_name_local }}

-
- {% comment %} - - - {% endcomment %} -
-
- - {% with profile.billing as contact %} -
-
{% trans "Billing information" %}
-
-
{{ contact.name }}
-
{{ contact.address }}
-
- {{ contact.zipcode }} - {{ contact.city }} - {{ contact.country }} -
-
- {{ contact.vat }} -
- -
- {% trans "payment method:" %} {{ payment.method }} -
-
- {% if payment.method == 'SEPADirectDebit' %} - IBAN {{ payment.data.iban }} - {% else %} - {# #} - Details: {{ payment.data }} - {% endif %} -
- -
-
-
-{% endwith %} -{% endblock %} diff --git a/idhub/templates/templates/musician/saas.html b/idhub/templates/templates/musician/saas.html deleted file mode 100644 index 4da034f..0000000 --- a/idhub/templates/templates/musician/saas.html +++ /dev/null @@ -1,56 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n %} - -{% block content %} - -

{{ service.verbose_name }}

-

{{ service.description }}

- -{% for saas in object_list %} -
-
-
-
- {{ saas.name }} -
- {% comment "Hidden until API provides this information" %} -
- {% trans "Installed on" %}: {{ saas.domain|default:"-" }} -
- {% endcomment %} -
-
-
-
-

{{ saas.service|capfirst }}

-

-
-
-

{% trans "Service info" %}

- {{ saas.is_active|yesno }}
- {% for key, value in saas.data.items %} - {{ value }}
- {% endfor %} -
- -
-
- {% empty %} -
-
-
-
-

- {# Translators: saas page when there isn't any saas. #} -
{% trans "Ooops! Looks like there is nothing here!" %}
-
-
-
-
-{% endfor %} - -{% endblock %} diff --git a/idhub/templates/templates/musician/service_list.html b/idhub/templates/templates/musician/service_list.html deleted file mode 100644 index d413fc8..0000000 --- a/idhub/templates/templates/musician/service_list.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends "musician/base.html" %} -{% load i18n musician %} - -{% block content %} - -

{{ service.verbose_name }}

-

{{ service.description }}

- - - - - {% for field_name in service.fields %} - - {% endfor %} - - - - {% for resource in object_list %} - - {% for field_name in service.fields %} - - {% endfor %} - - {% endfor %} - - {% include "musician/components/table_paginator.html" %} -
{{ field_name }}
{{ resource|get_item:field_name }}
-{% endblock %} diff --git a/idhub/urls.py b/idhub/urls.py index 648f155..d107b3f 100644 --- a/idhub/urls.py +++ b/idhub/urls.py @@ -17,7 +17,12 @@ Including another URLconf from django.contrib.auth import views as auth_views from django.views.generic import RedirectView from django.urls import path, reverse_lazy -from .views import LoginView, PasswordResetConfirmView, serve_did +from .views import ( + LoginView, + PasswordResetConfirmView, + serve_did, + DobleFactorSendView, +) from .admin import views as views_admin from .user import views as views_user # from .verification_portal import views as views_verification_portal @@ -95,6 +100,8 @@ urlpatterns = [ path('user/credentials_presentation/demand', views_user.DemandAuthorizationView.as_view(), name='user_demand_authorization'), + path('user/terms/', views_user.TermsAndConditionsView.as_view(), + name='user_terms_and_conditions'), # Admin path('admin/dashboard/', views_admin.DashboardView.as_view(), @@ -177,8 +184,13 @@ urlpatterns = [ name='admin_schemas_import_add'), path('admin/import', views_admin.ImportView.as_view(), name='admin_import'), + path('admin/terms/', views_admin.TermsAndConditionsView.as_view(), + name='admin_terms_and_conditions'), path('admin/import/new', views_admin.ImportAddView.as_view(), name='admin_import_add'), + path('admin/auth/', views_admin.DobleFactorAuthView.as_view(), + name='admin_2fauth'), + path('admin/auth/2f/', DobleFactorSendView.as_view(), name='confirm_send_2f'), path('did-registry//did.json', serve_did) diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 58c8ff5..0277034 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -16,6 +16,33 @@ class ProfileForm(forms.ModelForm): fields = ('first_name', 'last_name', 'email') +class TermsConditionsForm(forms.Form): + accept = forms.BooleanField( + label=_("Accept terms and conditions of the service"), + required=False + ) + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) + super().__init__(*args, **kwargs) + + def clean(self): + data = self.cleaned_data + if data.get("accept"): + self.user.accept_gdpr = True + else: + self.user.accept_gdpr = False + return data + + def save(self, commit=True): + + if commit: + self.user.save() + return self.user + + return + + class RequestCredentialForm(forms.Form): did = forms.ChoiceField(label=_("Did"), choices=[]) credential = forms.ChoiceField(label=_("Credential"), choices=[]) diff --git a/idhub/user/views.py b/idhub/user/views.py index aa0b2a7..3f69faf 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -31,7 +31,8 @@ from django.conf import settings from idhub.user.forms import ( ProfileForm, RequestCredentialForm, - DemandAuthorizationForm + DemandAuthorizationForm, + TermsConditionsForm ) from utils import certs from idhub.mixins import UserView @@ -105,6 +106,26 @@ class CredentialsView(MyWallet, TemplateView): }) return context + +class TermsAndConditionsView(UserView, FormView): + template_name = "idhub/user/terms_conditions.html" + title = _("GDPR") + section = "" + subtitle = _('Accept 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 + kwargs['initial'] = {"accept": self.request.user.accept_gdpr} + return kwargs + + def form_valid(self, form): + user = form.save() + return super().form_valid(form) + class CredentialView(MyWallet, TemplateView): template_name = "idhub/user/credential.html" diff --git a/idhub/verification_portal/__init__.py b/idhub/verification_portal/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/idhub/verification_portal/models.py b/idhub/verification_portal/models.py deleted file mode 100644 index 0bd203a..0000000 --- a/idhub/verification_portal/models.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.db import models - - -class VPVerifyRequest(models.Model): - """ - `nonce` is an opaque random string used to lookup verification requests. URL-safe. - Example: "UPBQ3JE2DGJYHP5CPSCRIGTHRTCYXMQPNQ" - `expected_credentials` is a JSON list of credential types that must be present in this VP. - Example: ["FinancialSituationCredential", "HomeConnectivitySurveyCredential"] - `expected_contents` is a JSON object that places optional constraints on the contents of the - returned VP. - Example: [{"FinancialSituationCredential": {"financial_vulnerability_score": "7"}}] - `action` is (for now) a JSON object describing the next steps to take if this verification - is successful. For example "send mail to with and " - Example: {"action": "send_mail", "params": {"to": "orders@somconnexio.coop", "subject": "New client", "body": ...} - `response` is a URL that the user's wallet will redirect the user to. - `submitted_on` is used (by a cronjob) to purge old entries that didn't complete verification - """ - nonce = models.CharField(max_length=50) - expected_credentials = models.CharField(max_length=255) - expected_contents = models.TextField() - action = models.TextField() - response_or_redirect = models.CharField(max_length=255) - submitted_on = models.DateTimeField(auto_now=True) diff --git a/idhub/verification_portal/views.py b/idhub/verification_portal/views.py deleted file mode 100644 index 486f4f7..0000000 --- a/idhub/verification_portal/views.py +++ /dev/null @@ -1,49 +0,0 @@ -import json - -from django.core.mail import send_mail -from django.http import HttpResponse, HttpResponseRedirect - -from utils.idhub_ssikit import verify_presentation -from .models import VPVerifyRequest -from django.shortcuts import get_object_or_404 -from more_itertools import flatten, unique_everseen - - -def verify(request): - assert request.method == "POST" - # TODO: incorporate request.POST["presentation_submission"] as schema definition - (presentation_valid, _) = verify_presentation(request.POST["vp_token"]) - if not presentation_valid: - raise Exception("Failed to verify signature on the given Verifiable Presentation.") - vp = json.loads(request.POST["vp_token"]) - nonce = vp["nonce"] - # "vr" = verification_request - vr = get_object_or_404(VPVerifyRequest, nonce=nonce) # TODO: return meaningful error, not 404 - # Get a list of all included verifiable credential types - included_credential_types = unique_everseen(flatten([ - vc["type"] for vc in vp["verifiableCredential"] - ])) - # Check that it matches what we requested - for requested_vc_type in json.loads(vr.expected_credentials): - if requested_vc_type not in included_credential_types: - raise Exception("You're missing some credentials we requested!") # TODO: return meaningful error - # Perform whatever action we have to do - action = json.loads(vr.action) - if action["action"] == "send_mail": - subject = action["params"]["subject"] - to_email = action["params"]["to"] - from_email = "noreply@verifier-portal" - body = request.POST["vp-token"] - send_mail( - subject, - body, - from_email, - [to_email] - ) - elif action["action"] == "something-else": - pass - else: - raise Exception("Unknown action!") - # OK! Your verifiable presentation was successfully presented. - return HttpResponseRedirect(vr.response_or_redirect) - diff --git a/idhub/views.py b/idhub/views.py index f513353..7a525b1 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -1,13 +1,18 @@ -from django.shortcuts import get_object_or_404 -from django.urls import reverse_lazy +import uuid + from django.conf import settings from django.core.cache import cache -from django.utils.translation import gettext_lazy as _ +from django.urls import reverse_lazy +from django.views.generic.base import TemplateView from django.contrib.auth import views as auth_views from django.contrib.auth import login as auth_login -from django.http import HttpResponseRedirect, HttpResponse +from django.utils.translation import gettext_lazy as _ +from django.shortcuts import get_object_or_404, redirect +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpResponseRedirect, HttpResponse, Http404 from idhub.models import DID +from idhub.email.views import NotifyActivateUserByEmail from trustchain_idhub import settings @@ -41,6 +46,9 @@ class LoginView(auth_views.LoginView): # ) # cache.set("KEY_DIDS", encryption_key, None) cache.set("KEY_DIDS", sensitive_data_encryption_key, None) + if not settings.DEVELOPMENT: + self.request.session["2fauth"] = str(uuid.uuid4()) + return redirect(reverse_lazy('idhub:confirm_send_2f')) self.request.session["key_did"] = user.encrypt_data( sensitive_data_encryption_key, @@ -69,3 +77,23 @@ def serve_did(request, did_id): retval = HttpResponse(document) retval.headers["Content-Type"] = "application/json" return retval + + +class DobleFactorSendView(LoginRequiredMixin, NotifyActivateUserByEmail, TemplateView): + template_name = 'auth/2fadmin.html' + subject_template_name = 'auth/2fadmin_email_subject.txt' + email_template_name = 'auth/2fadmin_email.txt' + html_email_template_name = 'auth/2fadmin_email.html' + + def get(self, request, *args, **kwargs): + if not request.user.is_admin: + raise Http404 + + f2auth = self.request.session.get("2fauth") + if not f2auth: + raise Http404 + + self.send_email(self.request.user, token=f2auth) + return super().get(request, *args, **kwargs) + + diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py deleted file mode 100644 index 840bc0b..0000000 --- a/idhub_auth/migrations/0001_initial.py +++ /dev/null @@ -1,58 +0,0 @@ -# Generated by Django 4.2.5 on 2024-01-18 11:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - initial = True - - dependencies = [] - - operations = [ - migrations.CreateModel( - name='User', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('password', models.CharField(max_length=128, verbose_name='password')), - ( - 'last_login', - models.DateTimeField( - blank=True, null=True, verbose_name='last login' - ), - ), - ( - 'email', - models.EmailField( - max_length=255, unique=True, verbose_name='Email address' - ), - ), - ('is_active', models.BooleanField(default=True)), - ('is_admin', models.BooleanField(default=False)), - ( - 'first_name', - models.CharField( - blank=True, max_length=255, null=True, verbose_name='First name' - ), - ), - ( - 'last_name', - models.CharField( - blank=True, max_length=255, null=True, verbose_name='Last name' - ), - ), - ('encrypted_sensitive_data', models.CharField(max_length=255)), - ('salt', models.CharField(max_length=255)), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/idhub_auth/models.py b/idhub_auth/models.py index aa3e5b7..e89bc35 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -51,6 +51,7 @@ class User(AbstractBaseUser): last_name = models.CharField(_("Last name"), max_length=255, blank=True, null=True) encrypted_sensitive_data = models.CharField(max_length=255) salt = models.CharField(max_length=255) + accept_gdpr = models.BooleanField(default=False) objects = UserManager() diff --git a/oidc4vp/migrations/0001_initial.py b/oidc4vp/migrations/0001_initial.py deleted file mode 100644 index b29aed0..0000000 --- a/oidc4vp/migrations/0001_initial.py +++ /dev/null @@ -1,137 +0,0 @@ -# Generated by Django 4.2.5 on 2024-01-18 11:32 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import oidc4vp.models - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Authorization', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'code', - models.CharField(default=oidc4vp.models.set_code, max_length=24), - ), - ('code_used', models.BooleanField(default=False)), - ('created', models.DateTimeField(auto_now=True)), - ('presentation_definition', models.CharField(max_length=250)), - ], - ), - migrations.CreateModel( - name='Organization', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('name', models.CharField(max_length=250)), - ( - 'client_id', - models.CharField( - default=oidc4vp.models.set_client_id, max_length=24, unique=True - ), - ), - ( - 'client_secret', - models.CharField( - default=oidc4vp.models.set_client_secret, max_length=48 - ), - ), - ('my_client_id', models.CharField(max_length=24)), - ('my_client_secret', models.CharField(max_length=48)), - ( - 'response_uri', - models.URLField( - help_text='Url where to send the verificable presentation', - max_length=250, - ), - ), - ], - ), - migrations.CreateModel( - name='OAuth2VPToken', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('created', models.DateTimeField(auto_now=True)), - ('result_verify', models.CharField(max_length=255)), - ('vp_token', models.TextField()), - ( - 'authorization', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name='vp_tokens', - to='oidc4vp.authorization', - ), - ), - ( - 'organization', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='vp_tokens', - to='oidc4vp.organization', - ), - ), - ( - 'user', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='vp_tokens', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - migrations.AddField( - model_name='authorization', - name='organization', - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='authorizations', - to='oidc4vp.organization', - ), - ), - migrations.AddField( - model_name='authorization', - name='user', - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ] diff --git a/promotion/migrations/0001_initial.py b/promotion/migrations/0001_initial.py deleted file mode 100644 index cabebc3..0000000 --- a/promotion/migrations/0001_initial.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 4.2.5 on 2024-01-18 11:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - ('oidc4vp', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Promotion', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('name', models.CharField(max_length=250)), - ( - 'discount', - models.PositiveSmallIntegerField( - choices=[(1, 'Financial vulnerability')] - ), - ), - ( - 'authorize', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='promotions', - to='oidc4vp.authorization', - ), - ), - ], - ), - ] diff --git a/requirements.txt b/requirements.txt index c5f9def..c7103f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,4 +26,4 @@ uharfbuzz==0.38.0 fontTools==4.47.0 weasyprint==60.2 ujson==5.9.0 -didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl +./didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl diff --git a/utils/idhub_ssikit/__init__.py b/utils/idhub_ssikit/__init__.py index 3521eba..85e6e2f 100644 --- a/utils/idhub_ssikit/__init__.py +++ b/utils/idhub_ssikit/__init__.py @@ -2,6 +2,7 @@ import asyncio import datetime import didkit import json +import urllib import jinja2 from django.template.backends.django import Template from django.template.loader import get_template @@ -29,7 +30,8 @@ def webdid_from_controller_key(key): keydid = keydid_from_controller_key(key) # "did:key:<...>" pubkeyid = keydid.rsplit(":")[-1] # <...> document = json.loads(asyncio.run(resolve_keydid(keydid))) # Documento DID en terminos "key" - webdid_url = f"did:web:{settings.DOMAIN}:did-registry:{pubkeyid}" # nueva URL: "did:web:idhub.pangea.org:<...>" + domain = urllib.parse.urlencode({"domain": settings.DOMAIN})[7:] + webdid_url = f"did:web:{domain}:did-registry:{pubkeyid}" # nueva URL: "did:web:idhub.pangea.org:<...>" webdid_url_owner = webdid_url + "#owner" # Reemplazamos los campos del documento DID necesarios: document["id"] = webdid_url