generate presentation, step1
This commit is contained in:
parent
2c97bf8d36
commit
37908ba1e7
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2023-12-01 17:19
|
# Generated by Django 4.2.5 on 2023-12-01 18:29
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -148,7 +148,6 @@ 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)),
|
||||||
('subject_did', models.CharField(max_length=250)),
|
|
||||||
('data', models.TextField()),
|
('data', models.TextField()),
|
||||||
('csv_data', models.TextField()),
|
('csv_data', models.TextField()),
|
||||||
(
|
(
|
||||||
|
@ -179,6 +178,14 @@ class Migration(migrations.Migration):
|
||||||
to='idhub.schemas',
|
to='idhub.schemas',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'subject_did',
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='subject_credentials',
|
||||||
|
to='idhub.did',
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
'user',
|
'user',
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
|
|
@ -463,7 +463,6 @@ 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)
|
||||||
subject_did = models.CharField(max_length=250)
|
|
||||||
data = models.TextField()
|
data = models.TextField()
|
||||||
csv_data = models.TextField()
|
csv_data = models.TextField()
|
||||||
status = models.PositiveSmallIntegerField(
|
status = models.PositiveSmallIntegerField(
|
||||||
|
@ -475,6 +474,11 @@ class VerificableCredential(models.Model):
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='vcredentials',
|
related_name='vcredentials',
|
||||||
)
|
)
|
||||||
|
subject_did = models.ForeignKey(
|
||||||
|
DID,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='subject_credentials',
|
||||||
|
)
|
||||||
issuer_did = models.ForeignKey(
|
issuer_did = models.ForeignKey(
|
||||||
DID,
|
DID,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2023-12-01 17:19
|
# Generated by Django 4.2.5 on 2023-12-01 18:29
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
|
@ -1,54 +1,13 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from utils.idhub_ssikit import issue_verifiable_presentation
|
||||||
from oidc4vp.models import Organization
|
from oidc4vp.models import Organization
|
||||||
|
|
||||||
|
|
||||||
# class OrganizationForm(forms.Form):
|
|
||||||
# wallet = forms.ChoiceField(
|
|
||||||
# "Wallet",
|
|
||||||
# choices=[(x.id, x.name) for x in Organization.objects.all()]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# def clean_wallet(self):
|
|
||||||
# data = self.cleaned_data["wallet"]
|
|
||||||
# organization = Organization.objects.filter(
|
|
||||||
# id=data
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if not organization.exists():
|
|
||||||
# raise ValidationError("organization is not valid!")
|
|
||||||
|
|
||||||
# self.organization = organization.first()
|
|
||||||
|
|
||||||
# return data
|
|
||||||
|
|
||||||
# def authorize(self):
|
|
||||||
# data = {
|
|
||||||
# "response_type": "vp_token",
|
|
||||||
# "response_mode": "direct_post",
|
|
||||||
# "client_id": self.organization.client_id,
|
|
||||||
# "response_uri": settings.RESPONSE_URI,
|
|
||||||
# "presentation_definition": self.pv_definition(),
|
|
||||||
# "nonce": ""
|
|
||||||
# }
|
|
||||||
# query_dict = QueryDict('', mutable=True)
|
|
||||||
# query_dict.update(data)
|
|
||||||
|
|
||||||
# url = '{response_uri}/authorize?{params}'.format(
|
|
||||||
# response_uri=self.organization.response_uri,
|
|
||||||
# params=query_dict.urlencode()
|
|
||||||
# )
|
|
||||||
|
|
||||||
# def pv_definition(self):
|
|
||||||
# return ""
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizeForm(forms.Form):
|
class AuthorizeForm(forms.Form):
|
||||||
# organization = forms.ChoiceField(choices=[])
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# import pdb; pdb.set_trace()
|
|
||||||
self.data = kwargs.get('data', {}).copy()
|
self.data = kwargs.get('data', {}).copy()
|
||||||
self.user = kwargs.pop('user', None)
|
self.user = kwargs.pop('user', None)
|
||||||
self.presentation_definition = kwargs.pop('presentation_definition', [])
|
self.presentation_definition = kwargs.pop('presentation_definition', [])
|
||||||
|
@ -69,20 +28,36 @@ class AuthorizeForm(forms.Form):
|
||||||
widget=forms.RadioSelect,
|
widget=forms.RadioSelect,
|
||||||
choices=choices
|
choices=choices
|
||||||
)
|
)
|
||||||
|
def clean(self):
|
||||||
|
data = super().clean()
|
||||||
|
import pdb; pdb.set_trace()
|
||||||
|
self.list_credentials = []
|
||||||
|
for c in self.credentials:
|
||||||
|
if str(c.id) == data.get(c.schema.type.lower()):
|
||||||
|
self.list_credentials.append(c)
|
||||||
|
return data
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
# self.org = Organization.objects.filter(
|
if not self.list_credentials:
|
||||||
# id=self.data['organization']
|
return
|
||||||
# )
|
|
||||||
# if not self.org.exists():
|
|
||||||
# return
|
|
||||||
|
|
||||||
# self.org = self.org[0]
|
did = self.list_credentials[0].subject_did
|
||||||
|
|
||||||
# if commit:
|
self.vp = issue_verifiable_presentation(
|
||||||
# url = self.org.demand_authorization()
|
vp_template: Template,
|
||||||
# if url.status_code == 200:
|
vc_list: list[str],
|
||||||
# return url.json().get('redirect_uri')
|
jwk_holder: str,
|
||||||
|
holder_did: str)
|
||||||
return
|
|
||||||
|
self.vp = issue_verifiable_presentation(
|
||||||
|
vp_template: Template,
|
||||||
|
self.list_credentials,
|
||||||
|
did.key_material,
|
||||||
|
did.did)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
result = requests.post(self.vp)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
96
oidc4vp/templates/credentials_presentation.html
Normal file
96
oidc4vp/templates/credentials_presentation.html
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
{% extends "idhub/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>
|
||||||
|
<i class="{{ icon }}"></i>
|
||||||
|
{{ subtitle }}
|
||||||
|
</h3>
|
||||||
|
{% load django_bootstrap5 %}
|
||||||
|
<form role="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% if form.errors %}
|
||||||
|
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||||
|
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||||
|
<div class="message">
|
||||||
|
{% for field, error in form.errors.items %}
|
||||||
|
{{ error }}<br />
|
||||||
|
{% endfor %}
|
||||||
|
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if form.credentials.all %}
|
||||||
|
{% for presentation in form.presentation_definition %}
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ presentation|capfirst }}</3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Type' %}</button></th>
|
||||||
|
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Details' %}</button></th>
|
||||||
|
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Issued' %}</button></th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for f in form.credentials.all %}
|
||||||
|
{% if f.schema.type.lower == presentation.lower %}
|
||||||
|
<tr style="font-size:15px;">
|
||||||
|
<td><input class="form-check-input" type="radio" value="{{ f.id }}" name="{{ presentation.lower }}"></td>
|
||||||
|
<td>{{ f.type }}</td>
|
||||||
|
<td>{{ f.description }}</td>
|
||||||
|
<td>{{ f.get_issued_on }}</td>
|
||||||
|
<td><a href="{% url 'idhub:user_credential' f.id %}" class="text-primary" title="{% trans 'View' %}"><i class="bi bi-eye"></i></a></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="form-actions-no-box mt-3">
|
||||||
|
<input class="form-check-input" type="checkbox" value="" name="consent" required="required" /> {% trans 'I read and understood the' %}
|
||||||
|
<a href="javascript:void()" data-bs-toggle="modal" data-bs-target="#legality-consent">{% trans 'data sharing notice' %}</a>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions-no-box mt-5">
|
||||||
|
<a class="btn btn-grey" href="{% url 'idhub:user_demand_authorization' %}">{% trans "Cancel" %}</a>
|
||||||
|
<input class="btn btn-green-user" type="submit" name="submit" value="{% trans 'Present' %}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="col">
|
||||||
|
<h3>{% trans 'There are not credentials for present' %}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal" id="legality-consent" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">{% trans 'Data sharing notice' %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{% trans 'Are you sure that you want delete this user?' %}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -4,6 +4,7 @@ import didkit
|
||||||
import json
|
import json
|
||||||
import jinja2
|
import jinja2
|
||||||
from django.template.backends.django import Template
|
from django.template.backends.django import Template
|
||||||
|
from django.template.loader import get_template
|
||||||
|
|
||||||
|
|
||||||
def generate_did_controller_key():
|
def generate_did_controller_key():
|
||||||
|
@ -49,34 +50,35 @@ def render_and_sign_credential(vc_template: jinja2.Template, jwk_issuer, vc_data
|
||||||
|
|
||||||
def sign_credential(unsigned_vc: str, jwk_issuer):
|
def sign_credential(unsigned_vc: str, jwk_issuer):
|
||||||
"""
|
"""
|
||||||
Signs the and unsigned credential with the provided key.
|
Signs the unsigned credential with the provided key.
|
||||||
|
The credential template must be rendered with all user data.
|
||||||
"""
|
"""
|
||||||
async def inner():
|
async def inner():
|
||||||
signed_vc = await didkit.issue_credential(
|
signed_vc = await didkit.issue_credential(
|
||||||
unsigned_vc,
|
unsigned_vc,
|
||||||
'{"proofFormat": "ldp"}',
|
'{"proofFormat": "ldp"}',
|
||||||
jwk_issuer
|
jwk_issuer
|
||||||
)
|
)
|
||||||
return signed_vc
|
return signed_vc
|
||||||
|
|
||||||
return asyncio.run(inner())
|
return asyncio.run(inner())
|
||||||
|
|
||||||
|
|
||||||
def verify_credential(vc, proof_options):
|
def verify_credential(vc):
|
||||||
"""
|
"""
|
||||||
Returns a (bool, str) tuple indicating whether the credential is valid.
|
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 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.
|
If it is false, the VC is invalid and the second argument contains a JSON object with further information.
|
||||||
"""
|
"""
|
||||||
async def inner():
|
async def inner():
|
||||||
return didkit.verify_credential(vc, proof_options)
|
return await didkit.verify_credential(vc, '{"proofFormat": "ldp"}')
|
||||||
|
|
||||||
return asyncio.run(inner())
|
return asyncio.run(inner())
|
||||||
|
|
||||||
|
|
||||||
def issue_verifiable_presentation(vc_list: list[str], jwk_holder: str, holder_did: str) -> str:
|
def issue_verifiable_presentation(vp_template: Template, vc_list: list[str], jwk_holder: str, holder_did: str) -> str:
|
||||||
async def inner():
|
async def inner():
|
||||||
unsigned_vp = unsigned_vp_template.render(data)
|
unsigned_vp = vp_template.render(data)
|
||||||
signed_vp = await didkit.issue_presentation(
|
signed_vp = await didkit.issue_presentation(
|
||||||
unsigned_vp,
|
unsigned_vp,
|
||||||
'{"proofFormat": "ldp"}',
|
'{"proofFormat": "ldp"}',
|
||||||
|
@ -84,12 +86,6 @@ def issue_verifiable_presentation(vc_list: list[str], jwk_holder: str, holder_di
|
||||||
)
|
)
|
||||||
return signed_vp
|
return signed_vp
|
||||||
|
|
||||||
# TODO: convert from Jinja2 -> django-templates
|
|
||||||
env = Environment(
|
|
||||||
loader=FileSystemLoader("vc_templates"),
|
|
||||||
autoescape=select_autoescape()
|
|
||||||
)
|
|
||||||
unsigned_vp_template = env.get_template("verifiable_presentation.json")
|
|
||||||
data = {
|
data = {
|
||||||
"holder_did": holder_did,
|
"holder_did": holder_did,
|
||||||
"verifiable_credential_list": "[" + ",".join(vc_list) + "]"
|
"verifiable_credential_list": "[" + ",".join(vc_list) + "]"
|
||||||
|
@ -106,6 +102,7 @@ def verify_presentation(vp):
|
||||||
"""
|
"""
|
||||||
async def inner():
|
async def inner():
|
||||||
proof_options = '{"proofFormat": "ldp"}'
|
proof_options = '{"proofFormat": "ldp"}'
|
||||||
return didkit.verify_presentation(vp, proof_options)
|
return await didkit.verify_presentation(vp, proof_options)
|
||||||
|
|
||||||
return asyncio.run(inner())
|
return asyncio.run(inner())
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue