Merge branch 'main' into release
This commit is contained in:
commit
b4058aba3d
|
@ -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.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -674,7 +674,6 @@ class VerificableCredential(models.Model):
|
|||
'organisation': settings.ORGANIZATION or '',
|
||||
}
|
||||
context.update(d)
|
||||
context['firstName'] = ""
|
||||
return context
|
||||
|
||||
def render(self, domain):
|
||||
|
|
|
@ -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-->
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 }}",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -101,6 +101,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):
|
||||
template_name = "idhub/user/roles.html"
|
||||
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -137,7 +137,6 @@ 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)
|
||||
|
||||
return JsonResponse(response)
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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", '')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import pandas as pd
|
||||
import json
|
||||
# import jsonld
|
||||
import csv
|
||||
import sys
|
||||
import jsonschema
|
||||
from pyld import jsonld
|
||||
# from jsonschema import validate, ValidationError
|
||||
import requests
|
||||
from pyld import jsonld
|
||||
import jsonref
|
||||
from jsonpath_ng import jsonpath, parse
|
||||
|
||||
|
||||
# def remove_null_values(dictionary):
|
||||
# return {k: v for k, v in dictionary.items() if v is not None}
|
||||
|
@ -17,6 +19,7 @@ def _remove_null_values(dictionary):
|
|||
dictionary.clear()
|
||||
dictionary.update(filtered)
|
||||
|
||||
|
||||
def validate_context(jsld):
|
||||
"""Validate a @context string through expanding"""
|
||||
context = jsld["@context"]
|
||||
|
@ -30,6 +33,7 @@ def validate_context(jsld):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
def compact_js(doc, context):
|
||||
"""Validate a @context string through compacting, returns compacted context"""
|
||||
try:
|
||||
|
@ -40,6 +44,7 @@ def compact_js(doc, context):
|
|||
return None
|
||||
return compacted
|
||||
|
||||
|
||||
def dereference_context_file(json_file):
|
||||
"""Dereference and return json-ld context from file"""
|
||||
json_text = open(json_file).read()
|
||||
|
@ -76,22 +81,36 @@ def dereference_context(jsonld_dict):
|
|||
def validate_schema_file(json_schema_file):
|
||||
"""Validate standalone schema from file"""
|
||||
try:
|
||||
json_schema = open(json_schema_file).read()
|
||||
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
|
||||
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"""
|
||||
|
@ -100,8 +119,10 @@ def validate_json(json_data, 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:
|
||||
|
@ -111,9 +132,29 @@ def validate_json_format(json_data, json_schema):
|
|||
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())
|
||||
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'
|
||||
|
||||
# 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:
|
||||
|
@ -121,6 +162,70 @@ def schema_to_csv(schema, csv_file_path):
|
|||
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"""
|
||||
|
@ -144,6 +249,7 @@ def csv_to_json(csvFilePath, schema, jsonFilePath):
|
|||
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
|
||||
|
@ -164,11 +270,18 @@ def csv_to_json2(csv_file_path, json_file_path):
|
|||
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')
|
||||
# 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: ", sch_name)
|
||||
print("Validation error: ", schema)
|
||||
|
|
Loading…
Reference in New Issue