From 37dc8335a72d48e1f550f0f11f524875d61904c6 Mon Sep 17 00:00:00 2001 From: Daniel Armengod Date: Fri, 1 Dec 2023 06:39:26 +0100 Subject: [PATCH 01/33] Changed model definitions and added logic to decrypt and set user key in session storage --- idhub/models.py | 38 +++++++++++++++++++++++++++++++++++--- idhub/views.py | 4 ++++ idhub_auth/models.py | 18 ++++++++++++++++++ requirements.txt | 1 + 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 53f8186..fbf00ef 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -6,6 +6,8 @@ from django.db import models from django.conf import settings from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ +from nacl import secret + from utils.idhub_ssikit import ( generate_did_controller_key, keydid_from_controller_key, @@ -409,7 +411,9 @@ class DID(models.Model): # In JWK format. Must be stored as-is and passed whole to library functions. # Example key material: # '{"kty":"OKP","crv":"Ed25519","x":"oB2cPGFx5FX4dtS1Rtep8ac6B__61HAP_RtSzJdPxqs","d":"OJw80T1CtcqV0hUcZdcI-vYNBN1dlubrLaJa0_se_gU"}' - key_material = models.CharField(max_length=250) + # CHANGED: `key_material` to `_key_material`, datatype from CharField to BinaryField and the key is now stored encrypted. + key_material = None + _key_material = models.BinaryField(max_length=250) user = models.ForeignKey( User, on_delete=models.CASCADE, @@ -417,6 +421,18 @@ class DID(models.Model): null=True, ) + def get_key_material(self, session): + if "sensitive_data_encryption_key" not in session: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") + sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + return sb.decrypt(self._key_material) + + def set_key_material(self, value, session): + if "sensitive_data_encryption_key" not in session: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") + sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + self._key_material = sb.encrypt(value) + @property def is_organization_did(self): if not self.user: @@ -427,7 +443,8 @@ class DID(models.Model): self.key_material = generate_did_controller_key() self.did = keydid_from_controller_key(self.key_material) - def get_key(self): + # TODO: darmengo: esta funcion solo se llama desde un fichero que sube cosas a s3 (??) Preguntar a ver que hace. + def get_key_deprecated(self): return json.loads(self.key_material) @@ -464,7 +481,10 @@ class VerificableCredential(models.Model): created_on = models.DateTimeField(auto_now=True) issued_on = models.DateTimeField(null=True) subject_did = models.CharField(max_length=250) - data = models.TextField() + # CHANGED: `data` to `_data`, datatype from TextField to BinaryField and the rendered VC is now stored encrypted. + # TODO: verify that BinaryField can hold arbitrary amounts of data (max_length = ???) + data = None + _data = models.BinaryField() csv_data = models.TextField() status = models.PositiveSmallIntegerField( choices=Status.choices, @@ -486,6 +506,18 @@ class VerificableCredential(models.Model): related_name='vcredentials', ) + def get_data(self, session): + if "sensitive_data_encryption_key" not in session: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") + sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + return sb.decrypt(self._data) + + def set_data(self, value, session): + if "sensitive_data_encryption_key" not in session: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") + sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + self._data = sb.encrypt(value) + @property def get_schema(self): if not self.data: diff --git a/idhub/views.py b/idhub/views.py index 53db736..f8a62a7 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -25,4 +25,8 @@ class LoginView(auth_views.LoginView): if self.extra_context['success_url'] == user_dashboard: self.extra_context['success_url'] = admin_dashboard auth_login(self.request, user) + # Decrypt the user's sensitive data encryption key and store it in the session. + password = form.cleaned_data.get("password") # TODO: Is this right???????? + sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) + self.request.session["sensitive_data_encryption_key"] = sensitive_data_encryption_key return HttpResponseRedirect(self.extra_context['success_url']) diff --git a/idhub_auth/models.py b/idhub_auth/models.py index ccda94c..cf1ac69 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -1,5 +1,6 @@ from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser +from nacl import secret, pwhash class UserManager(BaseUserManager): @@ -43,6 +44,10 @@ class User(AbstractBaseUser): is_admin = models.BooleanField(default=False) first_name = models.CharField(max_length=255, blank=True, null=True) last_name = models.CharField(max_length=255, blank=True, null=True) + # TODO: Hay que generar una clave aleatoria para cada usuario cuando se le da de alta en el sistema. + encrypted_sensitive_data_encryption_key = models.BinaryField(max_length=255) + # TODO: Hay que generar un salt aleatorio para cada usuario cuando se le da de alta en el sistema. + salt_of_sensitive_data_encryption_key = models.BinaryField(max_length=255) objects = UserManager() @@ -85,3 +90,16 @@ class User(AbstractBaseUser): for r in s.service.rol.all(): roles.append(r.name) return ", ".join(set(roles)) + + def derive_key_from_password(self, password): + kdf = pwhash.argon2i.kdf # TODO: Move the KDF choice to SETTINGS.PY + ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY + mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY + salt = self.salt_of_sensitive_data_encryption_key + return kdf(secret.SecretBox.KEY_SIZE, password, salt, opslimit=ops, memlimit=mem) + + def decrypt_sensitive_data_encryption_key(self, password): + sb_key = self.derive_key_from_password(password) + sb = secret.SecretBox(sb_key) + return sb.decrypt(self.encrypted_sensitive_data_encryption_key) + diff --git a/requirements.txt b/requirements.txt index 140c49f..c09c9d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ didkit==0.3.2 jinja2==3.1.2 jsonref==1.1.0 pyld==2.0.3 +pynacl==1.5.0 From 20f40b43d049d325ccc33b97bef0da784ac7d380 Mon Sep 17 00:00:00 2001 From: Daniel Armengod Date: Fri, 1 Dec 2023 07:01:51 +0100 Subject: [PATCH 02/33] Refactored all uses of DID.key_material --- idhub/admin/views.py | 2 +- idhub/models.py | 17 ++++++++++++----- idhub/user/forms.py | 3 ++- idhub/user/views.py | 3 ++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index f8fd6d0..1725339 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(self.request.session) 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 fbf00ef..6a326b9 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -439,9 +439,16 @@ class DID(models.Model): return True return False - def set_did(self): - self.key_material = generate_did_controller_key() - self.did = keydid_from_controller_key(self.key_material) + def set_did(self, session): + """ + Generates a new DID Controller Key and derives a DID from it. + Because DID Controller Keys are stored encrypted using a User's Sensitive Data Encryption Key, + this function needs to be called in the context of a request. + """ + new_key_material = generate_did_controller_key() + self.did = keydid_from_controller_key(new_key_material) + self.set_key_material(new_key_material, session) + # TODO: darmengo: esta funcion solo se llama desde un fichero que sube cosas a s3 (??) Preguntar a ver que hace. def get_key_deprecated(self): @@ -546,7 +553,7 @@ class VerificableCredential(models.Model): data = json.loads(self.csv_data).items() return data - def issue(self, did): + def issue(self, did, session): if self.status == self.Status.ISSUED: return @@ -555,7 +562,7 @@ class VerificableCredential(models.Model): self.issued_on = datetime.datetime.now().astimezone(pytz.utc) self.data = sign_credential( self.render(), - self.issuer_did.key_material + self.issuer_did.get_key_material(session) ) def get_context(self): diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 53a1149..3735d64 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -18,6 +18,7 @@ class RequestCredentialForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) + self.session = kwargs.pop('session', None) super().__init__(*args, **kwargs) self.fields['did'].choices = [ (x.did, x.label) for x in DID.objects.filter(user=self.user) @@ -45,7 +46,7 @@ class RequestCredentialForm(forms.Form): did = did[0].did cred = cred[0] try: - cred.issue(did) + cred.issue(did, self.session) except Exception: return diff --git a/idhub/user/views.py b/idhub/user/views.py index 482b40e..d59f7d6 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -128,6 +128,7 @@ class CredentialsRequestView(MyWallet, FormView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user + kwargs['session'] = self.request.session return kwargs def form_valid(self, form): @@ -189,7 +190,7 @@ class DidRegisterView(MyWallet, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did() + form.instance.set_did(self.request.session) form.save() messages.success(self.request, _('DID created successfully')) From 45f30f36649ddb298f8dc03f771e8891223c8029 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 15 Dec 2023 17:52:08 +0100 Subject: [PATCH 03/33] new credential membership --- examples/membership-card.csv | 4 +- .../credentials/membership-card.json | 162 +++++++----------- schemas/membership-card.json | 151 +++++++++------- 3 files changed, 154 insertions(+), 163 deletions(-) diff --git a/examples/membership-card.csv b/examples/membership-card.csv index 1f4d08d..7447dcc 100644 --- a/examples/membership-card.csv +++ b/examples/membership-card.csv @@ -1,2 +1,2 @@ -name surnames email typeOfPerson membershipType organisation affiliatedSince -Pepe Gómez user1@example.org individual Member Pangea 01-01-2023 +firstName lastName email membershipType membershipId affiliatedUntil affiliatedSince typeOfPerson identityDocType identityNumber +Pepe Gómez user1@example.org individual 123456 2024-01-01T00:00:00Z 2023-01-01T00:00:00Z natural DNI 12345678A diff --git a/idhub/templates/credentials/membership-card.json b/idhub/templates/credentials/membership-card.json index bc315bb..e618833 100644 --- a/idhub/templates/credentials/membership-card.json +++ b/idhub/templates/credentials/membership-card.json @@ -1,105 +1,67 @@ { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - { - "individual": "https://schema.org/Person", - "Member": "https://schema.org/Member", - "startDate": "https://schema.org/startDate", - "jsonSchema": "https://schema.org/jsonSchema", - "$ref": "https://schema.org/jsonSchemaRef", - "credentialSchema": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#credentialSchema", - "organisation": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#organisation", - "membershipType": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#membershipType", - "membershipId": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#membershipId", - "typeOfPerson": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#typeOfPerson", - "identityDocType": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#identityDocType", - "identityNumber": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#identityNumber", - "name": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#name", - "description": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#description", - "value": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#value", - "lang": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#lang", - "surnames": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#surnames", - "email": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#email", - "affiliatedSince": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#affiliatedSince", - "affiliatedUntil": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/contexts/vocab#affiliatedUntil", - "issued": "https://ec.europa.eu/digital-building-blocks/wikis/display/EBSIDOC/Verifiable+Attestation#issued", - "validFrom": "https://ec.europa.eu/digital-building-blocks/wikis/display/EBSIDOC/Verifiable+Attestation#validFrom", - "validUntil": "https://ec.europa.eu/digital-building-blocks/wikis/display/EBSIDOC/Verifiable+Attestation#validUntil" - } - ], - "type": [ - "VerifiableCredential", - "VerifiableAttestation", - "MembershipCard" - ], - "id": "{{ vc_id }}", - "issuer": { - "id": "{{ issuer_did }}", - "name": "Pangea", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://idhub.pangea.org/credentials/base/v1", + "https://idhub.pangea.org/credentials/membership-card/v1" + ], + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "MembershipCard" + ], + "id": "https://idhub.pangea.org/credentials/987654321", + "issuer": { + "id": "did:example:5678", + "name": "Pangea Internet Solidari" + }, + "issuanceDate": "2023-12-06T19:23:24Z", + "issued": "2023-12-06T19:23:24Z", + "validFrom": "2023-12-06T19:23:24Z", + "validUntil": "2024-12-06T19:23:24Z", + "name": [ + { + "value": "Membership Card", + "lang": "en" + }, + { + "value": "Carnet de soci/a", + "lang": "ca_ES" + }, + { + "value": "Carnet de socio/a", + "lang": "es" + } + ], "description": [ - { - "value": "Pangea.org is a service provider leveraging open-source technologies to provide affordable and accessible solutions for social enterprises and solidarity organisations.", - "lang": "en" - }, - { - "value": "Pangea.org és un proveïdor de serveis que aprofita les tecnologies de codi obert per oferir solucions assequibles i accessibles per a empreses socials i organitzacions solidàries.", - "lang": "ca_ES" - }, - { - "value": "Pangea.org es un proveedor de servicios que aprovecha tecnologías de código abierto para proporcionar soluciones asequibles y accesibles para empresas sociales y organizaciones solidarias.", - "lang": "es" - } - - ] - }, - "issuanceDate": "{{ issuance_date }}", - "issued": "{{ issuance_date }}", - "validFrom": "{{ issuance_date }}", - "validUntil": "{{ validUntil }}", - "name": [ - { - "value": "Membership Card", - "lang": "en" + { + "value": "The membership card specifies an individual's subscription or enrollment in specific services or benefits issued by an organization.", + "lang": "en" + }, + { + "value": "El carnet de soci especifica la subscripció o la inscripció d'un individu en serveis o beneficis específics emesos per una organització.", + "lang": "ca_ES" + }, + { + "value": "El carnet de socio especifica la suscripción o inscripción de un individuo en servicios o beneficios específicos emitidos por uns organización.", + "lang": "es" + } + ], + "credentialSubject": { + "id": "did:example:1234", + "firstName": "Joan", + "lastName": "Pera", + "email": "joan.pera@pangea.org", + "typeOfPerson": "natural", + "identityDocType": "DNI", + "identityNumber": "12345678A", + "organisation": "Pangea", + "membershipType": "individual", + "membershipId": "123456", + "affiliatedSince": "2023-01-01T00:00:00Z", + "affiliatedUntil": "2024-01-01T00:00:00Z" }, - { - "value": "Carnet de soci/a", - "lang": "ca_ES" - }, - { - "value": "Carnet de socio/a", - "lang": "es" - } - ], - "description": [ - { - "value": "The membership card specifies an individual's subscription or enrollment in specific services or benefits issued by an organization.", - "lang": "en" - }, - { - "value": "El carnet de soci especifica la subscripció o la inscripció d'un individu en serveis o beneficis específics emesos per una organització.", - "lang": "ca_ES" - }, - { - "value": "El carnet de socio especifica la suscripción o inscripción de un individuo en servicios o beneficios específicos emitidos por uns organización.", - "lang": "es" - } - ], - "credentialSubject": { - "id": "{{ subject_did }}", - "organisation": "Pangea", - "membershipType": "{{ membershipType }}", - "membershipId": "{{ vc_id }}", - "affiliatedSince": "{{ affiliatedSince }}", - "affiliatedUntil": "{{ affiliatedUntil }}", - "typeOfPerson": "{{ typeOfPerson }}", - "identityDocType": "{{ identityDocType }}", - "identityNumber": "{{ identityNumber }}", - "name": "{{ first_name }}", - "surnames": "{{ last_name }}", - "email": "{{ email }}", "credentialSchema": { - "id": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/membership-card-schema.json", - "type": "JsonSchema" + "id": "https://idhub.pangea.org/vc_schemas/membership-card.json", + "type": "FullJsonSchemaValidator2021" } - } -} \ No newline at end of file +} diff --git a/schemas/membership-card.json b/schemas/membership-card.json index 0d5ff9a..20a5f9b 100644 --- a/schemas/membership-card.json +++ b/schemas/membership-card.json @@ -1,65 +1,94 @@ { - "$id": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/membership-card-schema.json", + "$id": "https://idhub.pangea.org/vc_schemas/membership-card.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "name": "MembershipCard", - "description": "MembershipCard credential using JsonSchema", - "type": "object", - "properties": { - "credentialSubject": { - "type": "object", - "properties": { - "organisation": { - "type": "string" - }, - "membershipType": { - "type": "string" - }, - "affiliatedSince": { - "type": "string", - "format": "date-time" - }, - "affiliatedUntil": { - "type": "string", - "format": "date-time" - }, - "typeOfPerson": { - "type": "string", - "enum": [ - "individual", - "org" - ] - }, - "identityDocType": { - "type": "string", - "enum": [ - "DNI", - "NIF", - "NIE", - "PASSPORT" - ] - }, - "identityNumber": { - "type": "string" - }, - "name": { - "type": "string" - }, - "surnames": { - "type": "string" - }, - "email": { - "type": "string", - "format": "email" - } - }, - "required": [ - "organisation", - "affiliatedSince", - "typeOfPerson", - "name", - "surnames", - "email" - ] + "title": "Membership Card", + "description": "The membership card specifies an individual's subscription or enrollment in specific services or benefits issued by an organization.", + "name": [ + { + "value": "Membership Card", + "lang": "en" + }, + { + "value": "Carnet de soci/a", + "lang": "ca_ES" + }, + { + "value": "Carnet de socio/a", + "lang": "es" } - } + ], + "type": "object", + "allOf": [ + { + "$ref": "https://idhub.pangea.org/vc_schemas/ebsi/attestation.json" + }, + { + "properties": { + "credentialSubject": { + "description": "Defines additional properties on credentialSubject", + "type": "object", + "properties": { + "id": { + "description": "Defines a unique identifier of the credential subject", + "type": "string" + }, + "organisation": { + "description": "Organisation the credential subject is affiliated with", + "type": "string" + }, + "membershipType": { + "description": "Type of membership", + "type": "string" + }, + "membershipId": { + "description": "Membership identifier", + "type": "string" + }, + "affiliatedSince": { + "type": "string", + "format": "date-time" + }, + "affiliatedUntil": { + "type": "string", + "format": "date-time" + }, + "typeOfPerson": { + "type": "string", + "enum": [ + "natural", + "legal" + ] + }, + "identityDocType": { + "description": "Type of the Identity Document of the credential subject", + "type": "string" + }, + "identityNumber": { + "description": "Number of the Identity Document of the credential subject", + "type": "string" + }, + "firstName": { + "description": "Name of the natural person or name of the legal person (organisation)", + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": [ + "id", + "organisation", + "affiliatedSince", + "typeOfPerson", + "firstName", + "email" + ] + } + } + } + ] } \ No newline at end of file From e551d2aadcc4ad1ba5245ff8c06f1c933e0ab192 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 15 Dec 2023 17:52:23 +0100 Subject: [PATCH 04/33] fix name --- idhub/models.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 7d1ef6c..8e3785e 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -443,9 +443,19 @@ class Schemas(models.Model): return {} return json.loads(self.data) - def name(self): - return self.get_schema.get('name', '') + def name(self, request=None): + names = {} + for name in self.get_schema.get('name', []): + lang = name.get('lang') + if 'ca' in lang: + lang = 'ca' + names[lang]= name.get('value') + if request and request.LANGUAGE_CODE in names.keys(): + return names[request.LANGUAGE_CODE] + + return names[settings.LANGUAGE_CODE] + def description(self): return self.get_schema.get('description', '') From 5d6580f73fa3af8274a23a7993dc5e86f5a55a9e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 18 Dec 2023 12:00:00 +0100 Subject: [PATCH 05/33] fix validate1 --- idhub/admin/forms.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index 2649f4b..69e8f3a 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -62,7 +62,8 @@ class ImportForm(forms.Form): self._schema = schema.first() try: self.json_schema = json.loads(self._schema.data) - prop = self.json_schema['properties'] + props = [x for x in self.json_schema["allOf"] if 'properties' in x] + prop = props[0]['properties'] self.properties = prop['credentialSubject']['properties'] except Exception: raise ValidationError("Schema is not valid!") @@ -110,8 +111,10 @@ class ImportForm(forms.Form): return def validate_jsonld(self, line, row): + import pdb; pdb.set_trace() try: - credtools.validate_json(row, self.json_schema) + check = credtools.validate_json(row, self.json_schema) + raise ValidationError("Not valid row") except Exception as e: msg = "line {}: {}".format(line+1, e) self.exception(msg) From bac9c7209a1ce2974b69caa902717b0797134d6c Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 19 Dec 2023 10:31:44 +0100 Subject: [PATCH 06/33] fix requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9b19238..a065a87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ python-decouple==3.8 jsonschema==4.19.1 pandas==2.1.1 requests==2.31.0 -didkit==0.3.2 +#didkit==0.3.2 jinja2==3.1.2 jsonref==1.1.0 pyld==2.0.3 From d2f7e5395d73d4365ea8f87668834d4f2561b941 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 3 Jan 2024 17:52:46 +0100 Subject: [PATCH 07/33] encription from a env key and password admin --- idhub/admin/views.py | 2 +- idhub/models.py | 40 ++++++++++++++++++------------------ idhub/user/forms.py | 3 +-- idhub/user/views.py | 3 +-- idhub/views.py | 20 ++++++++++++++---- trustchain_idhub/settings.py | 2 ++ 6 files changed, 41 insertions(+), 29 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index 1725339..f8fd6d0 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(self.request.session) + 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 6a326b9..6e6e18d 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -421,16 +421,16 @@ class DID(models.Model): null=True, ) - def get_key_material(self, session): - if "sensitive_data_encryption_key" not in session: - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") - sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + def get_key_material(self): + if not settings.KEY_CREDENTIALS_CLEAN: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") + sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) return sb.decrypt(self._key_material) - def set_key_material(self, value, session): - if "sensitive_data_encryption_key" not in session: - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") - sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + def set_key_material(self, value): + if not settings.KEY_CREDENTIALS_CLEAN: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") + sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) self._key_material = sb.encrypt(value) @property @@ -439,7 +439,7 @@ class DID(models.Model): return True return False - def set_did(self, session): + def set_did(self): """ Generates a new DID Controller Key and derives a DID from it. Because DID Controller Keys are stored encrypted using a User's Sensitive Data Encryption Key, @@ -447,7 +447,7 @@ class DID(models.Model): """ new_key_material = generate_did_controller_key() self.did = keydid_from_controller_key(new_key_material) - self.set_key_material(new_key_material, session) + self.set_key_material(new_key_material) # TODO: darmengo: esta funcion solo se llama desde un fichero que sube cosas a s3 (??) Preguntar a ver que hace. @@ -513,16 +513,16 @@ class VerificableCredential(models.Model): related_name='vcredentials', ) - def get_data(self, session): - if "sensitive_data_encryption_key" not in session: - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") - sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + def get_data(self): + if not settings.KEY_CREDENTIALS_CLEAN: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") + sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) return sb.decrypt(self._data) - def set_data(self, value, session): - if "sensitive_data_encryption_key" not in session: - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.") - sb = secret.SecretBox(session["sensitive_data_encryption_key"]) + def set_data(self, value): + if not settings.KEY_CREDENTIALS_CLEAN: + raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") + sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) self._data = sb.encrypt(value) @property @@ -553,7 +553,7 @@ class VerificableCredential(models.Model): data = json.loads(self.csv_data).items() return data - def issue(self, did, session): + def issue(self, did): if self.status == self.Status.ISSUED: return @@ -562,7 +562,7 @@ class VerificableCredential(models.Model): self.issued_on = datetime.datetime.now().astimezone(pytz.utc) self.data = sign_credential( self.render(), - self.issuer_did.get_key_material(session) + self.issuer_did.get_key_material() ) def get_context(self): diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 3735d64..53a1149 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -18,7 +18,6 @@ class RequestCredentialForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) - self.session = kwargs.pop('session', None) super().__init__(*args, **kwargs) self.fields['did'].choices = [ (x.did, x.label) for x in DID.objects.filter(user=self.user) @@ -46,7 +45,7 @@ class RequestCredentialForm(forms.Form): did = did[0].did cred = cred[0] try: - cred.issue(did, self.session) + cred.issue(did) except Exception: return diff --git a/idhub/user/views.py b/idhub/user/views.py index d59f7d6..482b40e 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -128,7 +128,6 @@ class CredentialsRequestView(MyWallet, FormView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user - kwargs['session'] = self.request.session return kwargs def form_valid(self, form): @@ -190,7 +189,7 @@ class DidRegisterView(MyWallet, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did(self.request.session) + form.instance.set_did() form.save() messages.success(self.request, _('DID created successfully')) diff --git a/idhub/views.py b/idhub/views.py index f8a62a7..e87e5d7 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -1,8 +1,10 @@ from django.urls import reverse_lazy +from django.conf import settings 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 nacl import secret class LoginView(auth_views.LoginView): @@ -24,9 +26,19 @@ class LoginView(auth_views.LoginView): admin_dashboard = reverse_lazy('idhub:admin_dashboard') if self.extra_context['success_url'] == user_dashboard: self.extra_context['success_url'] = admin_dashboard + password = form.cleaned_data.get("password") + # Decrypt the user's sensitive data encryption key and store it in the session. + self.decript_key(user, password) + auth_login(self.request, user) - # Decrypt the user's sensitive data encryption key and store it in the session. - password = form.cleaned_data.get("password") # TODO: Is this right???????? - sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) - self.request.session["sensitive_data_encryption_key"] = sensitive_data_encryption_key return HttpResponseRedirect(self.extra_context['success_url']) + + def decript_key(self, user, password): + if not settings.KEY_CREDENTIALS: + return + + sb_key = user.derive_key_from_password(password) + sb = secret.SecretBox(sb_key) + data_decript = sb.decrypt(settings.KEY_CREDENTIALS) + settings.KEY_CREDENTIALS_CLEAN = data_decript + diff --git a/trustchain_idhub/settings.py b/trustchain_idhub/settings.py index 61d9637..305c929 100644 --- a/trustchain_idhub/settings.py +++ b/trustchain_idhub/settings.py @@ -184,3 +184,5 @@ USE_I18N = True USE_L10N = True AUTH_USER_MODEL = 'idhub_auth.User' +KEY_CREDENTIALS = config("KEY_CREDENTIALS") +KEY_CREDENTIALS_CLEAN = "" From c671ac489f2f3b159e878bf0f7c6a620c1ada721 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 3 Jan 2024 19:53:11 +0100 Subject: [PATCH 08/33] change settings for cache --- idhub/models.py | 21 +++++++++++++-------- idhub/views.py | 21 ++++++++------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 6e6e18d..05b163a 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -4,6 +4,7 @@ import requests import datetime from django.db import models from django.conf import settings +from django.core.cache import cache from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ from nacl import secret @@ -422,15 +423,17 @@ class DID(models.Model): ) def get_key_material(self): - if not settings.KEY_CREDENTIALS_CLEAN: + key_dids = cache.get("KEY_DIDS", {}) + if not key_dids.get(user.id): raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) + sb = secret.SecretBox(key_dids[user.id]) return sb.decrypt(self._key_material) def set_key_material(self, value): - if not settings.KEY_CREDENTIALS_CLEAN: + key_dids = cache.get("KEY_DIDS", {}) + if not key_dids.get(user.id): raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) + sb = secret.SecretBox(key_dids[user.id]) self._key_material = sb.encrypt(value) @property @@ -514,15 +517,17 @@ class VerificableCredential(models.Model): ) def get_data(self): - if not settings.KEY_CREDENTIALS_CLEAN: + key_dids = cache.get("KEY_DIDS", {}) + if not key_dids.get(user.id): raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) + sb = secret.SecretBox(key_dids[user.id]) return sb.decrypt(self._data) def set_data(self, value): - if not settings.KEY_CREDENTIALS_CLEAN: + key_dids = cache.get("KEY_DIDS", {}) + if not key_dids.get(user.id): raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(settings.KEY_CREDENTIALS_CLEAN) + sb = secret.SecretBox(key_dids[user.id]) self._data = sb.encrypt(value) @property diff --git a/idhub/views.py b/idhub/views.py index e87e5d7..463479c 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -1,5 +1,5 @@ from django.urls import reverse_lazy -from django.conf import settings +from django.core.cache import cache 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 @@ -26,19 +26,14 @@ class LoginView(auth_views.LoginView): admin_dashboard = reverse_lazy('idhub:admin_dashboard') if self.extra_context['success_url'] == user_dashboard: self.extra_context['success_url'] = admin_dashboard - password = form.cleaned_data.get("password") - # Decrypt the user's sensitive data encryption key and store it in the session. - self.decript_key(user, password) auth_login(self.request, user) + # Decrypt the user's sensitive data encryption key and store it in the session. + password = form.cleaned_data.get("password") + sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) + key_dids = cache.get("KEY_DIDS", {}) + key_dids[user.id] = sensitive_data_encryption_key + cache.set("KEY_DIDS", key_dids) + return HttpResponseRedirect(self.extra_context['success_url']) - def decript_key(self, user, password): - if not settings.KEY_CREDENTIALS: - return - - sb_key = user.derive_key_from_password(password) - sb = secret.SecretBox(sb_key) - data_decript = sb.decrypt(settings.KEY_CREDENTIALS) - settings.KEY_CREDENTIALS_CLEAN = data_decript - From 4225421147de10d45c9841701d515dbb02964a91 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 3 Jan 2024 19:54:10 +0100 Subject: [PATCH 09/33] fix settings --- trustchain_idhub/settings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/trustchain_idhub/settings.py b/trustchain_idhub/settings.py index 305c929..61d9637 100644 --- a/trustchain_idhub/settings.py +++ b/trustchain_idhub/settings.py @@ -184,5 +184,3 @@ USE_I18N = True USE_L10N = True AUTH_USER_MODEL = 'idhub_auth.User' -KEY_CREDENTIALS = config("KEY_CREDENTIALS") -KEY_CREDENTIALS_CLEAN = "" From 3655291dc6849a1d995d02809a3975334e9307e6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 3 Jan 2024 19:55:04 +0100 Subject: [PATCH 10/33] fix timeout --- idhub/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idhub/views.py b/idhub/views.py index 463479c..6d51159 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -33,7 +33,7 @@ class LoginView(auth_views.LoginView): sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) key_dids = cache.get("KEY_DIDS", {}) key_dids[user.id] = sensitive_data_encryption_key - cache.set("KEY_DIDS", key_dids) + cache.set("KEY_DIDS", key_dids, None) return HttpResponseRedirect(self.extra_context['success_url']) From f62348dcdb305a9e92db3a9b600773160102dec9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 3 Jan 2024 20:14:04 +0100 Subject: [PATCH 11/33] fix perpetual key in cache --- idhub/views.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/idhub/views.py b/idhub/views.py index 6d51159..3db164f 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -21,19 +21,23 @@ class LoginView(auth_views.LoginView): def form_valid(self, form): user = form.get_user() + # Decrypt the user's sensitive data encryption key and store it in the session. + password = form.cleaned_data.get("password") + sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) + key_dids = cache.get("KEY_DIDS", {}) if not user.is_anonymous and user.is_admin: user_dashboard = reverse_lazy('idhub:user_dashboard') admin_dashboard = reverse_lazy('idhub:admin_dashboard') if self.extra_context['success_url'] == user_dashboard: self.extra_context['success_url'] = admin_dashboard + key_dids[user.id] = sensitive_data_encryption_key + cache.set("KEY_DIDS", key_dids, None) + else: + key_dids[user.id] = sensitive_data_encryption_key + cache.set("KEY_DIDS", key_dids) + auth_login(self.request, user) - # Decrypt the user's sensitive data encryption key and store it in the session. - password = form.cleaned_data.get("password") - sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) - key_dids = cache.get("KEY_DIDS", {}) - key_dids[user.id] = sensitive_data_encryption_key - cache.set("KEY_DIDS", key_dids, None) return HttpResponseRedirect(self.extra_context['success_url']) From bd84dbc3bb0e9772146e3b0cd79b5c29d74ee537 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 4 Jan 2024 12:43:24 +0100 Subject: [PATCH 12/33] encripted in reset password --- idhub/urls.py | 15 ++++++---- idhub/views.py | 14 +++++++-- idhub_auth/forms.py | 3 +- idhub_auth/models.py | 69 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/idhub/urls.py b/idhub/urls.py index 785f4d1..f7df70d 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, PasswordResetConfirmView from .admin import views as views_admin from .user import views as views_user @@ -44,13 +44,16 @@ urlpatterns = [ ), name='password_reset_done' ), - path('auth/reset///', - auth_views.PasswordResetConfirmView.as_view( - template_name='auth/password_reset_confirm.html', - success_url=reverse_lazy('idhub:password_reset_complete') - ), + path('auth/reset///', PasswordResetConfirmView.as_view(), name='password_reset_confirm' ), + # path('auth/reset///', + # auth_views.PasswordResetConfirmView.as_view( + # template_name='auth/password_reset_confirm.html', + # success_url=reverse_lazy('idhub:password_reset_complete') + # ), + # name='password_reset_confirm' + # ), path('auth/reset/done/', auth_views.PasswordResetCompleteView.as_view( template_name='auth/password_reset_complete.html' diff --git a/idhub/views.py b/idhub/views.py index 3db164f..8e7f542 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -4,7 +4,6 @@ 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 nacl import secret class LoginView(auth_views.LoginView): @@ -23,7 +22,7 @@ class LoginView(auth_views.LoginView): user = form.get_user() # Decrypt the user's sensitive data encryption key and store it in the session. password = form.cleaned_data.get("password") - sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password) + sensitive_data_encryption_key = user.decrypt_sensitive_data(password) key_dids = cache.get("KEY_DIDS", {}) if not user.is_anonymous and user.is_admin: user_dashboard = reverse_lazy('idhub:user_dashboard') @@ -41,3 +40,14 @@ class LoginView(auth_views.LoginView): return HttpResponseRedirect(self.extra_context['success_url']) + +class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): + template_name = 'auth/password_reset_confirm.html' + success_url = reverse_lazy('idhub:password_reset_complete') + + def form_valid(self, form): + password = form.cleaned_data.get("password") + user = form.get_user() + user.set_encrypted_sensitive_data(password) + user.save() + return HttpResponseRedirect(self.success_url) diff --git a/idhub_auth/forms.py b/idhub_auth/forms.py index f292182..c9ab1f6 100644 --- a/idhub_auth/forms.py +++ b/idhub_auth/forms.py @@ -2,7 +2,7 @@ import re from django import forms from django.utils.translation import gettext_lazy as _ -from idhub_auth.models import User +from idhub_auth.models import User, gen_salt class ProfileForm(forms.ModelForm): @@ -31,4 +31,3 @@ class ProfileForm(forms.ModelForm): return last_name - diff --git a/idhub_auth/models.py b/idhub_auth/models.py index cf1ac69..2fcf669 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -1,6 +1,9 @@ +import nacl +import base64 + from django.db import models +from django.core.cache import cache from django.contrib.auth.models import BaseUserManager, AbstractBaseUser -from nacl import secret, pwhash class UserManager(BaseUserManager): @@ -44,10 +47,8 @@ class User(AbstractBaseUser): is_admin = models.BooleanField(default=False) first_name = models.CharField(max_length=255, blank=True, null=True) last_name = models.CharField(max_length=255, blank=True, null=True) - # TODO: Hay que generar una clave aleatoria para cada usuario cuando se le da de alta en el sistema. - encrypted_sensitive_data_encryption_key = models.BinaryField(max_length=255) - # TODO: Hay que generar un salt aleatorio para cada usuario cuando se le da de alta en el sistema. - salt_of_sensitive_data_encryption_key = models.BinaryField(max_length=255) + encrypted_sensitive_data = models.CharField(max_length=255) + salt = models.CharField(max_length=255) objects = UserManager() @@ -92,14 +93,54 @@ class User(AbstractBaseUser): return ", ".join(set(roles)) def derive_key_from_password(self, password): - kdf = pwhash.argon2i.kdf # TODO: Move the KDF choice to SETTINGS.PY - ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY - mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY - salt = self.salt_of_sensitive_data_encryption_key - return kdf(secret.SecretBox.KEY_SIZE, password, salt, opslimit=ops, memlimit=mem) + kdf = nacl.pwhash.argon2i.kdf + ops = nacl.pwhash.argon2i.OPSLIMIT_INTERACTIVE + mem = nacl.pwhash.argon2i.MEMLIMIT_INTERACTIVE + return kdf( + nacl.secret.SecretBox.KEY_SIZE, + password, + self.get_salt(), + opslimit=ops, + memlimit=mem + ) + + def decrypt_sensitive_data(self, password, data=None): + sb_key = self.derive_key_from_password(password.encode('utf-8')) + sb = nacl.secret.SecretBox(sb_key) + if not data: + data = self.get_encrypted_sensitive_data() + if not isinstance(data, bytes): + data = data.encode('utf-8') + + return sb.decrypt(data).decode('utf-8') + + def encrypt_sensitive_data(self, password, data): + sb_key = self.derive_key_from_password(password.encode('utf-8')) + sb = nacl.secret.SecretBox(sb_key) + if not isinstance(data, bytes): + data = data.encode('utf-8') + + return sb.encrypt(data).decode('utf-8') + + def get_salt(self): + return base64.b64decode(self.salt.encode('utf-8')) + + def set_salt(self): + self.salt = base64.b64encode(nacl.utils.random(16)).decode('utf-8') + + def get_encrypted_sensitive_data(self): + return base64.b64decode(self.encrypted_sensitive_data.encode('utf-8')) + + def set_encrypted_sensitive_data(self, password): + key = base64.b64encode(nacl.utils.random(64)) + key_dids = cache.get("KEY_DIDS", {}) + + if key_dids.get(user.id): + key = key_dids[user.id] + else: + self.set_salt() + + key_crypted = self.encrypt_sensitive_data(password, key) + self.encrypted_sensitive_data = base64.b64encode(key_crypted).decode('utf-8') - def decrypt_sensitive_data_encryption_key(self, password): - sb_key = self.derive_key_from_password(password) - sb = secret.SecretBox(sb_key) - return sb.decrypt(self.encrypted_sensitive_data_encryption_key) From e910f3ceec6ec0771285d2e6e963b0e7f647a985 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 4 Jan 2024 16:27:27 +0100 Subject: [PATCH 13/33] make migrations and fix some things --- idhub/management/commands/initial_datas.py | 7 ++- idhub/migrations/0001_initial.py | 69 +++++++++++----------- idhub_auth/forms.py | 2 +- idhub_auth/migrations/0001_initial.py | 4 +- idhub_auth/models.py | 15 ++--- 5 files changed, 53 insertions(+), 44 deletions(-) diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index acdf6c7..fd644e9 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -31,12 +31,15 @@ class Command(BaseCommand): self.create_organizations(r[0].strip(), r[1].strip()) def create_admin_users(self, email, password): - User.objects.create_superuser(email=email, password=password) + su = User.objects.create_superuser(email=email, password=password) + su.set_encrypted_sensitive_data(password) + su.save() def create_users(self, email, password): - u= User.objects.create(email=email, password=password) + u = User.objects.create(email=email, password=password) u.set_password(password) + u.set_encrypted_sensitive_data(password) u.save() diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index b4d6ac7..5bd4f31 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-11-15 09:58 +# Generated by Django 4.2.5 on 2024-01-04 15:12 from django.conf import settings from django.db import migrations, models @@ -28,7 +28,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now=True)), ('label', models.CharField(max_length=50)), ('did', models.CharField(max_length=250)), - ('key_material', models.CharField(max_length=250)), + ('_key_material', models.BinaryField(max_length=250)), ( 'user', models.ForeignKey( @@ -169,7 +169,7 @@ class Migration(migrations.Migration): ('created_on', models.DateTimeField(auto_now=True)), ('issued_on', models.DateTimeField(null=True)), ('subject_did', models.CharField(max_length=250)), - ('data', models.TextField()), + ('_data', models.BinaryField()), ('csv_data', models.TextField()), ( 'status', @@ -274,36 +274,39 @@ class Migration(migrations.Migration): 'type', models.PositiveSmallIntegerField( choices=[ - (1, 'EV_USR_REGISTERED'), - (2, 'EV_USR_WELCOME'), - (3, 'EV_DATA_UPDATE_REQUESTED_BY_USER'), - (4, 'EV_DATA_UPDATE_REQUESTED'), - (5, 'EV_USR_UPDATED_BY_ADMIN'), - (6, 'EV_USR_UPDATED'), - (7, 'EV_USR_DELETED_BY_ADMIN'), - (8, 'EV_DID_CREATED_BY_USER'), - (9, 'EV_DID_CREATED'), - (10, 'EV_DID_DELETED'), - (11, 'EV_CREDENTIAL_DELETED_BY_ADMIN'), - (12, 'EV_CREDENTIAL_DELETED'), - (13, 'EV_CREDENTIAL_ISSUED_FOR_USER'), - (14, 'EV_CREDENTIAL_ISSUED'), - (15, 'EV_CREDENTIAL_PRESENTED_BY_USER'), - (16, 'EV_CREDENTIAL_PRESENTED'), - (17, 'EV_CREDENTIAL_ENABLED'), - (18, 'EV_CREDENTIAL_CAN_BE_REQUESTED'), - (19, 'EV_CREDENTIAL_REVOKED_BY_ADMIN'), - (20, 'EV_CREDENTIAL_REVOKED'), - (21, 'EV_ROLE_CREATED_BY_ADMIN'), - (22, 'EV_ROLE_MODIFIED_BY_ADMIN'), - (23, 'EV_ROLE_DELETED_BY_ADMIN'), - (24, 'EV_SERVICE_CREATED_BY_ADMIN'), - (25, 'EV_SERVICE_MODIFIED_BY_ADMIN'), - (26, 'EV_SERVICE_DELETED_BY_ADMIN'), - (27, 'EV_ORG_DID_CREATED_BY_ADMIN'), - (28, 'EV_ORG_DID_DELETED_BY_ADMIN'), - (29, 'EV_USR_DEACTIVATED_BY_ADMIN'), - (30, 'EV_USR_ACTIVATED_BY_ADMIN'), + (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'), ] ), ), diff --git a/idhub_auth/forms.py b/idhub_auth/forms.py index c9ab1f6..fa771d4 100644 --- a/idhub_auth/forms.py +++ b/idhub_auth/forms.py @@ -2,7 +2,7 @@ import re from django import forms from django.utils.translation import gettext_lazy as _ -from idhub_auth.models import User, gen_salt +from idhub_auth.models import User class ProfileForm(forms.ModelForm): diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index d40f0a4..5655a6f 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-11-15 09:58 +# Generated by Django 4.2.5 on 2024-01-04 15:12 from django.db import migrations, models @@ -38,6 +38,8 @@ class Migration(migrations.Migration): ('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)), + ('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 2fcf669..7c3434d 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -1,6 +1,7 @@ import nacl import base64 +from nacl import pwhash from django.db import models from django.core.cache import cache from django.contrib.auth.models import BaseUserManager, AbstractBaseUser @@ -93,9 +94,9 @@ class User(AbstractBaseUser): return ", ".join(set(roles)) def derive_key_from_password(self, password): - kdf = nacl.pwhash.argon2i.kdf - ops = nacl.pwhash.argon2i.OPSLIMIT_INTERACTIVE - mem = nacl.pwhash.argon2i.MEMLIMIT_INTERACTIVE + kdf = pwhash.argon2i.kdf + ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE + mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE return kdf( nacl.secret.SecretBox.KEY_SIZE, password, @@ -120,7 +121,7 @@ class User(AbstractBaseUser): if not isinstance(data, bytes): data = data.encode('utf-8') - return sb.encrypt(data).decode('utf-8') + return base64.b64encode(sb.encrypt(data)).decode('utf-8') def get_salt(self): return base64.b64decode(self.salt.encode('utf-8')) @@ -135,12 +136,12 @@ class User(AbstractBaseUser): key = base64.b64encode(nacl.utils.random(64)) key_dids = cache.get("KEY_DIDS", {}) - if key_dids.get(user.id): - key = key_dids[user.id] + if key_dids.get(self.id): + key = key_dids[self.id] else: self.set_salt() key_crypted = self.encrypt_sensitive_data(password, key) - self.encrypted_sensitive_data = base64.b64encode(key_crypted).decode('utf-8') + self.encrypted_sensitive_data = key_crypted From 10c6d20a10397f9f51a8adf17044de11c2991875 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 4 Jan 2024 19:17:18 +0100 Subject: [PATCH 14/33] fix command initial datas --- idhub/management/commands/initial_datas.py | 8 ++++++++ idhub/migrations/0001_initial.py | 4 ++-- idhub/models.py | 22 +++++++++------------- idhub_auth/migrations/0001_initial.py | 2 +- idhub_auth/models.py | 9 ++++++--- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index 62c048a..8481a81 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -7,6 +7,7 @@ from utils import credtools from django.conf import settings from django.core.management.base import BaseCommand, CommandError from django.contrib.auth import get_user_model +from django.core.cache import cache from decouple import config from idhub.models import DID, Schemas from oidc4vp.models import Organization @@ -43,6 +44,9 @@ class Command(BaseCommand): su = User.objects.create_superuser(email=email, password=password) su.set_encrypted_sensitive_data(password) su.save() + key = su.decrypt_sensitive_data(password) + key_dids = {su.id: key} + cache.set("KEY_DIDS", key_dids, None) def create_users(self, email, password): @@ -50,6 +54,10 @@ class Command(BaseCommand): u.set_password(password) u.set_encrypted_sensitive_data(password) u.save() + key_dids = cache.get("KEY_DIDS", {}) + key = u.decrypt_sensitive_data(password) + key_dids.update({u.id: key}) + cache.set("KEY_DIDS", key_dids) def create_organizations(self, name, url): diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index 751af85..05fdbf4 100644 --- a/idhub/migrations/0001_initial.py +++ b/idhub/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2024-01-04 16:59 +# Generated by Django 4.2.5 on 2024-01-04 18:09 from django.conf import settings from django.db import migrations, models @@ -28,7 +28,7 @@ class Migration(migrations.Migration): ('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.BinaryField(max_length=250)), + ('key_material', models.CharField(max_length=255)), ( 'user', models.ForeignKey( diff --git a/idhub/models.py b/idhub/models.py index 30ad551..116f46c 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -412,9 +412,7 @@ class DID(models.Model): # In JWK format. Must be stored as-is and passed whole to library functions. # Example key material: # '{"kty":"OKP","crv":"Ed25519","x":"oB2cPGFx5FX4dtS1Rtep8ac6B__61HAP_RtSzJdPxqs","d":"OJw80T1CtcqV0hUcZdcI-vYNBN1dlubrLaJa0_se_gU"}' - # CHANGED: `key_material` to `_key_material`, datatype from CharField to BinaryField and the key is now stored encrypted. - key_material = None - _key_material = models.BinaryField(max_length=250) + key_material = models.CharField(max_length=255) user = models.ForeignKey( User, on_delete=models.CASCADE, @@ -423,18 +421,16 @@ class DID(models.Model): ) def get_key_material(self): - key_dids = cache.get("KEY_DIDS", {}) - if not key_dids.get(user.id): - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(key_dids[user.id]) - return sb.decrypt(self._key_material) + return self.user.decrypt_data(self.key_material) def set_key_material(self, value): - key_dids = cache.get("KEY_DIDS", {}) - if not key_dids.get(user.id): - raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave.") - sb = secret.SecretBox(key_dids[user.id]) - self._key_material = sb.encrypt(value) + self.key_material = self.user.encrypt_data(value) + + def get_data(self): + return self.user.decrypt_data(self.data) + + def set_data(self, value): + self.data = self.user.encrypt_data(value) @property def is_organization_did(self): diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index 8ea6578..ee16760 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 2024-01-04 16:59 +# Generated by Django 4.2.5 on 2024-01-04 18:09 from django.db import migrations, models diff --git a/idhub_auth/models.py b/idhub_auth/models.py index aed2199..86da431 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -148,12 +148,13 @@ class User(AbstractBaseUser): def encrypt_data(self, data): sb = self.get_secret_box() value = base64.b64encode(data.encode('utf-8')) - return sb.encrypt(data) + value_enc = sb.encrypt(data.encode('utf-8')) + return base64.b64encode(value_enc).decode('utf-8') def decrypt_data(self, data): sb = self.get_secret_box() value = base64.b64decode(data.encode('utf-8')) - return sb.decrypt(data) + return sb.decrypt(value).decode('utf-8') def get_secret_box(self): key_dids = cache.get("KEY_DIDS", {}) @@ -162,4 +163,6 @@ class User(AbstractBaseUser): err += "data without having the key." raise Exception(_(err)) - return secret.SecretBox(key_dids[self.id]) + pw = base64.b64decode(key_dids[self.id].encode('utf-8')) + sb_key = self.derive_key_from_password(pw) + return nacl.secret.SecretBox(sb_key) From 5dc1577d9ee3d7e681429ae774fe3bf4d0ce5fb4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 4 Jan 2024 21:11:11 +0100 Subject: [PATCH 15/33] encripted y download credential --- idhub/models.py | 7 ++++--- idhub/user/views.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 116f46c..c0e237a 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -3,7 +3,6 @@ import pytz import datetime from django.db import models from django.conf import settings -from django.core.cache import cache from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ from nacl import secret @@ -541,13 +540,15 @@ class VerificableCredential(models.Model): if self.status == self.Status.ISSUED: return - self.status = self.Status.ISSUED + # self.status = self.Status.ISSUED + import pdb; pdb.set_trace() self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) - self.data = sign_credential( + data = sign_credential( self.render(), self.issuer_did.get_key_material() ) + self.data = self.user.encrypt_data(data) def get_context(self): d = json.loads(self.csv_data) diff --git a/idhub/user/views.py b/idhub/user/views.py index e6e28dc..0a273ec 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -120,7 +120,7 @@ class CredentialJsonView(MyWallet, TemplateView): pk=pk, user=self.request.user ) - response = HttpResponse(self.object.data, content_type="application/json") + response = HttpResponse(self.object.get_data(), content_type="application/json") response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json") return response From cb9ef0b608c1067a89b0c5a70342f5b74adbbecf Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Sat, 6 Jan 2024 19:18:59 +0100 Subject: [PATCH 16/33] fix issue dids and credentials --- idhub/admin/views.py | 4 +- idhub/management/commands/initial_datas.py | 15 +++----- idhub/models.py | 44 +++++++++------------- idhub/user/forms.py | 4 +- idhub/user/views.py | 25 +++++++++++- idhub/views.py | 16 ++++---- idhub_auth/models.py | 18 +++------ trustchain_idhub/settings.py | 1 + 8 files changed, 68 insertions(+), 59 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index b6dcbc8..634ec0b 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -17,6 +17,7 @@ from django.views.generic.edit import ( UpdateView, ) from django.shortcuts import get_object_or_404, redirect +from django.core.cache import cache from django.urls import reverse_lazy from django.http import HttpResponse from django.contrib import messages @@ -645,13 +646,14 @@ class DidRegisterView(Credentials, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did() + form.instance.set_did(cache.get("KEY_DIDS")) form.save() messages.success(self.request, _('DID created successfully')) Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance) return super().form_valid(form) + class DidEditView(Credentials, UpdateView): template_name = "idhub/admin/did_register.html" subtitle = _('Organization Identities (DID)') diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index 8481a81..0d8e0a2 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -37,7 +37,6 @@ class Command(BaseCommand): self.create_organizations(r[0].strip(), r[1].strip()) self.sync_credentials_organizations("pangea.org", "somconnexio.coop") self.sync_credentials_organizations("local 8000", "local 9000") - self.create_defaults_dids() self.create_schemas() def create_admin_users(self, email, password): @@ -47,6 +46,7 @@ class Command(BaseCommand): key = su.decrypt_sensitive_data(password) key_dids = {su.id: key} cache.set("KEY_DIDS", key_dids, None) + self.create_defaults_dids(su, key) def create_users(self, email, password): @@ -54,10 +54,8 @@ class Command(BaseCommand): u.set_password(password) u.set_encrypted_sensitive_data(password) u.save() - key_dids = cache.get("KEY_DIDS", {}) key = u.decrypt_sensitive_data(password) - key_dids.update({u.id: key}) - cache.set("KEY_DIDS", key_dids) + self.create_defaults_dids(u, key) def create_organizations(self, name, url): @@ -73,11 +71,10 @@ class Command(BaseCommand): org1.save() org2.save() - def create_defaults_dids(self): - for u in User.objects.all(): - did = DID(label="Default", user=u) - did.set_did() - did.save() + def create_defaults_dids(self, u, password): + did = DID(label="Default", user=u) + did.set_did(password) + did.save() def create_schemas(self): schemas_files = os.listdir(settings.SCHEMAS_DIR) diff --git a/idhub/models.py b/idhub/models.py index c0e237a..dee615f 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -3,6 +3,7 @@ import pytz import datetime from django.db import models from django.conf import settings +from django.core.cache import cache from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ from nacl import secret @@ -419,25 +420,19 @@ class DID(models.Model): null=True, ) - def get_key_material(self): - return self.user.decrypt_data(self.key_material) + def get_key_material(self, password): + return self.user.decrypt_data(self.key_material, password) - def set_key_material(self, value): - self.key_material = self.user.encrypt_data(value) + def set_key_material(self, value, password): + self.key_material = self.user.encrypt_data(value, password) - def get_data(self): - return self.user.decrypt_data(self.data) - - def set_data(self, value): - self.data = self.user.encrypt_data(value) - @property def is_organization_did(self): if not self.user: return True return False - def set_did(self): + def set_did(self, password): """ Generates a new DID Controller Key and derives a DID from it. Because DID Controller Keys are stored encrypted using a User's Sensitive Data Encryption Key, @@ -445,12 +440,7 @@ class DID(models.Model): """ new_key_material = generate_did_controller_key() self.did = keydid_from_controller_key(new_key_material) - self.set_key_material(new_key_material) - - - # TODO: darmengo: esta funcion solo se llama desde un fichero que sube cosas a s3 (??) Preguntar a ver que hace. - def get_key_deprecated(self): - return json.loads(self.key_material) + self.set_key_material(new_key_material, password) class Schemas(models.Model): @@ -514,11 +504,13 @@ class VerificableCredential(models.Model): related_name='vcredentials', ) - def get_data(self): - return self.user.decrypt_data(self.data) + def get_data(self, password): + if not self.data: + return "" + return self.user.decrypt_data(self.data, password) - def set_data(self, value): - self.data = self.user.encrypt_data(value) + def set_data(self, value, password): + self.data = self.user.encrypt_data(value, password) def type(self): return self.schema.type @@ -536,19 +528,19 @@ class VerificableCredential(models.Model): data = json.loads(self.csv_data).items() return data - def issue(self, did): + def issue(self, did, password): if self.status == self.Status.ISSUED: return - # self.status = self.Status.ISSUED - import pdb; pdb.set_trace() + self.status = self.Status.ISSUED self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) + issuer_pass = cache.get("KEY_DIDS") data = sign_credential( self.render(), - self.issuer_did.get_key_material() + self.issuer_did.get_key_material(issuer_pass) ) - self.data = self.user.encrypt_data(data) + self.data = self.user.encrypt_data(data, password) def get_context(self): d = json.loads(self.csv_data) diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 5ac04ad..efe9ad4 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -22,6 +22,7 @@ class RequestCredentialForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) + self.password = kwargs.pop('password', None) super().__init__(*args, **kwargs) self.fields['did'].choices = [ (x.did, x.label) for x in DID.objects.filter(user=self.user) @@ -49,7 +50,8 @@ class RequestCredentialForm(forms.Form): did = did[0] cred = cred[0] try: - cred.issue(did) + if self.password: + cred.issue(did, self.password) except Exception: return diff --git a/idhub/user/views.py b/idhub/user/views.py index 0a273ec..dc83377 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -120,7 +120,15 @@ class CredentialJsonView(MyWallet, TemplateView): pk=pk, user=self.request.user ) - response = HttpResponse(self.object.get_data(), content_type="application/json") + pass_enc = self.request.session.get("key_did") + data = "" + if pass_enc: + user_pass = self.request.user.decrypt_data( + pass_enc, + self.request.user.password+self.request.session._session_key + ) + data = self.object.get_data(user_pass) + response = HttpResponse(data, content_type="application/json") response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json") return response @@ -135,6 +143,15 @@ class CredentialsRequestView(MyWallet, FormView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user + pass_enc = self.request.session.get("key_did") + if pass_enc: + user_pass = self.request.user.decrypt_data( + pass_enc, + self.request.user.password+self.request.session._session_key + ) + else: + pass_enc = None + kwargs['password'] = user_pass return kwargs def form_valid(self, form): @@ -205,7 +222,11 @@ class DidRegisterView(MyWallet, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.set_did() + pw = self.request.user.decrypt_data( + self.request.session.get("key_did"), + self.request.user.password+self.request.session._session_key + ) + form.instance.set_did(pw) form.save() messages.success(self.request, _('DID created successfully')) diff --git a/idhub/views.py b/idhub/views.py index 8e7f542..176449b 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -20,23 +20,23 @@ class LoginView(auth_views.LoginView): def form_valid(self, form): user = form.get_user() - # Decrypt the user's sensitive data encryption key and store it in the session. password = form.cleaned_data.get("password") + auth_login(self.request, user) + sensitive_data_encryption_key = user.decrypt_sensitive_data(password) - key_dids = cache.get("KEY_DIDS", {}) + if not user.is_anonymous and user.is_admin: user_dashboard = reverse_lazy('idhub:user_dashboard') admin_dashboard = reverse_lazy('idhub:admin_dashboard') if self.extra_context['success_url'] == user_dashboard: self.extra_context['success_url'] = admin_dashboard - key_dids[user.id] = sensitive_data_encryption_key - cache.set("KEY_DIDS", key_dids, None) - else: - key_dids[user.id] = sensitive_data_encryption_key - cache.set("KEY_DIDS", key_dids) + cache.set("KEY_DIDS", sensitive_data_encryption_key, None) - auth_login(self.request, user) + self.request.session["key_did"] = user.encrypt_data( + sensitive_data_encryption_key, + user.password+self.request.session._session_key + ) return HttpResponseRedirect(self.extra_context['success_url']) diff --git a/idhub_auth/models.py b/idhub_auth/models.py index 86da431..189b421 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -145,24 +145,18 @@ class User(AbstractBaseUser): key_crypted = self.encrypt_sensitive_data(password, key) self.encrypted_sensitive_data = key_crypted - def encrypt_data(self, data): - sb = self.get_secret_box() + def encrypt_data(self, data, password): + sb = self.get_secret_box(password) value = base64.b64encode(data.encode('utf-8')) value_enc = sb.encrypt(data.encode('utf-8')) return base64.b64encode(value_enc).decode('utf-8') - def decrypt_data(self, data): - sb = self.get_secret_box() + def decrypt_data(self, data, password): + sb = self.get_secret_box(password) value = base64.b64decode(data.encode('utf-8')) return sb.decrypt(value).decode('utf-8') - def get_secret_box(self): - key_dids = cache.get("KEY_DIDS", {}) - if not key_dids.get(self.id): - err = "An attempt is made to access encrypted " - err += "data without having the key." - raise Exception(_(err)) - - pw = base64.b64decode(key_dids[self.id].encode('utf-8')) + def get_secret_box(self, password): + pw = base64.b64decode(password.encode('utf-8')) sb_key = self.derive_key_from_password(pw) return nacl.secret.SecretBox(sb_key) diff --git a/trustchain_idhub/settings.py b/trustchain_idhub/settings.py index b9eecc5..01567c6 100644 --- a/trustchain_idhub/settings.py +++ b/trustchain_idhub/settings.py @@ -149,6 +149,7 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # Internationalization # https://docs.djangoproject.com/en/4.2/topics/i18n/ From f54a6f9729b6f94c2cee6883cfac2565de90dc97 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 12 Jan 2024 14:27:19 +0100 Subject: [PATCH 17/33] . --- idhub/models.py | 12 ++++++++++++ requirements.txt | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/idhub/models.py b/idhub/models.py index 8e3785e..dd57062 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -563,6 +563,18 @@ class VerificableCredential(models.Model): return '' + def filter_dict(self, dic): + new_dict = {} + for key, value in dic.items(): + if isinstance(value, dict): + new_value = self.filter_dict(value) + if new_value: + new_dict[key] = new_value + elif value: + new_dict[key] = value + return new_dict + + class VCTemplate(models.Model): wkit_template_id = models.CharField(max_length=250) data = models.TextField() diff --git a/requirements.txt b/requirements.txt index a065a87..4aa9c3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,10 +6,13 @@ black==23.9.1 python-decouple==3.8 jsonschema==4.19.1 pandas==2.1.1 +xlrd==2.0.1 +odfpy==1.4.1 requests==2.31.0 -#didkit==0.3.2 +didkit==0.3.2 jinja2==3.1.2 jsonref==1.1.0 pyld==2.0.3 more-itertools==10.1.0 dj-database-url==2.1.0 +ujson==5.9.0 From 16ea7631dc18030fad3d3c104ba8f4bd1a71373c Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 12 Jan 2024 20:09:19 +0100 Subject: [PATCH 18/33] filter_dict and new template membership --- idhub/models.py | 10 +- .../credentials/membership-card.json | 126 +++++++++--------- 2 files changed, 71 insertions(+), 65 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index dd57062..b862dc1 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -1,6 +1,8 @@ import json +import ujson import pytz import datetime +from collections import OrderedDict from django.db import models from django.conf import settings from django.template.loader import get_template @@ -525,10 +527,13 @@ class VerificableCredential(models.Model): self.status = self.Status.ISSUED self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) - self.data = sign_credential( + data = sign_credential( self.render(), self.issuer_did.key_material ) + d_ordered = ujson.loads(data) + d_minimum = self.filter_dict(d_ordered) + self.data = ujson.dumps(d_minimum) def get_context(self): d = json.loads(self.csv_data) @@ -544,6 +549,7 @@ class VerificableCredential(models.Model): 'issuance_date': issuance_date, 'first_name': self.user.first_name, 'last_name': self.user.last_name, + 'email': self.user.email } context.update(d) return context @@ -564,7 +570,7 @@ class VerificableCredential(models.Model): return '' def filter_dict(self, dic): - new_dict = {} + new_dict = OrderedDict() for key, value in dic.items(): if isinstance(value, dict): new_value = self.filter_dict(value) diff --git a/idhub/templates/credentials/membership-card.json b/idhub/templates/credentials/membership-card.json index e618833..e3d1c15 100644 --- a/idhub/templates/credentials/membership-card.json +++ b/idhub/templates/credentials/membership-card.json @@ -1,67 +1,67 @@ { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://idhub.pangea.org/credentials/base/v1", - "https://idhub.pangea.org/credentials/membership-card/v1" - ], - "type": [ - "VerifiableCredential", - "VerifiableAttestation", - "MembershipCard" - ], - "id": "https://idhub.pangea.org/credentials/987654321", - "issuer": { - "id": "did:example:5678", - "name": "Pangea Internet Solidari" + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://idhub.pangea.org/credentials/base/v1", + "https://idhub.pangea.org/credentials/membership-card/v1" + ], + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "MembershipCard" + ], + "id": "[[PLACEHOLDER]]", + "issuer": { + "id": "[[PLACEHOLDER]]", + "name": "[[PLACEHOLDER]]" + }, + "issuanceDate": "[[PLACEHOLDER]]", + "issued": "[[PLACEHOLDER]]", + "validFrom": "[[PLACEHOLDER]]", + "validUntil": "[[PLACEHOLDER]]", + "name": [ + { + "value": "Membership Card", + "lang": "en" }, - "issuanceDate": "2023-12-06T19:23:24Z", - "issued": "2023-12-06T19:23:24Z", - "validFrom": "2023-12-06T19:23:24Z", - "validUntil": "2024-12-06T19:23:24Z", - "name": [ - { - "value": "Membership Card", - "lang": "en" - }, - { - "value": "Carnet de soci/a", - "lang": "ca_ES" - }, - { - "value": "Carnet de socio/a", - "lang": "es" - } - ], - "description": [ - { - "value": "The membership card specifies an individual's subscription or enrollment in specific services or benefits issued by an organization.", - "lang": "en" - }, - { - "value": "El carnet de soci especifica la subscripció o la inscripció d'un individu en serveis o beneficis específics emesos per una organització.", - "lang": "ca_ES" - }, - { - "value": "El carnet de socio especifica la suscripción o inscripción de un individuo en servicios o beneficios específicos emitidos por uns organización.", - "lang": "es" - } - ], - "credentialSubject": { - "id": "did:example:1234", - "firstName": "Joan", - "lastName": "Pera", - "email": "joan.pera@pangea.org", - "typeOfPerson": "natural", - "identityDocType": "DNI", - "identityNumber": "12345678A", - "organisation": "Pangea", - "membershipType": "individual", - "membershipId": "123456", - "affiliatedSince": "2023-01-01T00:00:00Z", - "affiliatedUntil": "2024-01-01T00:00:00Z" + { + "value": "Carnet de soci/a", + "lang": "ca_ES" }, - "credentialSchema": { - "id": "https://idhub.pangea.org/vc_schemas/membership-card.json", - "type": "FullJsonSchemaValidator2021" + { + "value": "Carnet de socio/a", + "lang": "es" } -} + ], + "description": [ + { + "value": "The membership card specifies an individual's subscription or enrollment in specific services or benefits issued by an organization.", + "lang": "en" + }, + { + "value": "El carnet de soci especifica la subscripció o la inscripció d'un individu en serveis o beneficis específics emesos per una organització.", + "lang": "ca_ES" + }, + { + "value": "El carnet de socio especifica la suscripción o inscripción de un individuo en servicios o beneficios específicos emitidos por uns organización.", + "lang": "es" + } + ], + "credentialSubject": { + "id": "[[PLACEHOLDER]]", + "firstName": "[[PLACEHOLDER]]", + "lastName": "[[PLACEHOLDER]]", + "email": "[[PLACEHOLDER]]", + "typeOfPerson": "[[PLACEHOLDER]]", + "identityDocType": "[[PLACEHOLDER]]", + "identityNumber": "[[PLACEHOLDER]]", + "organisation": "[[PLACEHOLDER]]", + "membershipType": "[[PLACEHOLDER]]", + "membershipId": "[[PLACEHOLDER]]", + "affiliatedSince": "[[PLACEHOLDER]]", + "affiliatedUntil": "[[PLACEHOLDER]]" + }, + "credentialSchema": { + "id": "https://idhub.pangea.org/vc_schemas/membership-card.json", + "type": "FullJsonSchemaValidator2021" + } +} \ No newline at end of file From 336a2148f20f30644e90a9cadaab73d3a4710568 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 12 Jan 2024 21:32:26 +0100 Subject: [PATCH 19/33] fix of schema --- idhub/admin/forms.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index 719696d..c54929b 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -1,5 +1,6 @@ import csv import json +import copy import pandas as pd from django import forms @@ -62,7 +63,7 @@ class ImportForm(forms.Form): self._schema = schema.first() try: self.json_schema = json.loads(self._schema.data) - props = [x for x in self.json_schema["allOf"] if 'properties' in x] + props = [x for x in self.json_schema["allOf"] if 'properties' in x.keys()] prop = props[0]['properties'] self.properties = prop['credentialSubject']['properties'] except Exception: @@ -71,7 +72,10 @@ class ImportForm(forms.Form): if not self.properties: raise ValidationError("Schema is not valid!") - + # TODO we need filter "$ref" of schema for can validate a csv + self.json_schema_filtered = copy.copy(self.json_schema) + allOf = [x for x in self.json_schema["allOf"] if '$ref' not in x.keys()] + self.json_schema_filtered["allOf"] = allOf return data def clean_file_import(self): @@ -112,10 +116,10 @@ class ImportForm(forms.Form): return def validate_jsonld(self, line, row): - import pdb; pdb.set_trace() try: - check = credtools.validate_json(row, self.json_schema) - raise ValidationError("Not valid row") + check = credtools.validate_json(row, self.json_schema_filtered) + if check is not True: + raise ValidationError("Not valid row") except Exception as e: msg = "line {}: {}".format(line+1, e) self.exception(msg) From 93c8b9812eb1cb2601529af18ae85949013a6c7c Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 12 Jan 2024 21:38:23 +0100 Subject: [PATCH 20/33] fix excels --- examples/membership-card.ods | Bin 11886 -> 11038 bytes examples/membership-card.xls | Bin 6144 -> 6144 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/membership-card.ods b/examples/membership-card.ods index adf3cfc39e1983a28dd3b589a12319017e1092c6..b1fe1aca9064055079330662d028d29b275fa6bf 100644 GIT binary patch delta 8965 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 delta 9860 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 HzkB}|R_M)` diff --git a/examples/membership-card.xls b/examples/membership-card.xls index 857fb283e9381444eb17fa2b393c6ddf98697ba6..bd105384bd0edb4ac47284168c35b794f5641275 100644 GIT binary patch delta 1000 zcmZuvO-vI(6#iyg`cMDdVxkv+5mZQ9KuIrxVj^OS33!pyNVl+NyRF+@Lpe|&8iN{R zoU;cF7d;ud7~;_wjS1mwV#3*j@!~-et#8^DBf8zU?|t9A_vV{7T}&^gU)=Kb90G8d z>qr%9ZGZyD%!9Lh66xA zRKjOt?ReWp2}EBgU*{R|A#Bg~vJ^4Wf!K3rsFyU!S7-IgXn`8=Vo^7A zZfk`J-JI6`Vy;kY!wXz=xJiySIWICN|zE@jx|8Ba05KYGs+mX9T zKS)XExz6Dfq6o`SY_w;R#pd(%Og92(#Uc?M7-TD9I+1)78jW<#FV!KS#DB-~#sC&; zauS5am*|Z;;75!M_(1}wf)HJbi-Lscq%H~(k~uO{c8E!BE@Fh}9$ci*QQCKqht{OB zR-KoS)Z0Xt=syyJ6xmwz5u)+CNJ>i*T2SPMQkt^jch{t}Jv*6Fnkv>~R@4i+Aj8Q7zq$*o1CC9`Y@9ST2 Cgx7)q delta 794 zcmaKpPj3=I7{;GjmM#CvpF%apXs}fjDh4nnCK{#b!B}HUltYad$1>DS*aa6*OQJyS z$y0T{L605`ma9jkm2Wau+?9R;do9CT(-<|1XI+@K}bD7-50en>QO}RV$YIz=UAhDE~E1Fbtd__Es->F*MKEG0REMA#QWOIqd zS)|l4{Xa}z3ub3truVnf``@l7;lW7DZtzkY%%I<*_8b8)dOF?-YtYb(OxOh%-UTMC z)7ch76L!OmW&eZ?82I#0{1m{p$38T5H%ZVZR3i%>EqtKaP_WQ&O~I7>8hm_Tsn9U? zH=UF!mm+9wL>iIzkX%G`xMNQz@*k3$h%R^R8AQ`E6?TGbSdGd{N|SHH$C*!d^;EwT zS!4@W*3`FNd|CR;G6M d*_>^&)A9$6XJqq8+kK8QG-Nk=OMcWp`~`sQsz(3- From c0dba1c42305a4bb4ae66068d9d2a5738b689beb Mon Sep 17 00:00:00 2001 From: Daniel Armengod Date: Mon, 15 Jan 2024 10:34:42 +0100 Subject: [PATCH 21/33] 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 6b247811895d8c09464c5bad73d0b51aff6f0761 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 15 Jan 2024 19:10:07 +0100 Subject: [PATCH 22/33] add ORGANIZATION as env var --- trustchain_idhub/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trustchain_idhub/settings.py b/trustchain_idhub/settings.py index b9eecc5..6091fe0 100644 --- a/trustchain_idhub/settings.py +++ b/trustchain_idhub/settings.py @@ -222,3 +222,4 @@ LOGGING = { } } +ORGANIZATION = config('ORGANIZATION', 'Pangea') From 225c2a371207238c33e91b343b87d58ee80825fe Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 15 Jan 2024 19:10:39 +0100 Subject: [PATCH 23/33] get didkit as file in path --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4aa9c3e..13da29d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,10 +9,10 @@ pandas==2.1.1 xlrd==2.0.1 odfpy==1.4.1 requests==2.31.0 -didkit==0.3.2 jinja2==3.1.2 jsonref==1.1.0 pyld==2.0.3 more-itertools==10.1.0 dj-database-url==2.1.0 ujson==5.9.0 +didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl From 01d9d0d1899462e4a280df78b06b0c893fe2a602 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 15 Jan 2024 19:10:55 +0100 Subject: [PATCH 24/33] fix initial_datas --- idhub/management/commands/initial_datas.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index 034bc68..e4e6e4d 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -82,10 +82,18 @@ class Command(BaseCommand): try: ldata = json.loads(data) assert credtools.validate_schema(ldata) - name = ldata.get('name') - assert name + dname = ldata.get('name') + assert dname except Exception: return + name = '' + try: + for x in dname: + if settings.LANGUAGE_CODE in x['lang']: + name = x.get('value', '') + except Exception: + return + Schemas.objects.create(file_schema=file_name, data=data, type=name) def open_file(self, file_name): From 9d3b33db28cb490926cd08de20a783173c553013 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 15 Jan 2024 19:11:22 +0100 Subject: [PATCH 25/33] add new credentials --- idhub/models.py | 47 +++++++++++++------ .../credentials/membership-card.json | 38 +++++++-------- idhub/templates/idhub/admin/credentials.html | 2 +- idhub/templates/idhub/user/credentials.html | 2 +- idhub/user/forms.py | 5 +- idhub/user/views.py | 3 ++ 6 files changed, 61 insertions(+), 36 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 8d201e5..6c5ed1e 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -11,6 +11,7 @@ from utils.idhub_ssikit import ( generate_did_controller_key, keydid_from_controller_key, sign_credential, + verify_credential ) from idhub_auth.models import User @@ -509,10 +510,23 @@ class VerificableCredential(models.Model): def description(self): for des in json.loads(self.render()).get('description', []): - if settings.LANGUAGE_CODE == des.get('lang'): + if settings.LANGUAGE_CODE in des.get('lang'): return des.get('value', '') return '' + def get_type(self, lang=None): + schema = json.loads(self.schema.data) + if not schema.get('name'): + return '' + try: + for x in schema['name']: + if lang or settings.LANGUAGE_CODE in x['lang']: + return x.get('value', '') + except: + return self.schema.type + + return '' + def get_status(self): return self.Status(self.status).label @@ -524,16 +538,16 @@ class VerificableCredential(models.Model): if self.status == self.Status.ISSUED: return - self.status = self.Status.ISSUED + # self.status = self.Status.ISSUED self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) - data = sign_credential( - self.render(), + d_ordered = ujson.loads(self.render()) + d_minimum = self.filter_dict(d_ordered) + data = ujson.dumps(d_minimum) + self.data = sign_credential( + data, self.issuer_did.key_material ) - d_ordered = ujson.loads(data) - d_minimum = self.filter_dict(d_ordered) - self.data = ujson.dumps(d_minimum) def get_context(self): d = json.loads(self.csv_data) @@ -542,20 +556,25 @@ class VerificableCredential(models.Model): format = "%Y-%m-%dT%H:%M:%SZ" issuance_date = self.issued_on.strftime(format) - url_id = "{}/credentials/{}".format( - settings.DOMAIN.strip("/"), - self.id - ) + try: + domain = self._domain + except: + domain = settings.DOMAIN.strip("/") + + url_id = "{}/credentials/{}".format(domain, self.id) + context = { 'vc_id': url_id, 'issuer_did': self.issuer_did.did, 'subject_did': self.subject_did and self.subject_did.did or '', 'issuance_date': issuance_date, - 'first_name': self.user.first_name, - 'last_name': self.user.last_name, - 'email': self.user.email + 'firstName': self.user.first_name or "", + 'lastName': self.user.last_name or "", + 'email': self.user.email, + 'organisation': settings.ORGANIZATION or '', } context.update(d) + context['firstName'] = "" return context def render(self): diff --git a/idhub/templates/credentials/membership-card.json b/idhub/templates/credentials/membership-card.json index e3d1c15..5ecda0a 100644 --- a/idhub/templates/credentials/membership-card.json +++ b/idhub/templates/credentials/membership-card.json @@ -9,15 +9,15 @@ "VerifiableAttestation", "MembershipCard" ], - "id": "[[PLACEHOLDER]]", + "id": "{{ vc_id }}", "issuer": { - "id": "[[PLACEHOLDER]]", - "name": "[[PLACEHOLDER]]" + "id": "{{ issuer_did }}", + "name": "{{ organisation }}" }, - "issuanceDate": "[[PLACEHOLDER]]", - "issued": "[[PLACEHOLDER]]", - "validFrom": "[[PLACEHOLDER]]", - "validUntil": "[[PLACEHOLDER]]", + "issuanceDate": "{{ issuance_date }}", + "issued": "{{ issuance_date }}", + "validFrom": "{{ issuance_date }}", + "validUntil": "{{ validUntil }}", "name": [ { "value": "Membership Card", @@ -47,18 +47,18 @@ } ], "credentialSubject": { - "id": "[[PLACEHOLDER]]", - "firstName": "[[PLACEHOLDER]]", - "lastName": "[[PLACEHOLDER]]", - "email": "[[PLACEHOLDER]]", - "typeOfPerson": "[[PLACEHOLDER]]", - "identityDocType": "[[PLACEHOLDER]]", - "identityNumber": "[[PLACEHOLDER]]", - "organisation": "[[PLACEHOLDER]]", - "membershipType": "[[PLACEHOLDER]]", - "membershipId": "[[PLACEHOLDER]]", - "affiliatedSince": "[[PLACEHOLDER]]", - "affiliatedUntil": "[[PLACEHOLDER]]" + "id": "{{ subject_did }}", + "firstName": "{{ firstName }}", + "lastName": "{{ lastName }}", + "email": "{{ email }}", + "typeOfPerson": "{{ typeOfPerson }}", + "identityDocType": "{{ identityDocType }}", + "identityNumber": "{{ identityNumber }}", + "organisation": "{{ organisation }}", + "membershipType": "{{ membershipType }}", + "membershipId": "{{ vc_id }}", + "affiliatedSince": "{{ affiliatedSince }}", + "affiliatedUntil": "{{ affiliatedUntil }}" }, "credentialSchema": { "id": "https://idhub.pangea.org/vc_schemas/membership-card.json", diff --git a/idhub/templates/idhub/admin/credentials.html b/idhub/templates/idhub/admin/credentials.html index cab6a97..caee5fc 100644 --- a/idhub/templates/idhub/admin/credentials.html +++ b/idhub/templates/idhub/admin/credentials.html @@ -23,7 +23,7 @@ {% for f in credentials.all %} - {{ f.type }} + {{ f.get_type }} {{ f.description }} {{ f.get_issued_on }} {{ f.get_status }} diff --git a/idhub/templates/idhub/user/credentials.html b/idhub/templates/idhub/user/credentials.html index 6f68e56..f12ae00 100644 --- a/idhub/templates/idhub/user/credentials.html +++ b/idhub/templates/idhub/user/credentials.html @@ -22,7 +22,7 @@ {% for f in credentials.all %} - {{ f.type }} + {{ f.get_type }} {{ f.description }} {{ f.get_issued_on }} {{ f.get_status }} diff --git a/idhub/user/forms.py b/idhub/user/forms.py index 5ac04ad..f9eda6a 100644 --- a/idhub/user/forms.py +++ b/idhub/user/forms.py @@ -22,12 +22,14 @@ class RequestCredentialForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) + self.lang = kwargs.pop('lang', None) + self._domain = kwargs.pop('domain', None) super().__init__(*args, **kwargs) self.fields['did'].choices = [ (x.did, x.label) for x in DID.objects.filter(user=self.user) ] self.fields['credential'].choices = [ - (x.id, x.type()) for x in VerificableCredential.objects.filter( + (x.id, x.get_type(lang=self.lang)) for x in VerificableCredential.objects.filter( user=self.user, status=VerificableCredential.Status.ENABLED ) @@ -48,6 +50,7 @@ class RequestCredentialForm(forms.Form): did = did[0] cred = cred[0] + cred._domain = self._domain try: cred.issue(did) except Exception: diff --git a/idhub/user/views.py b/idhub/user/views.py index e6e28dc..2e1258e 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -135,6 +135,9 @@ class CredentialsRequestView(MyWallet, FormView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user + kwargs['lang'] = self.request.LANGUAGE_CODE + domain = "{}://{}".format(self.request.scheme, self.request.get_host()) + kwargs['domain'] = domain return kwargs def form_valid(self, form): From 47cf19f1296c21a4069ad606edf4fed8f76e44e4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Jan 2024 14:00:05 +0100 Subject: [PATCH 26/33] 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 27/33] 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 28/33] 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 29/33] 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 From 1e4323673c172fd560caeb3f60c24279db7e6a8b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 17 Jan 2024 12:40:54 +0100 Subject: [PATCH 30/33] encrypt admin dids with secret_key --- idhub/models.py | 5 ++++- idhub/views.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 0ff6499..128071b 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -535,7 +535,10 @@ class VerificableCredential(models.Model): self.status = self.Status.ISSUED self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) - issuer_pass = cache.get("KEY_DIDS") + issuer_pass = self.user.decrypt_data( + cache.get("KEY_DIDS"), + settings.SECRET_KEY, + ) data = sign_credential( self.render(), self.issuer_did.get_key_material(issuer_pass) diff --git a/idhub/views.py b/idhub/views.py index e746f02..3d64501 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -1,4 +1,5 @@ from django.urls import reverse_lazy +from django.conf import settings from django.core.cache import cache from django.utils.translation import gettext_lazy as _ from django.contrib.auth import views as auth_views @@ -30,7 +31,11 @@ class LoginView(auth_views.LoginView): if not user.is_anonymous and user.is_admin: admin_dashboard = reverse_lazy('idhub:admin_dashboard') self.extra_context['success_url'] = admin_dashboard - cache.set("KEY_DIDS", sensitive_data_encryption_key, None) + encryption_key = user.encrypt_data( + sensitive_data_encryption_key, + settings.SECRET_KEY + ) + cache.set("KEY_DIDS", encryption_key, None) self.request.session["key_did"] = user.encrypt_data( sensitive_data_encryption_key, From 9f2abf6a04ae3a9dfd58cb193e0e758a66ef83eb Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 17 Jan 2024 13:43:40 +0100 Subject: [PATCH 31/33] fix --- idhub/models.py | 9 +++++---- idhub/views.py | 11 ++++++----- idhub_auth/models.py | 11 +++-------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/idhub/models.py b/idhub/models.py index 128071b..0e3fe32 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -535,10 +535,11 @@ class VerificableCredential(models.Model): self.status = self.Status.ISSUED self.subject_did = did self.issued_on = datetime.datetime.now().astimezone(pytz.utc) - issuer_pass = self.user.decrypt_data( - cache.get("KEY_DIDS"), - settings.SECRET_KEY, - ) + issuer_pass = cache.get("KEY_DIDS") + # issuer_pass = self.user.decrypt_data( + # cache.get("KEY_DIDS"), + # settings.SECRET_KEY, + # ) data = sign_credential( self.render(), self.issuer_did.get_key_material(issuer_pass) diff --git a/idhub/views.py b/idhub/views.py index 3d64501..f544adb 100644 --- a/idhub/views.py +++ b/idhub/views.py @@ -31,11 +31,12 @@ class LoginView(auth_views.LoginView): if not user.is_anonymous and user.is_admin: admin_dashboard = reverse_lazy('idhub:admin_dashboard') self.extra_context['success_url'] = admin_dashboard - encryption_key = user.encrypt_data( - sensitive_data_encryption_key, - settings.SECRET_KEY - ) - cache.set("KEY_DIDS", encryption_key, None) + # encryption_key = user.encrypt_data( + # sensitive_data_encryption_key, + # settings.SECRET_KEY + # ) + # cache.set("KEY_DIDS", encryption_key, None) + cache.set("KEY_DIDS", sensitive_data_encryption_key, None) self.request.session["key_did"] = user.encrypt_data( sensitive_data_encryption_key, diff --git a/idhub_auth/models.py b/idhub_auth/models.py index 189b421..38224b2 100644 --- a/idhub_auth/models.py +++ b/idhub_auth/models.py @@ -135,28 +135,23 @@ class User(AbstractBaseUser): def set_encrypted_sensitive_data(self, password): key = base64.b64encode(nacl.utils.random(64)) - key_dids = cache.get("KEY_DIDS", {}) - - if key_dids.get(self.id): - key = key_dids[self.id] - else: - self.set_salt() + self.set_salt() key_crypted = self.encrypt_sensitive_data(password, key) self.encrypted_sensitive_data = key_crypted def encrypt_data(self, data, password): sb = self.get_secret_box(password) - value = base64.b64encode(data.encode('utf-8')) value_enc = sb.encrypt(data.encode('utf-8')) return base64.b64encode(value_enc).decode('utf-8') def decrypt_data(self, data, password): + # import pdb; pdb.set_trace() sb = self.get_secret_box(password) value = base64.b64decode(data.encode('utf-8')) return sb.decrypt(value).decode('utf-8') def get_secret_box(self, password): - pw = base64.b64decode(password.encode('utf-8')) + pw = base64.b64decode(password.encode('utf-8')*4) sb_key = self.derive_key_from_password(pw) return nacl.secret.SecretBox(sb_key) From fa3ae3dbe823d65207db8693e88ce37e835389ae Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 17 Jan 2024 18:42:15 +0100 Subject: [PATCH 32/33] fix url dids --- idhub/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idhub/urls.py b/idhub/urls.py index 5d52840..7f983c3 100644 --- a/idhub/urls.py +++ b/idhub/urls.py @@ -176,7 +176,7 @@ urlpatterns = [ path('admin/import/new', views_admin.ImportAddView.as_view(), name='admin_import_add'), - path('did-registry/', serve_did) + path('did-registry//did.json', serve_did) # path('verification_portal/verify/', views_verification_portal.verify, # name="verification_portal_verify") From 39197bc3e5b57c842c25e2bed8f578da2c933e42 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 17 Jan 2024 18:42:44 +0100 Subject: [PATCH 33/33] rebuild migrations --- idhub/migrations/0001_initial.py | 2 +- idhub_auth/migrations/0001_initial.py | 2 +- oidc4vp/migrations/0001_initial.py | 2 +- promotion/migrations/0001_initial.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index 321e70a..355af60 100644 --- a/idhub/migrations/0001_initial.py +++ b/idhub/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2024-01-17 13:11 +# Generated by Django 4.2.5 on 2024-01-17 16:56 from django.conf import settings from django.db import migrations, models diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index ee8f4a9..84a93da 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 2024-01-17 13:11 +# Generated by Django 4.2.5 on 2024-01-17 16:56 from django.db import migrations, models diff --git a/oidc4vp/migrations/0001_initial.py b/oidc4vp/migrations/0001_initial.py index 586b4e1..b1fceed 100644 --- a/oidc4vp/migrations/0001_initial.py +++ b/oidc4vp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2024-01-17 13:11 +# Generated by Django 4.2.5 on 2024-01-17 16:56 from django.conf import settings from django.db import migrations, models diff --git a/promotion/migrations/0001_initial.py b/promotion/migrations/0001_initial.py index 1aff341..ad2ac86 100644 --- a/promotion/migrations/0001_initial.py +++ b/promotion/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2024-01-17 13:11 +# Generated by Django 4.2.5 on 2024-01-17 16:56 from django.db import migrations, models import django.db.models.deletion