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.db import migrations, models
|
||||
|
@ -148,7 +148,6 @@ class Migration(migrations.Migration):
|
|||
('verified', models.BooleanField()),
|
||||
('created_on', models.DateTimeField(auto_now=True)),
|
||||
('issued_on', models.DateTimeField(null=True)),
|
||||
('subject_did', models.CharField(max_length=250)),
|
||||
('data', models.TextField()),
|
||||
('csv_data', models.TextField()),
|
||||
(
|
||||
|
@ -179,6 +178,14 @@ class Migration(migrations.Migration):
|
|||
to='idhub.schemas',
|
||||
),
|
||||
),
|
||||
(
|
||||
'subject_did',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='subject_credentials',
|
||||
to='idhub.did',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
|
|
|
@ -463,7 +463,6 @@ class VerificableCredential(models.Model):
|
|||
verified = models.BooleanField()
|
||||
created_on = models.DateTimeField(auto_now=True)
|
||||
issued_on = models.DateTimeField(null=True)
|
||||
subject_did = models.CharField(max_length=250)
|
||||
data = models.TextField()
|
||||
csv_data = models.TextField()
|
||||
status = models.PositiveSmallIntegerField(
|
||||
|
@ -475,6 +474,11 @@ class VerificableCredential(models.Model):
|
|||
on_delete=models.CASCADE,
|
||||
related_name='vcredentials',
|
||||
)
|
||||
subject_did = models.ForeignKey(
|
||||
DID,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='subject_credentials',
|
||||
)
|
||||
issuer_did = models.ForeignKey(
|
||||
DID,
|
||||
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
|
||||
|
||||
|
|
|
@ -1,54 +1,13 @@
|
|||
from django import forms
|
||||
from django.conf import settings
|
||||
|
||||
from utils.idhub_ssikit import issue_verifiable_presentation
|
||||
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):
|
||||
# organization = forms.ChoiceField(choices=[])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# import pdb; pdb.set_trace()
|
||||
self.data = kwargs.get('data', {}).copy()
|
||||
self.user = kwargs.pop('user', None)
|
||||
self.presentation_definition = kwargs.pop('presentation_definition', [])
|
||||
|
@ -69,20 +28,36 @@ class AuthorizeForm(forms.Form):
|
|||
widget=forms.RadioSelect,
|
||||
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):
|
||||
# self.org = Organization.objects.filter(
|
||||
# id=self.data['organization']
|
||||
# )
|
||||
# if not self.org.exists():
|
||||
# return
|
||||
if not self.list_credentials:
|
||||
return
|
||||
|
||||
# self.org = self.org[0]
|
||||
did = self.list_credentials[0].subject_did
|
||||
|
||||
# if commit:
|
||||
# url = self.org.demand_authorization()
|
||||
# if url.status_code == 200:
|
||||
# return url.json().get('redirect_uri')
|
||||
|
||||
return
|
||||
self.vp = issue_verifiable_presentation(
|
||||
vp_template: Template,
|
||||
vc_list: list[str],
|
||||
jwk_holder: str,
|
||||
holder_did: str)
|
||||
|
||||
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 jinja2
|
||||
from django.template.backends.django import Template
|
||||
from django.template.loader import get_template
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
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():
|
||||
signed_vc = await didkit.issue_credential(
|
||||
unsigned_vc,
|
||||
'{"proofFormat": "ldp"}',
|
||||
jwk_issuer
|
||||
)
|
||||
return signed_vc
|
||||
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):
|
||||
def verify_credential(vc):
|
||||
"""
|
||||
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 await didkit.verify_credential(vc, '{"proofFormat": "ldp"}')
|
||||
|
||||
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():
|
||||
unsigned_vp = unsigned_vp_template.render(data)
|
||||
unsigned_vp = vp_template.render(data)
|
||||
signed_vp = await didkit.issue_presentation(
|
||||
unsigned_vp,
|
||||
'{"proofFormat": "ldp"}',
|
||||
|
@ -84,12 +86,6 @@ def issue_verifiable_presentation(vc_list: list[str], jwk_holder: str, holder_di
|
|||
)
|
||||
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 = {
|
||||
"holder_did": holder_did,
|
||||
"verifiable_credential_list": "[" + ",".join(vc_list) + "]"
|
||||
|
@ -106,6 +102,7 @@ def verify_presentation(vp):
|
|||
"""
|
||||
async def inner():
|
||||
proof_options = '{"proofFormat": "ldp"}'
|
||||
return didkit.verify_presentation(vp, proof_options)
|
||||
return await didkit.verify_presentation(vp, proof_options)
|
||||
|
||||
return asyncio.run(inner())
|
||||
|
||||
|
|
Loading…
Reference in a new issue