From 628ca0554dda4344ced8fbeccc89d8965b077847 Mon Sep 17 00:00:00 2001 From: Daniel Armengod Date: Fri, 10 Nov 2023 06:48:52 +0100 Subject: [PATCH 1/7] Pushed ssi functionality to idhub repo --- idhub/models.py | 12 ++++ idhub_ssikit/README.md | 0 .../TENANT_CFG_TEMPLATE | 0 idhub_ssikit/__init__.py | 58 +++++++++++++++++++ requirements.txt | 3 +- schemas/member-credential.json | 32 ++++++++++ trustchain_walletkit/__init__.py | 52 ----------------- 7 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 idhub_ssikit/README.md rename {trustchain_walletkit => idhub_ssikit}/TENANT_CFG_TEMPLATE (100%) create mode 100644 idhub_ssikit/__init__.py create mode 100644 schemas/member-credential.json delete mode 100644 trustchain_walletkit/__init__.py diff --git a/idhub/models.py b/idhub/models.py index 8a805d4..16de09c 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -31,6 +31,18 @@ class DID(models.Model): return False +class DIDControllerKey(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) + owner_did = models.ForeignKey( + DID, + on_delete=models.CASCADE, + related_name="keys" + ) + + class Schemas(models.Model): file_schema = models.CharField(max_length=250) data = models.TextField() diff --git a/idhub_ssikit/README.md b/idhub_ssikit/README.md new file mode 100644 index 0000000..e69de29 diff --git a/trustchain_walletkit/TENANT_CFG_TEMPLATE b/idhub_ssikit/TENANT_CFG_TEMPLATE similarity index 100% rename from trustchain_walletkit/TENANT_CFG_TEMPLATE rename to idhub_ssikit/TENANT_CFG_TEMPLATE diff --git a/idhub_ssikit/__init__.py b/idhub_ssikit/__init__.py new file mode 100644 index 0000000..dced08e --- /dev/null +++ b/idhub_ssikit/__init__.py @@ -0,0 +1,58 @@ +import asyncio +import datetime +import didkit +import json +import jinja2 + + +def generate_did_controller_key(): + return didkit.generate_ed25519_key() + + +def keydid_from_controller_key(key): + return didkit.key_to_did("key", key) + + +def generate_generic_vc_id(): + # TODO agree on a system for Verifiable Credential IDs + return "https://pangea.org/credentials/42" + + +def render_and_sign_credential(vc_template: jinja2.Template, jwk_issuer, vc_data: dict[str, str]): + """ + Populates a VC template with data for issuance, and signs the result with the provided key. + + The `vc_data` parameter must at a minimum include: + * issuer_did + * subject_did + * vc_id + and must include whatever other fields are relevant for the vc_template to be instantiated. + + The following field(s) will be auto-generated if not passed in `vc_data`: + * issuance_date (to `datetime.datetime.now()`) + """ + async def inner(): + unsigned_vc = vc_template.render(vc_data) + signed_vc = await didkit.issue_credential( + unsigned_vc, + '{"proofFormat": "ldp"}', + jwk_issuer + ) + return signed_vc + + if vc_data.get("issuance_date") is None: + vc_data["issuance_date"] = datetime.datetime.now().replace(microsecond=0).isoformat() + + return asyncio.run(inner()) + + +def verify_credential(vc, proof_options): + """ + Returns a (bool, str) tuple indicating whether the credential is valid. + If the boolean is true, the credential is valid and the second argument can be ignored. + If it is false, the VC is invalid and the second argument contains a JSON object with further information. + """ + async def inner(): + return didkit.verify_credential(vc, proof_options) + + return asyncio.run(inner()) diff --git a/requirements.txt b/requirements.txt index 4281ff1..34bad77 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ python-decouple==3.8 jsonschema==4.19.1 pandas==2.1.1 requests==2.31.0 - +didkit==0.3.2 +jinja2==3.1.2 \ No newline at end of file diff --git a/schemas/member-credential.json b/schemas/member-credential.json new file mode 100644 index 0000000..e98bef2 --- /dev/null +++ b/schemas/member-credential.json @@ -0,0 +1,32 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + { + "name": "https://schema.org/name", + "email": "https://schema.org/email", + "membershipType": "https://schema.org/memberOf", + "individual": "https://schema.org/Person", + "organization": "https://schema.org/Organization", + "Member": "https://schema.org/Member", + "startDate": "https://schema.org/startDate", + "jsonSchema": "https://schema.org/jsonSchema", + "$ref": "https://schema.org/jsonSchemaRef" + } + ], + "type": ["VerifiableCredential", "VerifiableAttestation"], + "id": "{{ vc_id }}", + "issuer": "{{ issuer_did }}", + "issuanceDate": "{{ issuance_date }}", + "credentialSubject": { + "id": "{{ subject_did }}", + "Member": { + "name": "{{ name }}", + "email": "{{ email }}", + "membershipType": "{{ membershipType }}", + "startDate": "{{ startDate }}" + }, + "jsonSchema": { + "$ref": "https://gitea.pangea.org/trustchain-oc1-orchestral/schemas/member-schema.json" + } + } +} \ No newline at end of file diff --git a/trustchain_walletkit/__init__.py b/trustchain_walletkit/__init__.py deleted file mode 100644 index 02d0144..0000000 --- a/trustchain_walletkit/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -from pathlib import Path - -import requests -import json - -WALLETKITD = 'http://localhost:8080/' -ISSUER = f'{WALLETKITD}issuer-api/default/' -VERIFIER = f'{WALLETKITD}verifier-api/default/' - -default_ctype_header = { - 'Content-Type': 'application/json', # specify the type of data you're sending - 'Accept': 'application/json', # specify the type of data you can accept -} - - -def include_str(path): - with open(path, "r") as f: - return f.read().strip() - - -# Create DID for tenant -# Valid methods: 'key'|'web' -def user_create_did(did_method): - url = f'{ISSUER}config/did/create' - data = { - 'method': did_method - } - response = requests.post(url, json=data, headers=default_ctype_header) - response.raise_for_status() - return response.text - - -def admin_create_template(template_name, template_body): - url = f'{ISSUER}config/templates/{template_name}' - body = template_body - response = requests.post(url, data=body, headers=default_ctype_header) - response.raise_for_status() - return - - -def user_issue_vc(vc_name, vc_params): - url = f'{ISSUER}credentials/issuance/request' - # ... - # TODO examine cross-device issuance workflow - pass - - - - - -TENANT_CFG_TMEPLATE = include_str("./TENANT_CFG_TEMPLATE") - From 6cf289f5b125e3d0618dbe87b831bc94de415a6d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 14 Nov 2023 09:48:36 +0100 Subject: [PATCH 2/7] new way for to get a new did --- idhub/admin/views.py | 3 +-- idhub/models.py | 12 +++++++++++- idhub/user/views.py | 3 +-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/idhub/admin/views.py b/idhub/admin/views.py index 0492f5d..786b0be 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -20,7 +20,6 @@ from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.http import HttpResponse from django.contrib import messages -from utils.apiregiter import iota from utils import credtools from idhub_auth.models import User from idhub.mixins import AdminView @@ -511,7 +510,7 @@ class DidRegisterView(Credentials, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.did = iota.issue_did() + form.instance.set_did() form.save() messages.success(self.request, _('DID created successfully')) return super().form_valid(form) diff --git a/idhub/models.py b/idhub/models.py index bb9b4ae..1c0c8e8 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -2,6 +2,7 @@ import json import requests from django.db import models from django.utils.translation import gettext_lazy as _ +from idhub_ssikit import generate_did_controller_key from idhub_auth.models import User @@ -14,7 +15,6 @@ from idhub_auth.models import User class DID(models.Model): created_at = models.DateTimeField(auto_now=True) - did = models.CharField(max_length=250, unique=True) label = models.CharField(max_length=50) # In JWK format. Must be stored as-is and passed whole to library functions. # Example key material: @@ -33,6 +33,16 @@ class DID(models.Model): return True return False + @property + def did(self): + return self.get_key().get("d") + + def set_did(self): + self.key_material = idhub_ssikit.generate_did_controller_key() + + def get_key(self): + return json.loads(self.key_material) + class Schemas(models.Model): file_schema = models.CharField(max_length=250) diff --git a/idhub/user/views.py b/idhub/user/views.py index f45f7e6..02c0cfe 100644 --- a/idhub/user/views.py +++ b/idhub/user/views.py @@ -12,7 +12,6 @@ from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.http import HttpResponse from django.contrib import messages -from utils.apiregiter import iota from idhub.user.forms import ProfileForm, RequestCredentialForm, CredentialPresentationForm from idhub.mixins import UserView from idhub.models import DID, VerificableCredential @@ -183,7 +182,7 @@ class DidRegisterView(MyWallet, CreateView): def form_valid(self, form): form.instance.user = self.request.user - form.instance.did = iota.issue_did() + form.instance.set_did() form.save() messages.success(self.request, _('DID created successfully')) return super().form_valid(form) From 22e88c5798ae31d233dd692c690fe47ceb3522e2 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 14 Nov 2023 17:01:03 +0100 Subject: [PATCH 3/7] migratioins --- idhub/migrations/0001_initial.py | 4 ++-- idhub_auth/migrations/0001_initial.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index fe6fc19..e62b758 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-02 15:08 +# Generated by Django 4.2.5 on 2023-11-14 14:57 from django.conf import settings from django.db import migrations, models @@ -247,8 +247,8 @@ class Migration(migrations.Migration): ), ), ('created_at', models.DateTimeField(auto_now=True)), - ('did', models.CharField(max_length=250, unique=True)), ('label', models.CharField(max_length=50)), + ('key_material', models.CharField(max_length=250)), ( 'user', models.ForeignKey( diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index 2196067..91bc80c 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-02 15:08 +# Generated by Django 4.2.5 on 2023-11-14 14:57 from django.db import migrations, models From 2df87faf80d2414e7753ea887788267002b200a4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 14 Nov 2023 18:12:34 +0100 Subject: [PATCH 4/7] add idhub_ssikit in utils --- idhub/migrations/0001_initial.py | 3 ++- idhub/models.py | 13 +++++++------ .../templates/credentials}/member-credential.json | 0 idhub_auth/migrations/0001_initial.py | 2 +- {idhub_ssikit => utils/idhub_ssikit}/README.md | 0 .../idhub_ssikit}/TENANT_CFG_TEMPLATE | 0 {idhub_ssikit => utils/idhub_ssikit}/__init__.py | 0 7 files changed, 10 insertions(+), 8 deletions(-) rename {schemas => idhub/templates/credentials}/member-credential.json (100%) rename {idhub_ssikit => utils/idhub_ssikit}/README.md (100%) rename {idhub_ssikit => utils/idhub_ssikit}/TENANT_CFG_TEMPLATE (100%) rename {idhub_ssikit => utils/idhub_ssikit}/__init__.py (100%) diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index cdd6262..e95751c 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-14 16:48 +# Generated by Django 4.2.5 on 2023-11-14 17:08 from django.conf import settings from django.db import migrations, models @@ -289,6 +289,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)), ( 'user', diff --git a/idhub/models.py b/idhub/models.py index b74f9dc..68a0f8d 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -3,7 +3,10 @@ import requests import datetime from django.db import models from django.utils.translation import gettext_lazy as _ -from idhub_ssikit import generate_did_controller_key +from utils.idhub_ssikit import ( + generate_did_controller_key, + keydid_from_controller_key, +) from idhub_auth.models import User @@ -398,6 +401,7 @@ class Event(models.Model): class DID(models.Model): created_at = models.DateTimeField(auto_now=True) label = models.CharField(max_length=50) + did = models.CharField(max_length=250) # 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"}' @@ -415,12 +419,9 @@ class DID(models.Model): return True return False - @property - def did(self): - return self.get_key().get("d") - def set_did(self): - self.key_material = idhub_ssikit.generate_did_controller_key() + self.key_material = generate_did_controller_key() + self.did = keydid_from_controller_key(self.key_material) def get_key(self): return json.loads(self.key_material) diff --git a/schemas/member-credential.json b/idhub/templates/credentials/member-credential.json similarity index 100% rename from schemas/member-credential.json rename to idhub/templates/credentials/member-credential.json diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index 3400ba6..c15aee0 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-14 16:48 +# Generated by Django 4.2.5 on 2023-11-14 17:08 from django.db import migrations, models diff --git a/idhub_ssikit/README.md b/utils/idhub_ssikit/README.md similarity index 100% rename from idhub_ssikit/README.md rename to utils/idhub_ssikit/README.md diff --git a/idhub_ssikit/TENANT_CFG_TEMPLATE b/utils/idhub_ssikit/TENANT_CFG_TEMPLATE similarity index 100% rename from idhub_ssikit/TENANT_CFG_TEMPLATE rename to utils/idhub_ssikit/TENANT_CFG_TEMPLATE diff --git a/idhub_ssikit/__init__.py b/utils/idhub_ssikit/__init__.py similarity index 100% rename from idhub_ssikit/__init__.py rename to utils/idhub_ssikit/__init__.py From dc887c98c2031c6d79b7d9671cb18062b0d10361 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 14 Nov 2023 18:50:23 +0100 Subject: [PATCH 5/7] create a pre-credential --- idhub/admin/forms.py | 6 +++--- idhub/migrations/0001_initial.py | 11 +++++++++- idhub/models.py | 13 ++++++++++-- .../{member-credential.json => member.json} | 0 idhub_auth/migrations/0001_initial.py | 2 +- schemas/member-schema.json | 21 ------------------- schemas/member.json | 21 +------------------ 7 files changed, 26 insertions(+), 48 deletions(-) rename idhub/templates/credentials/{member-credential.json => member.json} (100%) delete mode 100644 schemas/member-schema.json diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index 8ff83b5..1f01555 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -114,12 +114,12 @@ class ImportForm(forms.Form): return user.first() def create_credential(self, user, row): - d = self.json_schema.copy() - d['instance'] = row return VerificableCredential( verified=False, user=user, - data=json.dumps(d) + csv_data=json.dumps(row), + did_issuer=self._did.did, + schema=self._schema, ) def exception(self, msg): diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index e95751c..d06dde8 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-14 17:08 +# Generated by Django 4.2.5 on 2023-11-14 17:48 from django.conf import settings from django.db import migrations, models @@ -144,6 +144,7 @@ class Migration(migrations.Migration): ('did_issuer', models.CharField(max_length=250)), ('did_subject', models.CharField(max_length=250)), ('data', models.TextField()), + ('csv_data', models.TextField()), ( 'status', models.PositiveSmallIntegerField( @@ -156,6 +157,14 @@ class Migration(migrations.Migration): default=1, ), ), + ( + 'schema', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='vcredentials', + to='idhub.schemas', + ), + ), ( 'user', models.ForeignKey( diff --git a/idhub/models.py b/idhub/models.py index 68a0f8d..58fe233 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -462,6 +462,7 @@ class VerificableCredential(models.Model): did_issuer = models.CharField(max_length=250) did_subject = models.CharField(max_length=250) data = models.TextField() + csv_data = models.TextField() status = models.PositiveSmallIntegerField( choices=Status.choices, default=Status.ENABLED @@ -471,6 +472,11 @@ class VerificableCredential(models.Model): on_delete=models.CASCADE, related_name='vcredentials', ) + schema = models.ForeignKey( + Schemas, + on_delete=models.CASCADE, + related_name='vcredentials', + ) @property def get_schema(self): @@ -488,7 +494,7 @@ class VerificableCredential(models.Model): return self.Status(self.status).label def get_datas(self): - data = json.loads(self.data).get('instance').items() + data = json.loads(self.csv_data).items() return data def issue(self, did): @@ -497,7 +503,10 @@ class VerificableCredential(models.Model): self.issued_on = datetime.datetime.now() def get_issued_on(self): - return self.issued_on.strftime("%m/%d/%Y") + if self.issued_on: + return self.issued_on.strftime("%m/%d/%Y") + + return '' class VCTemplate(models.Model): wkit_template_id = models.CharField(max_length=250) diff --git a/idhub/templates/credentials/member-credential.json b/idhub/templates/credentials/member.json similarity index 100% rename from idhub/templates/credentials/member-credential.json rename to idhub/templates/credentials/member.json diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index c15aee0..205b13a 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-14 17:08 +# Generated by Django 4.2.5 on 2023-11-14 17:48 from django.db import migrations, models diff --git a/schemas/member-schema.json b/schemas/member-schema.json deleted file mode 100644 index 38199be..0000000 --- a/schemas/member-schema.json +++ /dev/null @@ -1,21 +0,0 @@ - { - "$id": "https://pangea.org/schemas/member-credential-schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "name": "MemberCredential", - "description": "MemberCredential using JsonSchemaCredential", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string", - "format": "email" - }, - "membershipType": { - "type": "string", - "enum": ["individual", "organization"] - } - }, - "required": ["name", "email", "membershipType"] - } diff --git a/schemas/member.json b/schemas/member.json index 845ab50..38199be 100644 --- a/schemas/member.json +++ b/schemas/member.json @@ -1,21 +1,4 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://www.w3.org/ns/credentials/examples/v2" - ], - "id": "https://example.com/credentials/3734", - "type": ["VerifiableCredential", "JsonSchemaCredential"], - "issuer": "https://pangea.org/issuers/10", - "issuanceDate": "2023-09-01T19:23:24Z", - "credentialSchema": { - "id": "https://www.w3.org/2022/credentials/v2/json-schema-credential-schema.json", - "type": "JsonSchema", - "digestSRI": "sha384-S57yQDg1MTzF56Oi9DbSQ14u7jBy0RDdx0YbeV7shwhCS88G8SCXeFq82PafhCrW" - }, - "credentialSubject": { - "id": "https://pangea.org/schemas/member-credential-schema.json", - "type": "JsonSchema", - "jsonSchema": { + { "$id": "https://pangea.org/schemas/member-credential-schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "name": "MemberCredential", @@ -36,5 +19,3 @@ }, "required": ["name", "email", "membershipType"] } - } -} From 72a356ffc8f85da21f66cbc223bb58661de3d99a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 15 Nov 2023 11:43:13 +0100 Subject: [PATCH 6/7] issue real credentials --- idhub/admin/forms.py | 2 +- idhub/migrations/0001_initial.py | 67 +++++++++++++++------------ idhub/models.py | 42 +++++++++++++++-- idhub_auth/migrations/0001_initial.py | 2 +- utils/idhub_ssikit/__init__.py | 16 +++++++ 5 files changed, 94 insertions(+), 35 deletions(-) diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index 1f01555..90a9c0c 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -118,7 +118,7 @@ class ImportForm(forms.Form): verified=False, user=user, csv_data=json.dumps(row), - did_issuer=self._did.did, + issuer_did=self._did, schema=self._schema, ) diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index d06dde8..b4d6ac7 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-14 17:48 +# Generated by Django 4.2.5 on 2023-11-15 09:58 from django.conf import settings from django.db import migrations, models @@ -13,6 +13,33 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='DID', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('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)), + ( + 'user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='dids', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), migrations.CreateModel( name='File_datas', fields=[ @@ -141,8 +168,7 @@ class Migration(migrations.Migration): ('verified', models.BooleanField()), ('created_on', models.DateTimeField(auto_now=True)), ('issued_on', models.DateTimeField(null=True)), - ('did_issuer', models.CharField(max_length=250)), - ('did_subject', models.CharField(max_length=250)), + ('subject_did', models.CharField(max_length=250)), ('data', models.TextField()), ('csv_data', models.TextField()), ( @@ -157,6 +183,14 @@ class Migration(migrations.Migration): default=1, ), ), + ( + 'issuer_did', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='vcredentials', + to='idhub.did', + ), + ), ( 'schema', models.ForeignKey( @@ -284,33 +318,6 @@ class Migration(migrations.Migration): ), ], ), - migrations.CreateModel( - name='DID', - fields=[ - ( - 'id', - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ('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)), - ( - 'user', - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='dids', - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), migrations.CreateModel( name='UserRol', fields=[ diff --git a/idhub/models.py b/idhub/models.py index 58fe233..4e9cf6f 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -2,10 +2,12 @@ import json import requests import datetime from django.db import models +from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ from utils.idhub_ssikit import ( generate_did_controller_key, keydid_from_controller_key, + sign_credential, ) from idhub_auth.models import User @@ -459,8 +461,7 @@ class VerificableCredential(models.Model): verified = models.BooleanField() created_on = models.DateTimeField(auto_now=True) issued_on = models.DateTimeField(null=True) - did_issuer = models.CharField(max_length=250) - did_subject = models.CharField(max_length=250) + subject_did = models.CharField(max_length=250) data = models.TextField() csv_data = models.TextField() status = models.PositiveSmallIntegerField( @@ -472,6 +473,11 @@ class VerificableCredential(models.Model): on_delete=models.CASCADE, related_name='vcredentials', ) + issuer_did = models.ForeignKey( + DID, + on_delete=models.CASCADE, + related_name='vcredentials', + ) schema = models.ForeignKey( Schemas, on_delete=models.CASCADE, @@ -498,9 +504,39 @@ class VerificableCredential(models.Model): return data def issue(self, did): + if self.status == self.Status.ISSUED: + return + self.status = self.Status.ISSUED - self.did_subject = did + self.subject_did = did self.issued_on = datetime.datetime.now() + self.data = sign_credential( + self.render(), + self.issuer_did.key_material + ) + + def get_context(self): + d = json.loads(self.csv_data) + format = "%Y-%m-%dT%H:%M:%SZ" + issuance_date = self.issued_on.strftime(format) + + context = { + 'vc_id': self.id, + 'issuer_did': self.issuer_did.did, + 'subject_did': self.subject_did, + 'issuance_date': issuance_date, + } + context.update(d) + return context + + def render(self): + context = self.get_context() + template_name = 'credentials/{}'.format( + self.schema.file_schema + ) + tmpl = get_template(template_name) + return tmpl.render(context) + def get_issued_on(self): if self.issued_on: diff --git a/idhub_auth/migrations/0001_initial.py b/idhub_auth/migrations/0001_initial.py index 205b13a..d40f0a4 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-14 17:48 +# Generated by Django 4.2.5 on 2023-11-15 09:58 from django.db import migrations, models diff --git a/utils/idhub_ssikit/__init__.py b/utils/idhub_ssikit/__init__.py index dced08e..18a5ff2 100644 --- a/utils/idhub_ssikit/__init__.py +++ b/utils/idhub_ssikit/__init__.py @@ -3,6 +3,7 @@ import datetime import didkit import json import jinja2 +from django.template.backends.django import Template def generate_did_controller_key(): @@ -45,6 +46,21 @@ def render_and_sign_credential(vc_template: jinja2.Template, jwk_issuer, vc_data return asyncio.run(inner()) + +def sign_credential(unsigned_vc: str, jwk_issuer): + """ + Signs the and unsigned credential with the provided key. + """ + async def inner(): + signed_vc = await didkit.issue_credential( + unsigned_vc, + '{"proofFormat": "ldp"}', + jwk_issuer + ) + return signed_vc + + return asyncio.run(inner()) + def verify_credential(vc, proof_options): """ From d48716f9baf526a308273735a80219770e104633 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 15 Nov 2023 11:44:19 +0100 Subject: [PATCH 7/7] remove apiregister --- utils/apiregiter.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 utils/apiregiter.py diff --git a/utils/apiregiter.py b/utils/apiregiter.py deleted file mode 100644 index e794181..0000000 --- a/utils/apiregiter.py +++ /dev/null @@ -1,17 +0,0 @@ -import uuid -import hashlib - - -class Iota: - """ - Framework for simulate the comunication with IOTA DLT - """ - - def issue_did(self): - u = str(uuid.uuid4()).encode() - d = hashlib.sha3_256(u).hexdigest() - did = "did:iota:{}".format(d) - return did - - -iota = Iota()