Merge pull request 'Pushed ssi functionality to idhub repo' (#75) from ssi into main
Reviewed-on: https://gitea.pangea.org/trustchain-oc1-orchestral/IdHub/pulls/75
This commit is contained in:
commit
b13488fdd4
|
@ -114,12 +114,12 @@ class ImportForm(forms.Form):
|
||||||
return user.first()
|
return user.first()
|
||||||
|
|
||||||
def create_credential(self, user, row):
|
def create_credential(self, user, row):
|
||||||
d = self.json_schema.copy()
|
|
||||||
d['instance'] = row
|
|
||||||
return VerificableCredential(
|
return VerificableCredential(
|
||||||
verified=False,
|
verified=False,
|
||||||
user=user,
|
user=user,
|
||||||
data=json.dumps(d)
|
csv_data=json.dumps(row),
|
||||||
|
issuer_did=self._did,
|
||||||
|
schema=self._schema,
|
||||||
)
|
)
|
||||||
|
|
||||||
def exception(self, msg):
|
def exception(self, msg):
|
||||||
|
|
|
@ -19,7 +19,6 @@ from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from utils.apiregiter import iota
|
|
||||||
from utils import credtools
|
from utils import credtools
|
||||||
from idhub_auth.models import User
|
from idhub_auth.models import User
|
||||||
from idhub_auth.forms import ProfileForm
|
from idhub_auth.forms import ProfileForm
|
||||||
|
@ -646,7 +645,7 @@ class DidRegisterView(Credentials, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
form.instance.did = iota.issue_did()
|
form.instance.set_did()
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID created successfully'))
|
messages.success(self.request, _('DID created successfully'))
|
||||||
Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance)
|
Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2023-11-14 16:32
|
# Generated by Django 4.2.5 on 2023-11-15 09:58
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -13,6 +13,33 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
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(
|
migrations.CreateModel(
|
||||||
name='File_datas',
|
name='File_datas',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -141,9 +168,9 @@ class Migration(migrations.Migration):
|
||||||
('verified', models.BooleanField()),
|
('verified', models.BooleanField()),
|
||||||
('created_on', models.DateTimeField(auto_now=True)),
|
('created_on', models.DateTimeField(auto_now=True)),
|
||||||
('issued_on', models.DateTimeField(null=True)),
|
('issued_on', models.DateTimeField(null=True)),
|
||||||
('did_issuer', models.CharField(max_length=250)),
|
('subject_did', models.CharField(max_length=250)),
|
||||||
('did_subject', models.CharField(max_length=250)),
|
|
||||||
('data', models.TextField()),
|
('data', models.TextField()),
|
||||||
|
('csv_data', models.TextField()),
|
||||||
(
|
(
|
||||||
'status',
|
'status',
|
||||||
models.PositiveSmallIntegerField(
|
models.PositiveSmallIntegerField(
|
||||||
|
@ -156,6 +183,22 @@ class Migration(migrations.Migration):
|
||||||
default=1,
|
default=1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'issuer_did',
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='vcredentials',
|
||||||
|
to='idhub.did',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'schema',
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='vcredentials',
|
||||||
|
to='idhub.schemas',
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
'user',
|
'user',
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
@ -275,32 +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)),
|
|
||||||
('did', models.CharField(max_length=250, unique=True)),
|
|
||||||
('label', models.CharField(max_length=50)),
|
|
||||||
(
|
|
||||||
'user',
|
|
||||||
models.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name='dids',
|
|
||||||
to=settings.AUTH_USER_MODEL,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserRol',
|
name='UserRol',
|
||||||
fields=[
|
fields=[
|
||||||
|
|
|
@ -2,7 +2,13 @@ import json
|
||||||
import requests
|
import requests
|
||||||
import datetime
|
import datetime
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.template.loader import get_template
|
||||||
from django.utils.translation import gettext_lazy as _
|
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
|
from idhub_auth.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -396,15 +402,18 @@ class Event(models.Model):
|
||||||
|
|
||||||
class DID(models.Model):
|
class DID(models.Model):
|
||||||
created_at = models.DateTimeField(auto_now=True)
|
created_at = models.DateTimeField(auto_now=True)
|
||||||
did = models.CharField(max_length=250, unique=True)
|
|
||||||
label = models.CharField(max_length=50)
|
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"}'
|
||||||
|
key_material = models.CharField(max_length=250)
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='dids',
|
related_name='dids',
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
# kind = "KEY|WEB"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_organization_did(self):
|
def is_organization_did(self):
|
||||||
|
@ -412,6 +421,13 @@ class DID(models.Model):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def set_did(self):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
class Schemas(models.Model):
|
class Schemas(models.Model):
|
||||||
file_schema = models.CharField(max_length=250)
|
file_schema = models.CharField(max_length=250)
|
||||||
|
@ -445,9 +461,9 @@ class VerificableCredential(models.Model):
|
||||||
verified = models.BooleanField()
|
verified = models.BooleanField()
|
||||||
created_on = models.DateTimeField(auto_now=True)
|
created_on = models.DateTimeField(auto_now=True)
|
||||||
issued_on = models.DateTimeField(null=True)
|
issued_on = models.DateTimeField(null=True)
|
||||||
did_issuer = models.CharField(max_length=250)
|
subject_did = models.CharField(max_length=250)
|
||||||
did_subject = models.CharField(max_length=250)
|
|
||||||
data = models.TextField()
|
data = models.TextField()
|
||||||
|
csv_data = models.TextField()
|
||||||
status = models.PositiveSmallIntegerField(
|
status = models.PositiveSmallIntegerField(
|
||||||
choices=Status.choices,
|
choices=Status.choices,
|
||||||
default=Status.ENABLED
|
default=Status.ENABLED
|
||||||
|
@ -457,6 +473,16 @@ class VerificableCredential(models.Model):
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='vcredentials',
|
related_name='vcredentials',
|
||||||
)
|
)
|
||||||
|
issuer_did = models.ForeignKey(
|
||||||
|
DID,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='vcredentials',
|
||||||
|
)
|
||||||
|
schema = models.ForeignKey(
|
||||||
|
Schemas,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='vcredentials',
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_schema(self):
|
def get_schema(self):
|
||||||
|
@ -474,16 +500,49 @@ class VerificableCredential(models.Model):
|
||||||
return self.Status(self.status).label
|
return self.Status(self.status).label
|
||||||
|
|
||||||
def get_datas(self):
|
def get_datas(self):
|
||||||
data = json.loads(self.data).get('instance').items()
|
data = json.loads(self.csv_data).items()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def issue(self, did):
|
def issue(self, did):
|
||||||
|
if self.status == self.Status.ISSUED:
|
||||||
|
return
|
||||||
|
|
||||||
self.status = self.Status.ISSUED
|
self.status = self.Status.ISSUED
|
||||||
self.did_subject = did
|
self.subject_did = did
|
||||||
self.issued_on = datetime.datetime.now()
|
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):
|
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):
|
class VCTemplate(models.Model):
|
||||||
wkit_template_id = models.CharField(max_length=250)
|
wkit_template_id = models.CharField(max_length=250)
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from utils.apiregiter import iota
|
|
||||||
from idhub.user.forms import ProfileForm, RequestCredentialForm, CredentialPresentationForm
|
from idhub.user.forms import ProfileForm, RequestCredentialForm, CredentialPresentationForm
|
||||||
from idhub.mixins import UserView
|
from idhub.mixins import UserView
|
||||||
from idhub.models import DID, VerificableCredential, Event
|
from idhub.models import DID, VerificableCredential, Event
|
||||||
|
@ -190,7 +189,7 @@ class DidRegisterView(MyWallet, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
form.instance.did = iota.issue_did()
|
form.instance.set_did()
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID created successfully'))
|
messages.success(self.request, _('DID created successfully'))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2023-11-14 16:32
|
# Generated by Django 4.2.5 on 2023-11-15 09:58
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,7 @@ python-decouple==3.8
|
||||||
jsonschema==4.19.1
|
jsonschema==4.19.1
|
||||||
pandas==2.1.1
|
pandas==2.1.1
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
|
didkit==0.3.2
|
||||||
|
jinja2==3.1.2
|
||||||
jsonref==1.1.0
|
jsonref==1.1.0
|
||||||
pyld==2.0.3
|
pyld==2.0.3
|
||||||
|
|
|
@ -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"]
|
|
||||||
}
|
|
|
@ -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",
|
"$id": "https://pangea.org/schemas/member-credential-schema.json",
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"name": "MemberCredential",
|
"name": "MemberCredential",
|
||||||
|
@ -36,5 +19,3 @@
|
||||||
},
|
},
|
||||||
"required": ["name", "email", "membershipType"]
|
"required": ["name", "email", "membershipType"]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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()
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import asyncio
|
||||||
|
import datetime
|
||||||
|
import didkit
|
||||||
|
import json
|
||||||
|
import jinja2
|
||||||
|
from django.template.backends.django import Template
|
||||||
|
|
||||||
|
|
||||||
|
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 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):
|
||||||
|
"""
|
||||||
|
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())
|
Loading…
Reference in New Issue