From c0dba1c42305a4bb4ae66068d9d2a5738b689beb Mon Sep 17 00:00:00 2001 From: Daniel Armengod Date: Mon, 15 Jan 2024 10:34:42 +0100 Subject: [PATCH 1/5] Infraestructura para usar didweb --- idhub/admin/views.py | 2 +- idhub/models.py | 12 +++++++++--- idhub/urls.py | 4 +++- idhub/user/views.py | 2 +- idhub/views.py | 13 ++++++++++++- utils/idhub_ssikit/__init__.py | 26 ++++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index b6dcbc8..690c319 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -645,7 +645,7 @@ class DidRegisterView(Credentials, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did() + form.instance.set_did(form.instance.type) form.save() messages.success(self.request, _('DID created successfully')) Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance) diff --git a/idhub/models.py b/idhub/models.py index b81f32c..eb17650 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from utils.idhub_ssikit import ( generate_did_controller_key, keydid_from_controller_key, - sign_credential, + sign_credential, webdid_from_controller_key, ) from idhub_auth.models import User @@ -416,6 +416,7 @@ class DID(models.Model): related_name='dids', null=True, ) + didweb_document = models.TextField() @property def is_organization_did(self): @@ -423,9 +424,14 @@ class DID(models.Model): return True return False - def set_did(self): + def set_did(self, type): self.key_material = generate_did_controller_key() - self.did = keydid_from_controller_key(self.key_material) + if type == "key": + self.did = keydid_from_controller_key(self.key_material) + elif type == "web": + didurl, document = webdid_from_controller_key(self.key_material) + self.did = didurl + self.didweb_document = document def get_key(self): return json.loads(self.key_material) diff --git a/idhub/urls.py b/idhub/urls.py index d139c32..8b92d93 100644 --- a/idhub/urls.py +++ b/idhub/urls.py @@ -17,7 +17,7 @@ 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 +from .views import LoginView, serve_did from .admin import views as views_admin from .user import views as views_user # from .verification_portal import views as views_verification_portal @@ -173,6 +173,8 @@ urlpatterns = [ path('admin/import/new', views_admin.ImportAddView.as_view(), name='admin_import_add'), + path('did-registry/', serve_did) + # path('verification_portal/verify/', views_verification_portal.verify, # name="verification_portal_verify") ] diff --git a/idhub/user/views.py b/idhub/user/views.py index e6e28dc..50c241b 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -205,7 +205,7 @@ class DidRegisterView(MyWallet, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did() + form.instance.set_did(form.instance.type) form.save() messages.success(self.request, _('DID created successfully')) diff --git a/idhub/views.py b/idhub/views.py index 5f6fb71..b4fe08f 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -1,8 +1,12 @@ +from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.contrib.auth import views as auth_views from django.contrib.auth import login as auth_login -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponse + +from idhub.models import DID +from trustchain_idhub import settings class LoginView(auth_views.LoginView): @@ -26,3 +30,10 @@ class LoginView(auth_views.LoginView): self.extra_context['success_url'] = admin_dashboard auth_login(self.request, user) return HttpResponseRedirect(self.extra_context['success_url']) + + +def serve_did(request, did_id): + document = get_object_or_404(DID, did=f'did:web:{settings.DOMAIN}:{did_id}').didweb_document + retval = HttpResponse(document) + retval.headers["Content-Type"] = "application/json" + return retval diff --git a/utils/idhub_ssikit/__init__.py b/utils/idhub_ssikit/__init__.py index cc3e9b4..84b27fd 100644 --- a/utils/idhub_ssikit/__init__.py +++ b/utils/idhub_ssikit/__init__.py @@ -6,6 +6,8 @@ import jinja2 from django.template.backends.django import Template from django.template.loader import get_template +from trustchain_idhub import settings + def generate_did_controller_key(): return didkit.generate_ed25519_key() @@ -15,6 +17,30 @@ def keydid_from_controller_key(key): return didkit.key_to_did("key", key) +async def resolve_keydid(keydid): + return await didkit.resolve_did(keydid, "{}") + + +def webdid_from_controller_key(key): + """ + Se siguen los pasos para generar un webdid a partir de un keydid. + Documentado en la docu de spruceid. + """ + 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}:{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 + document["verificationMethod"]["id"] = webdid_url_owner + document["verificationMethod"]["controller"] = webdid_url + document["authentication"] = webdid_url_owner + document["assertionMethod"] = webdid_url_owner + document_fixed_serialized = json.dumps(document) + return webdid_url, document_fixed_serialized + + def generate_generic_vc_id(): # TODO agree on a system for Verifiable Credential IDs return "https://pangea.org/credentials/42" From 47cf19f1296c21a4069ad606edf4fed8f76e44e4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Jan 2024 14:00:05 +0100 Subject: [PATCH 2/5] add new field type in model DID --- idhub/admin/views.py | 4 ++-- idhub/models.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index 690c319..5d8d15c 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -639,13 +639,13 @@ class DidRegisterView(Credentials, CreateView): icon = 'bi bi-patch-check-fill' wallet = True model = DID - fields = ('label',) + fields = ('label', 'type') success_url = reverse_lazy('idhub:admin_dids') object = None def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did(form.instance.type) + form.instance.set_did() form.save() messages.success(self.request, _('DID created successfully')) Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance) diff --git a/idhub/models.py b/idhub/models.py index eb17650..141a335 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -403,6 +403,13 @@ class Event(models.Model): class DID(models.Model): + class Types(models.IntegerChoices): + KEY = 1, "Key" + WEB = 2, "Web" + type = models.PositiveSmallIntegerField( + _("Type"), + choices=Types.choices, + ) created_at = models.DateTimeField(auto_now=True) label = models.CharField(_("Label"), max_length=50) did = models.CharField(max_length=250) @@ -424,11 +431,11 @@ class DID(models.Model): return True return False - def set_did(self, type): + def set_did(self): self.key_material = generate_did_controller_key() - if type == "key": + if self.type == self.Types.KEY: self.did = keydid_from_controller_key(self.key_material) - elif type == "web": + elif self.type == self.Types.WEB: didurl, document = webdid_from_controller_key(self.key_material) self.did = didurl self.didweb_document = document From 72a895a6de7ef397d0dcca9696aa8340a5a75dbd Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Jan 2024 14:00:28 +0100 Subject: [PATCH 3/5] rebuild migrations --- idhub/migrations/0001_initial.py | 22 +++++++++++++++++----- idhub_auth/migrations/0001_initial.py | 18 ++++++++++++++---- oidc4vp/migrations/0001_initial.py | 6 +++--- promotion/migrations/0001_initial.py | 2 +- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index 6d87ae7..833ae4a 100644 --- a/idhub/migrations/0001_initial.py +++ b/idhub/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-12-11 08:35 +# Generated by Django 4.2.5 on 2024-01-16 11:57 from django.conf import settings from django.db import migrations, models @@ -25,10 +25,17 @@ class Migration(migrations.Migration): verbose_name='ID', ), ), + ( + 'type', + models.PositiveSmallIntegerField( + choices=[(1, 'Web'), (2, 'Key')], verbose_name='Type' + ), + ), ('created_at', models.DateTimeField(auto_now=True)), - ('label', models.CharField(max_length=50)), + ('label', models.CharField(max_length=50, verbose_name='Label')), ('did', models.CharField(max_length=250)), ('key_material', models.CharField(max_length=250)), + ('didweb_document', models.TextField()), ( 'user', models.ForeignKey( @@ -256,8 +263,11 @@ class Migration(migrations.Migration): verbose_name='ID', ), ), - ('created', models.DateTimeField(auto_now=True)), - ('message', models.CharField(max_length=350)), + ('created', models.DateTimeField(auto_now=True, verbose_name='Date')), + ( + 'message', + models.CharField(max_length=350, verbose_name='Description'), + ), ( 'type', models.PositiveSmallIntegerField( @@ -295,7 +305,8 @@ class Migration(migrations.Migration): (28, 'Organisational DID deleted by admin'), (29, 'User deactivated'), (30, 'User activated'), - ] + ], + verbose_name='Event', ), ), ( @@ -327,6 +338,7 @@ class Migration(migrations.Migration): on_delete=django.db.models.deletion.CASCADE, related_name='users', to='idhub.service', + verbose_name='Service', ), ), ( diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index f460a62..9462c05 100644 --- a/idhub_auth/migrations/0001_initial.py +++ b/idhub_auth/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-12-11 08:35 +# Generated by Django 4.2.5 on 2024-01-16 11:57 from django.db import migrations, models @@ -31,13 +31,23 @@ class Migration(migrations.Migration): ( 'email', models.EmailField( - max_length=255, unique=True, verbose_name='email address' + 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)), - ('last_name', models.CharField(blank=True, max_length=255, null=True)), + ( + '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' + ), + ), ], options={ 'abstract': False, diff --git a/oidc4vp/migrations/0001_initial.py b/oidc4vp/migrations/0001_initial.py index 700c4e8..f57138b 100644 --- a/oidc4vp/migrations/0001_initial.py +++ b/oidc4vp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-12-11 08:35 +# Generated by Django 4.2.5 on 2024-01-16 11:57 from django.conf import settings from django.db import migrations, models @@ -30,7 +30,7 @@ class Migration(migrations.Migration): 'code', models.CharField(default=oidc4vp.models.set_code, max_length=24), ), - ('code_used', models.BooleanField()), + ('code_used', models.BooleanField(default=False)), ('created', models.DateTimeField(auto_now=True)), ('presentation_definition', models.CharField(max_length=250)), ], @@ -91,7 +91,7 @@ class Migration(migrations.Migration): models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, - related_name='oauth2vptoken', + related_name='vp_tokens', to='oidc4vp.authorization', ), ), diff --git a/promotion/migrations/0001_initial.py b/promotion/migrations/0001_initial.py index cbc1f17..f299fd8 100644 --- a/promotion/migrations/0001_initial.py +++ b/promotion/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-12-11 08:35 +# Generated by Django 4.2.5 on 2024-01-16 11:57 from django.db import migrations, models import django.db.models.deletion From 01bc21d1a4f5ae1c67c1a07962aae0b51b86914d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Jan 2024 14:00:59 +0100 Subject: [PATCH 4/5] add field type in form --- idhub/user/views.py | 4 ++-- idhub/views.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/idhub/user/views.py b/idhub/user/views.py index 50c241b..d715e0f 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -199,13 +199,13 @@ class DidRegisterView(MyWallet, CreateView): icon = 'bi bi-patch-check-fill' wallet = True model = DID - fields = ('label',) + fields = ('label', 'type') success_url = reverse_lazy('idhub:user_dids') object = None def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did(form.instance.type) + form.instance.set_did() form.save() messages.success(self.request, _('DID created successfully')) diff --git a/idhub/views.py b/idhub/views.py index b4fe08f..851011f 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -33,7 +33,9 @@ class LoginView(auth_views.LoginView): def serve_did(request, did_id): - document = get_object_or_404(DID, did=f'did:web:{settings.DOMAIN}:{did_id}').didweb_document + id_did = f'did:web:{settings.DOMAIN}:did-registry:{did_id}' + did = get_object_or_404(DID, did=id_did) + document = did.didweb_document retval = HttpResponse(document) retval.headers["Content-Type"] = "application/json" return retval From 1453ddf4519a3f8047e25192d57d66bdf9e46c31 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Jan 2024 14:01:15 +0100 Subject: [PATCH 5/5] fix some things --- idhub/management/commands/initial_datas.py | 2 +- utils/idhub_ssikit/__init__.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index 034bc68..641b085 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -64,7 +64,7 @@ class Command(BaseCommand): def create_defaults_dids(self): for u in User.objects.all(): - did = DID(label="Default", user=u) + did = DID(label="Default", user=u, type=DID.Types.KEY) did.set_did() did.save() diff --git a/utils/idhub_ssikit/__init__.py b/utils/idhub_ssikit/__init__.py index 84b27fd..3521eba 100644 --- a/utils/idhub_ssikit/__init__.py +++ b/utils/idhub_ssikit/__init__.py @@ -29,14 +29,14 @@ 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}:{pubkeyid}" # nueva URL: "did:web:idhub.pangea.org:<...>" + webdid_url = f"did:web:{settings.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 - document["verificationMethod"]["id"] = webdid_url_owner - document["verificationMethod"]["controller"] = webdid_url - document["authentication"] = webdid_url_owner - document["assertionMethod"] = webdid_url_owner + document["verificationMethod"][0]["id"] = webdid_url_owner + document["verificationMethod"][0]["controller"] = webdid_url + document["authentication"][0] = webdid_url_owner + document["assertionMethod"][0] = webdid_url_owner document_fixed_serialized = json.dumps(document) return webdid_url, document_fixed_serialized