Merge branch 'main' into release

This commit is contained in:
Cayo Puigdefabregas 2024-02-13 20:45:32 +01:00
commit b4058aba3d
44 changed files with 814 additions and 3938 deletions

View File

@ -50,7 +50,7 @@ jobs:
- name: Get DIDKit wheel
id: didkit
run: |
wget https://gitea.pangea.org/trustchain-oc1-orchestral/ssikit_trustchain/raw/branch/master/didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl
wget -O didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl https://gitea.pangea.org/api/v1/repos/trustchain-oc1-orchestral/ssikit_trustchain/raw/didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl?token=${{ secrets.FILE_GETTER_TOKEN }}
echo "Successfully downloaded DIDkit"
- name: Install dependencies
@ -69,3 +69,21 @@ jobs:
source venv/bin/activate
python manage.py test
deploy:
needs: test
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Trigger Remote Script
run: |
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST http://45.150.187.54:5000/trigger-script -H "Authorization: SecretToken")
if [ "$response" -ne 200 ]; then
echo "Script execution failed with HTTP status $response"
exit 1
else
echo "Script execution successful"
exit 0
fi
if: success() && github.ref == 'refs/heads/main'

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -23,7 +23,7 @@ from idhub.models import (
from idhub_auth.models import User
class TermsConditionsForm(forms.Form):
class TermsConditionsForm2(forms.Form):
accept = forms.BooleanField(
label=_("Accept terms and conditions of the service"),
required=False
@ -50,6 +50,65 @@ class TermsConditionsForm(forms.Form):
return
class TermsConditionsForm(forms.Form):
accept_privacy = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
accept_legal = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
accept_cookies = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
def get_label(self, url, read):
label = _('I read and accepted the')
label += f' <a class="btn btn-green-admin" target="_blank" href="{url}" '
label += f'title="{read}">{read}</a>'
return label
def privacy_label(self):
url = "https://laweb.pangea.org/politica-de-privacitat/"
read = _("Privacy policy")
return self.get_label(url, read)
def legal_label(self):
url = "https://laweb.pangea.org/avis-legal/"
read = _("Legal policy")
return self.get_label(url, read)
def cookies_label(self):
url = "https://laweb.pangea.org/politica-de-cookies-2/"
read = _("Cookies policy")
return self.get_label(url, read)
def clean(self):
data = self.cleaned_data
privacy = data.get("accept_privacy")
legal = data.get("accept_legal")
cookies = data.get("accept_cookies")
if privacy and legal and cookies:
self.user.accept_gdpr = True
else:
self.user.accept_gdpr = False
return data
def save(self, commit=True):
if commit:
self.user.save()
return self.user
return
class ImportForm(forms.Form):
did = forms.ChoiceField(label=_("Did"), choices=[])
eidas1 = forms.ChoiceField(
@ -189,7 +248,7 @@ class ImportForm(forms.Form):
cred = VerificableCredential(
verified=False,
user=user,
csv_data=json.dumps(row),
csv_data=json.dumps(row, default=str),
issuer_did=self._did,
schema=self._schema,
eidas1_did=self._eidas1

View File

@ -25,7 +25,7 @@ from django.contrib import messages
from utils import credtools
from idhub_auth.models import User
from idhub_auth.forms import ProfileForm
from idhub.mixins import AdminView
from idhub.mixins import AdminView, Http403
from idhub.email.views import NotifyActivateUserByEmail
from idhub.admin.forms import (
ImportForm,
@ -60,9 +60,9 @@ from idhub.models import (
class TermsAndConditionsView(AdminView, FormView):
template_name = "idhub/admin/terms_conditions.html"
title = _("GDPR")
title = _('Data protection')
section = ""
subtitle = _('Accept Terms and Conditions')
subtitle = _('Terms and Conditions')
icon = 'bi bi-file-earmark-medical'
form_class = TermsConditionsForm
success_url = reverse_lazy('idhub:admin_dashboard')
@ -70,7 +70,12 @@ class TermsAndConditionsView(AdminView, FormView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
kwargs['initial'] = {"accept": self.request.user.accept_gdpr}
if self.request.user.accept_gdpr:
kwargs['initial'] = {
"accept_privacy": True,
"accept_legal": True,
"accept_cookies": True
}
return kwargs
def form_valid(self, form):
@ -82,7 +87,9 @@ class DobleFactorAuthView(AdminView, View):
url = reverse_lazy('idhub:admin_dashboard')
def get(self, request, *args, **kwargs):
self.check_valid_user()
if not self.request.user.is_admin:
raise Http403()
if not self.request.session.get("2fauth"):
return redirect(self.url)
@ -700,12 +707,13 @@ class DidsView(Credentials, SingleTableView):
def get_context_data(self, **kwargs):
queryset = kwargs.pop('object_list', None)
dids = DID.objects.filter(user=self.request.user)
if queryset is None:
self.object_list = self.model.objects.all()
self.object_list = dids.all()
context = super().get_context_data(**kwargs)
context.update({
'dids': DID.objects.filter(user=self.request.user),
'dids': dids
})
return context
@ -905,19 +913,20 @@ class SchemasImportAddView(SchemasMix):
def get(self, request, *args, **kwargs):
self.check_valid_user()
file_name = kwargs['file_schema']
self.file_name = kwargs['file_schema']
schemas_files = os.listdir(settings.SCHEMAS_DIR)
if file_name not in schemas_files:
if self.file_name not in schemas_files:
file_name = self.file_name
messages.error(self.request, f"The schema {file_name} not exist!")
return redirect('idhub:admin_schemas_import')
schema = self.create_schema(file_name)
schema = self.create_schema()
if schema:
messages.success(self.request, _("The schema was added sucessfully"))
return redirect('idhub:admin_schemas')
def create_schema(self, file_name):
data = self.open_file(file_name)
def create_schema(self):
data = self.open_file()
try:
ldata = json.loads(data)
assert credtools.validate_schema(ldata)
@ -933,7 +942,7 @@ class SchemasImportAddView(SchemasMix):
_description = json.dumps(ldata.get('description', ''))
schema = Schemas.objects.create(
file_schema=file_name,
file_schema=self.file_name,
data=data,
type=title,
_name=_name,
@ -944,9 +953,9 @@ class SchemasImportAddView(SchemasMix):
schema.save()
return schema
def open_file(self, file_name):
def open_file(self):
data = ''
filename = Path(settings.SCHEMAS_DIR).joinpath(file_name)
filename = Path(settings.SCHEMAS_DIR).joinpath(self.file_name)
with filename.open() as schema_file:
data = schema_file.read()
@ -955,7 +964,7 @@ class SchemasImportAddView(SchemasMix):
def get_template_description(self):
context = {}
template_name = 'credentials/{}'.format(
self.schema.file_schema
self.file_name
)
tmpl = get_template(template_name)
return tmpl.render(context)
@ -970,7 +979,7 @@ class SchemasImportAddView(SchemasMix):
class ImportView(ImportExport, SingleTableView):
template_name = "idhub/admin/import.html"
table_class = DataTable
subtitle = _('Import data')
subtitle = _('Imported data')
icon = ''
model = File_datas

View File

@ -57,12 +57,13 @@ class NotifyActivateUserByEmail:
html_email = loader.render_to_string(self.html_email_template_name, context)
email_message.attach_alternative(html_email, 'text/html')
try:
if settings.DEVELOPMENT:
logger.warning(to_email)
logger.warning(body)
if settings.ENABLE_EMAIL:
email_message.send()
return
email_message.send()
logger.warning(to_email)
logger.warning(body)
except Exception as err:
logger.error(err)
return

View File

@ -23,11 +23,12 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
ADMIN_EMAIL = config('ADMIN_EMAIL', 'admin@example.org')
ADMIN_PASSWORD = config('ADMIN_PASSWORD', '1234')
USER_EMAIL = config('USER_EMAIL', 'user1@example.org')
USER_PASSWORD = config('USER_PASSWORD', '1234')
self.create_admin_users(ADMIN_EMAIL, ADMIN_PASSWORD)
self.create_users(USER_EMAIL, USER_PASSWORD)
if settings.CREATE_TEST_USERS:
for u in range(1, 6):
user = 'user{}@example.org'.format(u)
self.create_users(user, '1234')
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
ORGANIZATION = os.path.join(BASE_DIR, settings.ORG_FILE)

View File

@ -674,7 +674,6 @@ class VerificableCredential(models.Model):
'organisation': settings.ORGANIZATION or '',
}
context.update(d)
context['firstName'] = ""
return context
def render(self, domain):

View File

@ -5,14 +5,14 @@
<div class="well">
<div class="row-fluid">
<h2>{% trans 'Doble Factor of Authentication' %}</h2>
<h2>{% trans 'Two-factor Authentication' %}</h2>
</div>
</div>
<div class="well">
<div class="row-fluid">
<div>
<span>{% trans "We have sent an email with a link that you have to select in order to login." %}</span>
<span>{% trans "We have sent you an email with a link that you have to click to log in." %}</span>
</div>
</div><!-- /.row-fluid -->
</div><!--/.well-->

View File

@ -1,6 +1,6 @@
{% load i18n %}{% autoescape off %}
<p>
{% blocktrans %}You're receiving this email because you try to access in {{ site_name }}.{% endblocktrans %}
{% blocktrans %}You're receiving this email because you tried to access {{ site_name }}.{% endblocktrans %}
</p>
<p>

View File

@ -1,5 +1,5 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you try to access in {{ site_name }}.{% endblocktrans %}
{% blocktrans %}You're receiving this email because you tried to access {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page" %}
{% block reset_link %}

View File

@ -46,7 +46,7 @@
class="btn btn-primary form-control" id="submit-id-submit">
</div>
</form>
<div id="login-footer" class="mt-3">
<div id="login-footer" class="mt-3 d-none">
<a href="{% url 'idhub:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
</div>
{% endblock %}

View File

@ -2,25 +2,7 @@
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://idhub.pangea.org/credentials/base/v1",
{
"firstName": "https://idhub.pangea.org/context/#firstName",
"lastName": "https://idhub.pangea.org/context/#lastName",
"personalIdentifier": "https://idhub.pangea.org/context/#personalIdentifier",
"issuedDate": "https://idhub.pangea.org/context/#issuedDate",
"modeOfInstruction": "https://idhub.pangea.org/context/#modeOfInstruction",
"courseDuration": "https://idhub.pangea.org/context/#courseDuration",
"courseDays": "https://idhub.pangea.org/context/#courseDays",
"courseName": "https://idhub.pangea.org/context/#courseName",
"courseDescription": "https://idhub.pangea.org/context/#courseDescription",
"gradingScheme": "https://idhub.pangea.org/context/#gradingScheme",
"scoreAwarded": "https://idhub.pangea.org/context/#scoreAwarded",
"qualificationAwarded": "https://idhub.pangea.org/context/#qualificationAwarded",
"courseLevel": "https://idhub.pangea.org/context/#courseLevel",
"courseFramework": "https://idhub.pangea.org/context/#courseFramework",
"courseCredits": "https://idhub.pangea.org/context/#courseCredits",
"dateOfAssessment": "https://idhub.pangea.org/context/#dateOfAssessment",
"evidenceAssessment": "https://idhub.pangea.org/context/#evidenceAssessment"
}
"https://idhub.pangea.org/credentials/course-credential/v1"
],
"id": "{{ vc_id }}",
"type": [
@ -59,6 +41,7 @@
"id": "{{ subject_did }}",
"firstName": "{{ firstName }}",
"lastName": "{{ lastName }}",
"email": "{{ email }}",
"personalIdentifier": "{{ personalIdentifier }}",
"issuedDate": "{{ issuedDate }}",
"modeOfInstruction": "{{ modeOfInstruction }}",

View File

@ -2,14 +2,7 @@
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://idhub.pangea.org/credentials/base/v1",
{
"legalName": "https://idhub.pangea.org/context/#legalName",
"accreditedBy": "https://idhub.pangea.org/context/#accreditedBy",
"operatorNumber": "https://idhub.pangea.org/context/#operatorNumber",
"limitJurisdiction": "https://idhub.pangea.org/context/#limitJurisdiction",
"accreditedFor": "https://idhub.pangea.org/context/#accreditedFor",
"role": "https://idhub.pangea.org/context/#role"
}
"https://idhub.pangea.org/credentials/e-operator-claim/v1"
],
"id": "{{ vc_id }}",
"type": [
@ -59,7 +52,8 @@
"operatorNumber": "{{ operatorNumber }}",
"limitJurisdiction": "{{ limitJurisdiction }}",
"accreditedFor": "{{ accreditedFor }}",
"role": "{{ role }}"
"role": "{{ role }}",
"email": "{{ email }}"
},
"credentialSchema": {
"id": "https://idhub.pangea.org/vc_schemas/federation-membership.json",

View File

@ -2,26 +2,7 @@
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://idhub.pangea.org/credentials/base/v1",
{
"federation": "https://idhub.pangea.org/context/#federation",
"legalName": "https://idhub.pangea.org/context/#legalName",
"shortName": "https://idhub.pangea.org/context/#shortName",
"registrationIdentifier": "https://idhub.pangea.org/context/#registrationIdentifier",
"publicRegistry": "https://idhub.pangea.org/context/#publicRegistry",
"streetAddress": "https://idhub.pangea.org/context/#streetAddress",
"postCode": "https://idhub.pangea.org/context/#postCode",
"city": "https://idhub.pangea.org/context/#city",
"taxReference": "https://idhub.pangea.org/context/#taxReference",
"membershipType": "https://idhub.pangea.org/context/#membershipType",
"membershipStatus": "https://idhub.pangea.org/context/#membershipStatus",
"membershipId": "https://idhub.pangea.org/context/#membershipId",
"membershipSince": "https://idhub.pangea.org/context/#membershipSince",
"email": "https://idhub.pangea.org/context/#email",
"phone": "https://idhub.pangea.org/context/#phone",
"website": "https://idhub.pangea.org/context/#website",
"evidence": "https://idhub.pangea.org/context/#evidence",
"certificationDate": "https://idhub.pangea.org/context/#certificationDate"
}
"https://idhub.pangea.org/credentials/federation-membership/v1"
],
"id": "{{ vc_id }}",
"type": [

View File

@ -20,38 +20,68 @@
</div>
</div>
{% endif %}
<div class="row">
<div class="row mt-4">
<div class="col">
You must read the terms and conditions of this service and accept the
<a class="btn btn-green-admin" href="jacascript:void()" data-bs-toggle="modal" data-bs-target="#gdpr" title="{% trans 'GDPR' %}">Read GDPR</a>
{{ form.accept_privacy }}
{{ form.privacy_label|safe }}
</div>
</div>
<div class="row">
<div class="col-sm-4">
{% bootstrap_form form %}
<div class="row mt-2">
<div class="col">
{{ form.accept_legal }}
{{ form.legal_label|safe }}
</div>
</div>
<div class="form-actions-no-box">
<a class="btn btn-grey" href="{% url 'idhub:admin_dashboard' %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
<div class="row mt-2">
<div class="col">
{{ form.accept_cookies }}
{{ form.cookies_label|safe }}
</div>
</div>
<div class="form-actions-no-box mt-4">
<a href="javascript:accepts();" type="button" class="btn btn-green-admin">{% trans 'Confirm' %}</a>
</div>
</form>
<!-- Modal -->
<div class="modal" id="gdpr" 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 'GDPR info' %}</h5>
<h5 class="modal-title" id="exampleModalLabel">{% trans 'Data protection' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Here we write the info about GDPR</p>
{% blocktrans %}
<p>If you do not consent to all terms and conditons this service may not be available to
anyone in your organization.<br /><br />
If you are sure to opt out of using this service please contact <a href="mailto:suport@pangea.org">suport@pangea.org</a> to unsubscribe. If you wish to continue, you must consent to all terms and conditions.</p>
{% endblocktrans %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Close' %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'No' %}</button>
<input id="submit" class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Yes' %}" />
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block extrascript %}
<script>
$(document).ready(function() {
});
function accepts() {
var privacy = $("#id_accept_privacy").prop("checked");
var policy = $("#id_accept_legal").prop("checked");
var cookies = $("#id_accept_cookies").prop("checked");
if (privacy && policy && cookies) {
$("#submit").trigger("click");
} else {
$("#gdpr").modal("show");
}
}
</script>
{% endblock %}

View File

@ -87,8 +87,8 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if path == 'user_gdpr' %}active2{% endif %}" href="{% url 'idhub:user_gdpr' %}">
{% trans 'GDPR info' %}
<a class="nav-link {% if path == 'user_terms_and_conditions' %}active2{% endif %}" href="{% url 'idhub:user_terms_and_conditions' %}">
{% trans 'Data protection' %}
</a>
</li>
</ul>
@ -150,9 +150,12 @@
</div>
</div>
{% block script %}
<script src="{% static "js/jquery-3.3.1.slim.min.js" %}"></script>
<script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
<script src="/static/js/dashboard.js"></script>
{% block extrascript %}{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -11,7 +11,12 @@
</h3>
</div>
<div class="col text-center">
<a href="javascript:void()" type="button" class="btn btn-green-user me-3">{% trans 'ARCO Forms' %}</a>
{% if lang == 'es' %}
<a href="https://laweb.pangea.org/es/politica-de-proteccion-de-datos/acceso-a-los-formularios-arco/" target="_blank" type="button" class="btn btn-green-user me-3">{% trans 'ARCO Forms' %}</a>
{% else %}
<a href="https://laweb.pangea.org/politica-de-proteccio-de-dades/acces-als-formularis-arco/" target="_blank" type="button" class="btn btn-green-user me-3">{% trans 'ARCO Forms' %}</a>
{% endif %}
<a href="javascript:void()" type="button" class="btn btn-green-user">{% trans 'Notice of Privacy' %}</a>
</div>
</div>

View File

@ -20,38 +20,68 @@
</div>
</div>
{% endif %}
<div class="row">
<div class="row mt-4">
<div class="col">
You must read the terms and conditions of this service and accept the
<a class="btn btn-green-user" href="jacascript:void()" data-bs-toggle="modal" data-bs-target="#gdpr" title="{% trans 'GDPR' %}">Read GDPR</a>
{{ form.accept_privacy }}
{{ form.privacy_label|safe }}
</div>
</div>
<div class="row">
<div class="col-sm-4">
{% bootstrap_form form %}
<div class="row mt-2">
<div class="col">
{{ form.accept_legal }}
{{ form.legal_label|safe }}
</div>
</div>
<div class="form-actions-no-box">
<a class="btn btn-grey" href="{% url 'idhub:user_dashboard' %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-user" type="submit" name="submit" value="{% translate 'Save' %}" />
<div class="row mt-2">
<div class="col">
{{ form.accept_cookies }}
{{ form.cookies_label|safe }}
</div>
</div>
<div class="form-actions-no-box mt-4">
<a href="javascript:accepts();" type="button" class="btn btn-green-user">{% trans 'Confirm' %}</a>
</div>
</form>
<!-- Modal -->
<div class="modal" id="gdpr" 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 'GDPR info' %}</h5>
<h5 class="modal-title" id="exampleModalLabel">{% trans 'Data protection' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Here we write the info about GDPR</p>
{% blocktrans %}
<p>If you do not accept all the terms and conditions of this service you will not be able
to continue.
<br /><br />
Are you sure to opt out of using this service?</p>
{% endblocktrans %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Close' %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'No' %}</button>
<input id="submit" class="btn btn-green-user" type="submit" name="submit" value="{% translate 'Yes' %}" />
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block extrascript %}
<script>
$(document).ready(function() {
});
function accepts() {
var privacy = $("#id_accept_privacy").prop("checked");
var policy = $("#id_accept_legal").prop("checked");
var cookies = $("#id_accept_cookies").prop("checked");
if (privacy && policy && cookies) {
$("#submit").trigger("click");
} else {
$("#gdpr").modal("show");
}
}
</script>
{% endblock %}

View File

@ -19,6 +19,7 @@ from django.views.generic import RedirectView
from django.urls import path, reverse_lazy
from .views import (
LoginView,
PasswordResetView,
PasswordResetConfirmView,
serve_did,
DobleFactorSendView,
@ -34,16 +35,7 @@ urlpatterns = [
permanent=False)),
path('login/', LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('auth/password_reset/',
auth_views.PasswordResetView.as_view(
template_name='auth/password_reset.html',
email_template_name='auth/password_reset_email.txt',
html_email_template_name='auth/password_reset_email.html',
subject_template_name='auth/password_reset_subject.txt',
success_url=reverse_lazy('idhub:password_reset_done')
),
name='password_reset'
),
path('auth/password_reset/', PasswordResetView.as_view(), name='password_reset'),
path('auth/password_reset/done/',
auth_views.PasswordResetDoneView.as_view(
template_name='auth/password_reset_done.html'
@ -53,13 +45,6 @@ urlpatterns = [
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
name='password_reset_confirm'
),
# path('auth/reset/<uidb64>/<token>/',
# 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'

View File

@ -15,8 +15,16 @@ class ProfileForm(forms.ModelForm):
class TermsConditionsForm(forms.Form):
accept = forms.BooleanField(
label=_("Accept terms and conditions of the service"),
accept_privacy = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
accept_legal = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
accept_cookies = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
required=False
)
@ -24,9 +32,33 @@ class TermsConditionsForm(forms.Form):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
def get_label(self, url, read):
label = _('I read and accepted the')
label += f' <a class="btn btn-green-user" target="_blank" href="{url}" '
label += f'title="{read}">{read}</a>'
return label
def privacy_label(self):
url = "https://laweb.pangea.org/politica-de-privacitat/"
read = _("Privacy policy")
return self.get_label(url, read)
def legal_label(self):
url = "https://laweb.pangea.org/avis-legal/"
read = _("Legal policy")
return self.get_label(url, read)
def cookies_label(self):
url = "https://laweb.pangea.org/politica-de-cookies-2/"
read = _("Cookies policy")
return self.get_label(url, read)
def clean(self):
data = self.cleaned_data
if data.get("accept"):
privacy = data.get("accept_privacy")
legal = data.get("accept_legal")
cookies = data.get("accept_cookies")
if privacy and legal and cookies:
self.user.accept_gdpr = True
else:
self.user.accept_gdpr = False

View File

@ -117,6 +117,13 @@ class DIDTable(tables.Table):
class CredentialsTable(tables.Table):
description = tables.Column(verbose_name="Details", empty_values=())
view_credential = ButtonColumn(
linkify={
"viewname": "idhub:user_credential",
"args": [tables.A("pk")]
},
orderable=False
)
def render_description(self, record):
return record.get_description()
@ -131,6 +138,9 @@ class CredentialsTable(tables.Table):
return (queryset, True)
def render_view_credential(self):
return format_html('<i class="bi bi-eye"></i>')
class Meta:
model = VerificableCredential
template_name = "idhub/custom_table.html"

View File

@ -100,6 +100,13 @@ class ProfileView(MyProfile, UpdateView, SingleTableView):
def form_valid(self, form):
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'lang': self.request.LANGUAGE_CODE,
})
return context
class RolesView(MyProfile, SingleTableView):
@ -116,7 +123,7 @@ class RolesView(MyProfile, SingleTableView):
class GDPRView(MyProfile, TemplateView):
template_name = "idhub/user/gdpr.html"
subtitle = _('GDPR info')
subtitle = _('Data protection')
icon = 'bi bi-file-earmark-medical'
@ -135,9 +142,9 @@ class CredentialsView(MyWallet, SingleTableView):
class TermsAndConditionsView(UserView, FormView):
template_name = "idhub/user/terms_conditions.html"
title = _("GDPR")
title = _("Data Protection")
section = ""
subtitle = _('Accept Terms and Conditions')
subtitle = _('Terms and Conditions')
icon = 'bi bi-file-earmark-medical'
form_class = TermsConditionsForm
success_url = reverse_lazy('idhub:user_dashboard')
@ -145,7 +152,12 @@ class TermsAndConditionsView(UserView, FormView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
kwargs['initial'] = {"accept": self.request.user.accept_gdpr}
if self.request.user.accept_gdpr:
kwargs['initial'] = {
"accept_privacy": True,
"accept_legal": True,
"accept_cookies": True
}
return kwargs
def form_valid(self, form):

View File

@ -1,4 +1,5 @@
import uuid
import logging
from django.conf import settings
from django.core.cache import cache
@ -16,6 +17,9 @@ from idhub.email.views import NotifyActivateUserByEmail
from trustchain_idhub import settings
logger = logging.getLogger(__name__)
class LoginView(auth_views.LoginView):
template_name = 'auth/login.html'
extra_context = {
@ -52,7 +56,7 @@ class LoginView(auth_views.LoginView):
# )
# cache.set("KEY_DIDS", encryption_key, None)
cache.set("KEY_DIDS", sensitive_data_encryption_key, None)
if not settings.DEVELOPMENT:
if settings.ENABLE_2FACTOR_AUTH:
self.request.session["2fauth"] = str(uuid.uuid4())
return redirect(reverse_lazy('idhub:confirm_send_2f'))
@ -69,13 +73,31 @@ class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
success_url = reverse_lazy('idhub:password_reset_complete')
def form_valid(self, form):
password = form.cleaned_data.get("password")
user = form.get_user()
password = form.cleaned_data.get("new_password1")
user = form.user
user.set_password(password)
user.set_encrypted_sensitive_data(password)
user.save()
return HttpResponseRedirect(self.success_url)
class PasswordResetView(auth_views.PasswordResetView):
template_name = 'auth/password_reset.html'
email_template_name = 'auth/password_reset_email.txt'
html_email_template_name = 'auth/password_reset_email.html'
subject_template_name = 'auth/password_reset_subject.txt'
success_url = reverse_lazy('idhub:password_reset_done')
def form_valid(self, form):
try:
return super().form_valid(form)
except Exception as err:
logger.error(err)
# url_error = reverse_lazy('idhub:password_reset_error')
# return HttpResponseRedirect(url_error)
return HttpResponseRedirect(self.success_url)
def serve_did(request, did_id):
id_did = f'did:web:{settings.DOMAIN}:did-registry:{did_id}'
did = get_object_or_404(DID, did=id_did)

View File

@ -155,3 +155,11 @@ class User(AbstractBaseUser):
pw = base64.b64decode(password.encode('utf-8')*4)
sb_key = self.derive_key_from_password(pw)
return nacl.secret.SecretBox(sb_key)
def change_password(self, old_password, new_password):
sensitive_data = self.decrypt_sensitive_data(old_password)
self.encrypted_sensitive_data = self.encrypt_sensitive_data(
new_password,
sensitive_data
)
self.set_password(new_password)

View File

@ -137,8 +137,7 @@ class VerifyView(View):
vp_token.verifing()
response = vp_token.get_response_verify()
vp_token.save()
if not vp_token.authorization.promotions.exists():
response["response"] = "Validation Code {}".format(code)
response["response"] = "Validation Code {}".format(code)
return JsonResponse(response)

View File

@ -14,7 +14,10 @@ from promotion.models import Promotion
class WalletForm(forms.Form):
organization = forms.ChoiceField(choices=[])
organization = forms.ChoiceField(
choices=[],
widget=forms.Select(attrs={'class': 'form-select'})
)
def __init__(self, *args, **kwargs):
self.presentation_definition = kwargs.pop('presentation_definition', [])

File diff suppressed because one or more lines are too long

View File

@ -36,7 +36,7 @@
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
</head>
<body id="body-login">
<header class="navbar navbar-dark sticky-top bg-grey flex-md-nowrap p-0 shadow">
<header class="navbar navbar-dark sticky-top bg-grey flex-md-nowrap p-0 shadow" style="background-color: #c4a812;">
<div class="navbar-nav navbar-sub-brand">
</div>
<div class="navbar-nav">
@ -66,7 +66,7 @@
<form role="form" method="post">
{% csrf_token %}
<div class="row">
<div class="col-sm-4">
<div class="col">
{% bootstrap_form form %}
</div>
</div>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -68,9 +68,9 @@ class ContractView(FormView):
return kwargs
self.vp_token.get_user_info()
kwargs['initial']["nif"] = self.vp_token.user_info.get("nif", '')
kwargs['initial']["name"] = self.vp_token.user_info.get("name", '')
kwargs['initial']["first_last_name"] = self.vp_token.user_info.get("first_last_name", '')
kwargs['initial']["nif"] = self.vp_token.user_info.get("identityNumber", '')
kwargs['initial']["name"] = self.vp_token.user_info.get("firstName", '')
kwargs['initial']["first_last_name"] = self.vp_token.user_info.get("lastName", '')
kwargs['initial']["second_last_name"] = self.vp_token.user_info.get("second_last_name", '')
kwargs['initial']["email"] = self.vp_token.user_info.get("email", '')
kwargs['initial']["email_repeat"] = self.vp_token.user_info.get("email", '')

View File

@ -27,4 +27,6 @@ uharfbuzz==0.38.0
fontTools==4.47.0
weasyprint==60.2
ujson==5.9.0
openpyxl==3.1.2
jsonpath_ng==1.6.1
./didkit-0.3.2-cp311-cp311-manylinux_2_34_x86_64.whl

View File

@ -1,5 +1,5 @@
{
"$id": "https://idhub.pangea.org/vc_schemas/courseCredential",
"$id": "https://idhub.pangea.org/vc_schemas/course-credential.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "NGO Course Credential Schema",
"description": "A NGO Course Credential Schema awarded by a NGO federation and their NGO members, as proposed by Lafede.cat",
@ -40,6 +40,10 @@
"type": "string",
"description": "The family name of the student"
},
"email": {
"type": "string",
"format": "email"
},
"personalIdentifier": {
"type": "string",
"description": "The personal identifier of the student, such as ID number"
@ -116,6 +120,7 @@
"id",
"firstName",
"lastName",
"email",
"personalIdentifier",
"issuedDate",
"modeOfInstruction",

View File

@ -1,176 +0,0 @@
{
"$id": "https://idhub.pangea.org/vc_schemas/devicePurchase.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Purchase of an eReuse device",
"description": "A device purchase credential is a proof of purchase of a device from a seller by a buyer",
"name": [
{
"value": "Device purchase credential",
"lang": "en"
},
{
"value": "Credencial d'adquisició d'un dispositiu",
"lang": "ca_ES"
},
{
"value": "Credencial de adquisición de un dispositivo",
"lang": "es"
}
],
"type": "object",
"allOf": [
{
"$ref": "https://idhub.pangea.org/vc_schemas/ebsi/attestation.json"
},
{
"properties": {
"credentialSubject": {
"description": "Defines additional properties on credentialSubject: the purchase act, to qualify as simplified invoice (ES)",
"type": "object",
"properties": {
"id": {
"description": "Defines a unique identifier (DID) of the credential subject: the purchase act/transaction",
"type": "string"
},
"invoiceNumber": {
"description": "The invoice number of the purchase act/transaction",
"type": "string"
},
"totalAmount": {
"description": "The total amount of the transaction in local currency units: Euro by default",
"type": "string"
},
"sellerId": {
"description": "Defines a unique identifier (DID) of the seller actor",
"type": "string"
},
"sellerBusinessName": {
"description": "Business name of the credential subject in the seller role",
"type": "string"
},
"sellerName": {
"description": "Name of the credential subject in the seller role",
"type": "string"
},
"sellerSurname": {
"description": "Surname of the credential subject in the seller role, if natural person",
"type": "string"
},
"sellerEmail": {
"type": "string",
"format": "email"
},
"sellerPhoneNumber": {
"type": "string"
},
"sellerIdentityDocType": {
"description": "Type of the Identity Document of the credential subject in the seller role",
"type": "string"
},
"sellerIdentityNumber": {
"description": "Number of the Identity Document of the credential subject in the seller role",
"type": "string"
},
"buyerId": {
"description": "Defines a unique identifier (DID) of the credential subject: the buyer actor",
"type": "string"
},
"buyerBusinessName": {
"description": "Business name of the credential subject in the buyer role",
"type": "string"
},
"buyerName": {
"description": "Name of the credential subject in the buyer role",
"type": "string"
},
"buyerSurname": {
"description": "Surname of the credential subject in the buyer role, if natural person",
"type": "string"
},
"buyerEmail": {
"type": "string",
"format": "email"
},
"buyerPhoneNumber": {
"type": "string"
},
"buyerIdentityDocType": {
"description": "Type of the Identity Document of the credential subject in the buyer role",
"type": "string"
},
"buyerIdentityNumber": {
"description": "Number of the Identity Document of the credential subject in the buyer role",
"type": "string"
},
"deliveryStreetAddress": {
"description": "Postal address of the credential Subject in the buyer role",
"type": "string"
},
"deliveryPostCode": {
"description": "Postal code of the credential Subject in the buyer role",
"type": "string"
},
"deliveryCity": {
"description": "City of the credential Subject in the buyer role",
"type": "string"
},
"supplyDescription": {
"description": "Description of the product/device supplied, needed in a simplified invoice",
"type": "string"
},
"taxRate": {
"description": "Description of Tax rate (VAT) and optionally also the expression VAT included, or special circumstances such as REBU, needed in a simplified invoice",
"type": "string"
},
"deviceChassisId": {
"description": "Chassis identifier of the device",
"type": "string"
},
"devicePreciseHardwareId": {
"description": "Chassis precise hardware configuration identifier of the device",
"type": "string"
},
"depositId": {
"description": "Identifier of an economic deposit left on loan to be returned under conditions",
"type": "string"
},
"sponsorId": {
"description": "Identifier of the sponsor of this purchase that paid the economic cost of the purchase",
"type": "string"
},
"sponsorName": {
"description": "Name of the sponsor of this purchase that paid the economic cost of the purchase",
"type": "string"
},
"purchaseDate": {
"type": "string",
"format": "date-time"
},
"invoiceDate": {
"type": "string",
"format": "date-time"
}
},
"required": [
"id",
"invoiceNumber",
"totalAmount",
"sellerId",
"sellerName",
"sellerBusinessName",
"sellerSurname",
"sellerEmail",
"sellerIdentityDocType",
"sellerIdentityNumber",
"buyerId",
"buyerEmail",
"supplyDescription",
"taxRate",
"deviceChassisId",
"purchaseDate"
]
}
}
}
]
}

View File

@ -1,5 +1,5 @@
{
"$id": "https://idhub.pangea.org/vc_schemas/eOperatorClaim.json",
"$id": "https://idhub.pangea.org/vc_schemas/e-operator-claim.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "EOperatorClaim",
"description": "Product and waste electronics operator claim, as proposed by eReuse.org",

View File

@ -1,5 +1,5 @@
{
"$id": "https://idhub.pangea.org/vc_schemas/federationMembership.json",
"$id": "https://idhub.pangea.org/vc_schemas/federation-membership.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Federation membership",
"description": "The federation membership specifies participation of a NGO into a NGO federation, as proposed by Lafede.cat",
@ -113,6 +113,7 @@
"membershipStatus",
"federation",
"membershipSince",
"email",
"certificationDate"
]
}

View File

@ -31,7 +31,6 @@ SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=False, cast=bool)
DEVELOPMENT = config('DEVELOPMENT', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv())
CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='', cast=Csv())
@ -226,3 +225,7 @@ LOGGING = {
ORGANIZATION = config('ORGANIZATION', 'Pangea')
SYNC_ORG_DEV = config('SYNC_ORG_DEV', 'y')
ORG_FILE = config('ORG_FILE', 'examples/organizations.csv')
ENABLE_EMAIL = config('ENABLE_EMAIL', default=True, cast=bool)
CREATE_TEST_USERS = config('CREATE_TEST_USERS', default=False, cast=bool)
ENABLE_2FACTOR_AUTH = config('ENABLE_2FACTOR_AUTH', default=True, cast=bool)

View File

@ -1,21 +1,24 @@
import pandas as pd
import json
#import jsonld
# import jsonld
import csv
import sys
import jsonschema
from pyld import jsonld
#from jsonschema import validate, ValidationError
# from jsonschema import validate, ValidationError
import requests
from pyld import jsonld
import jsonref
from jsonpath_ng import jsonpath, parse
#def remove_null_values(dictionary):
# def remove_null_values(dictionary):
# return {k: v for k, v in dictionary.items() if v is not None}
def _remove_null_values(dictionary):
filtered = {k: v for k, v in dictionary.items() if v is not None and v != ''}
dictionary.clear()
dictionary.update(filtered)
filtered = {k: v for k, v in dictionary.items() if v is not None and v != ''}
dictionary.clear()
dictionary.update(filtered)
def validate_context(jsld):
"""Validate a @context string through expanding"""
@ -23,152 +26,262 @@ def validate_context(jsld):
# schema = jsld["credentialSchema"]
# Validate the context
try:
jsonld.expand(context)
print("Context is valid")
jsonld.expand(context)
print("Context is valid")
except jsonld.JsonLdError:
print("Context is not valid")
return False
print("Context is not valid")
return False
return True
def compact_js(doc, context):
"""Validate a @context string through compacting, returns compacted context"""
try:
compacted = jsonld.compact(doc, context)
print(json.dumps(compacted, indent=2))
compacted = jsonld.compact(doc, context)
print(json.dumps(compacted, indent=2))
except jsonld.JsonLdError as e:
print(f"Error compacting document: {e}")
return None
print(f"Error compacting document: {e}")
return None
return compacted
def dereference_context_file(json_file):
"""Dereference and return json-ld context from file"""
json_text = open(json_file).read()
json_dict = json.loads(json_text)
return dereference_context(json_dict)
"""Dereference and return json-ld context from file"""
json_text = open(json_file).read()
json_dict = json.loads(json_text)
return dereference_context(json_dict)
def dereference_context(jsonld_dict):
"""Dereference and return json-ld context"""
try:
# Extract the context from the parsed JSON-LD
context_urls = jsonld_dict.get('@context')
if not context_urls:
raise ValueError("No context found in the JSON-LD string.")
return None
# Dereference each context URL
dereferenced_contexts = []
for context_url in context_urls:
response = requests.get(context_url)
response.raise_for_status() # Raise an exception if the request failed
context_dict = response.json()
dereferenced_context = jsonref.loads(json.dumps(context_dict))
dereferenced_contexts.append(dereferenced_context)
print(f"dereferenced contexts:\n", json.dumps(dereferenced_contexts, indent=4))
return dereferenced_contexts
"""Dereference and return json-ld context"""
try:
# Extract the context from the parsed JSON-LD
context_urls = jsonld_dict.get('@context')
if not context_urls:
raise ValueError("No context found in the JSON-LD string.")
return None
except (json.JSONDecodeError, requests.RequestException, jsonref.JsonRefError) as e:
print(f"An error occurred: {e}")
return None
# Dereference each context URL
dereferenced_contexts = []
for context_url in context_urls:
response = requests.get(context_url)
response.raise_for_status() # Raise an exception if the request failed
context_dict = response.json()
dereferenced_context = jsonref.loads(json.dumps(context_dict))
dereferenced_contexts.append(dereferenced_context)
print(f"dereferenced contexts:\n", json.dumps(dereferenced_contexts, indent=4))
return dereferenced_contexts
except (json.JSONDecodeError, requests.RequestException, jsonref.JsonRefError) as e:
print(f"An error occurred: {e}")
return None
def validate_schema_file(json_schema_file):
"""Validate standalone schema from file"""
try:
json_schema = open(json_schema_file).read()
validate_schema(json_schema)
except Exception as e:
print(f"Error loading file {json_schema_file} or validating schema {json_schema}: {e}")
return False
return True
"""Validate standalone schema from file"""
try:
json_schema = json.loads(open(json_schema_file).read())
validate_schema(json_schema)
except Exception as e:
print(f"Error loading file {json_schema_file} or validating schema {json_schema}: {e}")
return False
return True
def validate_schema(json_schema):
"""Validate standalone schema, returns bool (uses Draft202012Validator, alt: Draft7Validator, alt: Draft4Validator, Draft6Validator )"""
try:
jsonschema.validators.Draft202012Validator.check_schema(json_schema)
# jsonschema.validators.Draft7Validator.check_schema(json_schema)
return True
except jsonschema.exceptions.SchemaError as e:
print(e)
return False
"""Validate standalone schema, returns bool (uses Draft202012Validator, alt: Draft7Validator, alt: Draft4Validator, Draft6Validator )"""
try:
jsonschema.validators.Draft202012Validator.check_schema(json_schema)
# jsonschema.validators.Draft7Validator.check_schema(json_schema)
except jsonschema.exceptions.SchemaError as e:
print(e)
return False
return True
def validate_json_file(json_data_file, json_schema_file):
"""Validate standalone schema from file"""
try:
json_data = json.loads(open(json_data_file).read())
json_schema = json.loads(open(json_schema_file).read())
validate_json(json_data, json_schema)
except Exception as e:
print(f"Error loading file {json_schema_file} or {json_data_file}: {e}")
return False
return True
def validate_json(json_data, json_schema):
"""Validate json string basic (no format) with schema, returns bool"""
try:
jsonschema.validate(instance=json_data, schema=json_schema)
except jsonschema.exceptions.ValidationError as err:
print('Validation error: ', json_data, '\n')
return False
return True
"""Validate json string basic (no format) with schema, returns bool"""
try:
jsonschema.validate(instance=json_data, schema=json_schema)
except jsonschema.exceptions.ValidationError as err:
print('Validation error: ', json_data, '\n')
return False
print("Successful validation")
return True
def validate_json_format(json_data, json_schema):
"""Validate a json string basic (including format) with schema, returns bool"""
try:
jsonschema.validate(instance=json_data, schema=json_schema, format_checker=FormatChecker())
except jsonschema.exceptions.ValidationError as err:
print('Validation error: ', json_data, '\n')
return False
return True
"""Validate a json string basic (including format) with schema, returns bool"""
try:
jsonschema.validate(instance=json_data, schema=json_schema, format_checker=FormatChecker())
except jsonschema.exceptions.ValidationError as err:
print('Validation error: ', json_data, '\n')
return False
return True
def schema_to_csv_file(sch_f, csv_f):
try:
json_schema = json.loads(open(sch_f).read())
except Exception as e:
print(f"Error loading file {sch_f}: {e}\nSchema:\n{json_schema}.")
return False
schema_to_csv(json_schema, csv_f)
return True
def schema_to_csv(schema, csv_file_path):
"""Extract headers from an schema and write to file, returns bool"""
headers = list(schema['properties'].keys())
"""Extract headers from an schema and write to file, returns bool"""
jsonpath_expr = parse('$..credentialSubject.properties')
# Use the JSONPath expression to select all properties under 'credentialSubject.properties'
matches = [match.value for match in jsonpath_expr.find(schema)]
# Get the keys of the matched objects
# headers = [match.keys() for match in matches]
# Use the JSONPath expression to select all properties under 'credentialSubject.properties'
# Create a CSV file with the headers
with open(csv_file_path, 'w', newline='') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(headers)
return True
# Get the keys of the matched objects
headers = [key for match in matches for key in match.keys()]
# print('\nHeaders: ', headers)
# Create a CSV file with the headers
with open(csv_file_path, 'w', newline='') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(headers)
return True
def schema_to_xls_basic(schema, xls_file_path):
"""Extract headers from an schema and write to file, returns bool"""
jsonpath_expr = parse('$..credentialSubject.properties')
# Use the JSONPath expression to select all properties under 'credentialSubject.properties'
matches = [match.value for match in jsonpath_expr.find(schema)]
# Get the keys of the matched objects
# headers = [match.keys() for match in matches]
# Get the keys of the matched objects
headers = [key for match in matches for key in match.keys() if key != 'id']
# Create a DataFrame with the fields as columns
df = pd.DataFrame(columns=headers)
# Save the DataFrame as an Excel file
# df.to_excel(xls_file_path, index=False)
df.to_excel(xls_file_path, index=False, engine='openpyxl') # For .xlsx files, and pip install openpyxl
return True
def schema_to_xls_comment(schema, xls_file_path):
"""Extract headers from an schema and write to file, returns bool"""
jsonpath_expr = parse('$..credentialSubject.properties')
# Use the JSONPath expression to select all properties under 'credentialSubject.properties'
matches = [match.value for match in jsonpath_expr.find(schema)]
# Get the keys of the matched objects
# headers = [match.keys() for match in matches]
# Get the keys of the matched objects
headers = [key for match in matches for key in match.keys() if key != 'id']
jsonpath_expr_req = parse('$..credentialSubject.required')
req = [match.value for match in jsonpath_expr_req.find(schema)][0]
# Create a DataFrame with the fields as columns
df = pd.DataFrame(columns=headers)
writer = pd.ExcelWriter(xls_file_path, engine='xlsxwriter')
# Convert the dataframe to an xlsxwriter Excel object
df.to_excel(writer, sheet_name='Full1', index=False)
# Get the xlsxwriter workbook and worksheet objects
workbook = writer.book
worksheet = writer.sheets['Full1']
# Define a format for the required header cells
req_format = workbook.add_format({'border': 1})
# cell_format = workbook.add_format({'bold': True, 'font_color': 'red'})
# Write comments to the cells
for i, header in enumerate(headers):
if header in req:
worksheet.set_column(i,i, None, req_format)
# Get the description for the current field
if 'description' in matches[0][header]:
description = matches[0][header]['description']
if description is not None:
# Write the description as a comment to the corresponding cell
worksheet.write_comment(0, i, description)
# Close the Pandas Excel writer and output the Excel file
worksheet.autofit()
writer.close()
return True
def csv_to_json(csvFilePath, schema, jsonFilePath):
"""Read from a csv file, check schema, write to json file, returns bool"""
jsonArray = []
# Read CSV file
with open(csvFilePath, 'r') as csvf:
# Load CSV file data using csv library's dictionary reader
csvReader = csv.DictReader(csvf)
"""Read from a csv file, check schema, write to json file, returns bool"""
jsonArray = []
# Read CSV file
with open(csvFilePath, 'r') as csvf:
# Load CSV file data using csv library's dictionary reader
csvReader = csv.DictReader(csvf)
# Convert each CSV row into python dict and validate against schema
for row in csvReader:
_remove_null_values(row)
print('Row: ', row, '\n')
validate_json(row, schema)
# Add this python dict to json array
jsonArray.append(row)
# Convert python jsonArray to JSON String and write to file
with open(jsonFilePath, 'w', encoding='utf-8') as jsonf:
jsonString = json.dumps(jsonArray, indent=4)
jsonf.write(jsonString)
return True
# Convert each CSV row into python dict and validate against schema
for row in csvReader:
_remove_null_values(row)
print('Row: ', row, '\n')
validate_json(row, schema)
# Add this python dict to json array
jsonArray.append(row)
# Convert python jsonArray to JSON String and write to file
with open(jsonFilePath, 'w', encoding='utf-8') as jsonf:
jsonString = json.dumps(jsonArray, indent=4)
jsonf.write(jsonString)
return True
def csv_to_json2(csv_file_path, json_file_path):
"""Read from a csv file, write to json file (assumes a row 'No' is primary key), returns bool EXPERIMENT"""
# Create a dictionary
data = {}
"""Read from a csv file, write to json file (assumes a row 'No' is primary key), returns bool EXPERIMENT"""
# Create a dictionary
data = {}
# Open a csv reader called DictReader
with open(csv_file_path, encoding='utf-8') as csvf:
csvReader = csv.DictReader(csvf)
# Open a csv reader called DictReader
with open(csv_file_path, encoding='utf-8') as csvf:
csvReader = csv.DictReader(csvf)
# Convert each row into a dictionary and add it to data
for rows in csvReader:
# Assuming a column named 'No' to be the primary key
key = rows['No']
data[key] = rows
# Convert each row into a dictionary and add it to data
for rows in csvReader:
# Assuming a column named 'No' to be the primary key
key = rows['No']
data[key] = rows
# Open a json writer, and use the json.dumps() function to dump data
with open(json_file_path, 'w', encoding='utf-8') as jsonf:
jsonf.write(json.dumps(data, indent=4))
return True
# Open a json writer, and use the json.dumps() function to dump data
with open(json_file_path, 'w', encoding='utf-8') as jsonf:
jsonf.write(json.dumps(data, indent=4))
return True
if __name__ == "__main__":
sch_name = sys.argv[1]
sch_file = sch_name + '-schema.json'
sch = json.loads(open(sch_file).read())
if validate_json(d, sch):
generate_csv_from_schema(sch, sch_name + '-template.csv')
else:
print("Validation error: ", sch_name)
# sch_name = sys.argv[1]
schemas = sys.argv[1:]
# credtools.py course-credential device-purchase e-operator-claim federation-membership financial-vulnerability membership-card
#sch_name = 'e-operator-claim'
for i, schema in enumerate(schemas):
print(schema)
sch = json.loads(open('vc_schemas/' + schema + '.json').read())
if schema_to_xls_comment(sch,'vc_excel/' + schema + '.xlsx'):
print('Success')
else:
print("Validation error: ", schema)