Merge branch 'main' into release
This commit is contained in:
commit
b4058aba3d
|
@ -50,7 +50,7 @@ jobs:
|
||||||
- name: Get DIDKit wheel
|
- name: Get DIDKit wheel
|
||||||
id: didkit
|
id: didkit
|
||||||
run: |
|
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"
|
echo "Successfully downloaded DIDkit"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
@ -69,3 +69,21 @@ jobs:
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
python manage.py test
|
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'
|
||||||
|
|
BIN
examples/excel_templates/course-credential.xlsx
Normal file
BIN
examples/excel_templates/course-credential.xlsx
Normal file
Binary file not shown.
BIN
examples/excel_templates/device-purchase.xlsx
Normal file
BIN
examples/excel_templates/device-purchase.xlsx
Normal file
Binary file not shown.
BIN
examples/excel_templates/e-operator-claim.xlsx
Normal file
BIN
examples/excel_templates/e-operator-claim.xlsx
Normal file
Binary file not shown.
BIN
examples/excel_templates/federation-membership.xlsx
Normal file
BIN
examples/excel_templates/federation-membership.xlsx
Normal file
Binary file not shown.
BIN
examples/excel_templates/financial-vulnerability.xlsx
Normal file
BIN
examples/excel_templates/financial-vulnerability.xlsx
Normal file
Binary file not shown.
BIN
examples/excel_templates/membership-card.xlsx
Normal file
BIN
examples/excel_templates/membership-card.xlsx
Normal file
Binary file not shown.
BIN
examples/financial-vulnerability.xlsx
Normal file
BIN
examples/financial-vulnerability.xlsx
Normal file
Binary file not shown.
|
@ -23,7 +23,7 @@ from idhub.models import (
|
||||||
from idhub_auth.models import User
|
from idhub_auth.models import User
|
||||||
|
|
||||||
|
|
||||||
class TermsConditionsForm(forms.Form):
|
class TermsConditionsForm2(forms.Form):
|
||||||
accept = forms.BooleanField(
|
accept = forms.BooleanField(
|
||||||
label=_("Accept terms and conditions of the service"),
|
label=_("Accept terms and conditions of the service"),
|
||||||
required=False
|
required=False
|
||||||
|
@ -50,6 +50,65 @@ class TermsConditionsForm(forms.Form):
|
||||||
return
|
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):
|
class ImportForm(forms.Form):
|
||||||
did = forms.ChoiceField(label=_("Did"), choices=[])
|
did = forms.ChoiceField(label=_("Did"), choices=[])
|
||||||
eidas1 = forms.ChoiceField(
|
eidas1 = forms.ChoiceField(
|
||||||
|
@ -189,7 +248,7 @@ class ImportForm(forms.Form):
|
||||||
cred = VerificableCredential(
|
cred = VerificableCredential(
|
||||||
verified=False,
|
verified=False,
|
||||||
user=user,
|
user=user,
|
||||||
csv_data=json.dumps(row),
|
csv_data=json.dumps(row, default=str),
|
||||||
issuer_did=self._did,
|
issuer_did=self._did,
|
||||||
schema=self._schema,
|
schema=self._schema,
|
||||||
eidas1_did=self._eidas1
|
eidas1_did=self._eidas1
|
||||||
|
|
|
@ -25,7 +25,7 @@ from django.contrib import messages
|
||||||
from utils import credtools
|
from utils import credtools
|
||||||
from idhub_auth.models import User
|
from idhub_auth.models import User
|
||||||
from idhub_auth.forms import ProfileForm
|
from idhub_auth.forms import ProfileForm
|
||||||
from idhub.mixins import AdminView
|
from idhub.mixins import AdminView, Http403
|
||||||
from idhub.email.views import NotifyActivateUserByEmail
|
from idhub.email.views import NotifyActivateUserByEmail
|
||||||
from idhub.admin.forms import (
|
from idhub.admin.forms import (
|
||||||
ImportForm,
|
ImportForm,
|
||||||
|
@ -60,9 +60,9 @@ from idhub.models import (
|
||||||
|
|
||||||
class TermsAndConditionsView(AdminView, FormView):
|
class TermsAndConditionsView(AdminView, FormView):
|
||||||
template_name = "idhub/admin/terms_conditions.html"
|
template_name = "idhub/admin/terms_conditions.html"
|
||||||
title = _("GDPR")
|
title = _('Data protection')
|
||||||
section = ""
|
section = ""
|
||||||
subtitle = _('Accept Terms and Conditions')
|
subtitle = _('Terms and Conditions')
|
||||||
icon = 'bi bi-file-earmark-medical'
|
icon = 'bi bi-file-earmark-medical'
|
||||||
form_class = TermsConditionsForm
|
form_class = TermsConditionsForm
|
||||||
success_url = reverse_lazy('idhub:admin_dashboard')
|
success_url = reverse_lazy('idhub:admin_dashboard')
|
||||||
|
@ -70,7 +70,12 @@ class TermsAndConditionsView(AdminView, FormView):
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['user'] = self.request.user
|
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
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -82,7 +87,9 @@ class DobleFactorAuthView(AdminView, View):
|
||||||
url = reverse_lazy('idhub:admin_dashboard')
|
url = reverse_lazy('idhub:admin_dashboard')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
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"):
|
if not self.request.session.get("2fauth"):
|
||||||
return redirect(self.url)
|
return redirect(self.url)
|
||||||
|
|
||||||
|
@ -700,12 +707,13 @@ class DidsView(Credentials, SingleTableView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
queryset = kwargs.pop('object_list', None)
|
queryset = kwargs.pop('object_list', None)
|
||||||
|
dids = DID.objects.filter(user=self.request.user)
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
self.object_list = self.model.objects.all()
|
self.object_list = dids.all()
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context.update({
|
context.update({
|
||||||
'dids': DID.objects.filter(user=self.request.user),
|
'dids': dids
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -905,19 +913,20 @@ class SchemasImportAddView(SchemasMix):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.check_valid_user()
|
self.check_valid_user()
|
||||||
file_name = kwargs['file_schema']
|
self.file_name = kwargs['file_schema']
|
||||||
schemas_files = os.listdir(settings.SCHEMAS_DIR)
|
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!")
|
messages.error(self.request, f"The schema {file_name} not exist!")
|
||||||
return redirect('idhub:admin_schemas_import')
|
return redirect('idhub:admin_schemas_import')
|
||||||
|
|
||||||
schema = self.create_schema(file_name)
|
schema = self.create_schema()
|
||||||
if schema:
|
if schema:
|
||||||
messages.success(self.request, _("The schema was added sucessfully"))
|
messages.success(self.request, _("The schema was added sucessfully"))
|
||||||
return redirect('idhub:admin_schemas')
|
return redirect('idhub:admin_schemas')
|
||||||
|
|
||||||
def create_schema(self, file_name):
|
def create_schema(self):
|
||||||
data = self.open_file(file_name)
|
data = self.open_file()
|
||||||
try:
|
try:
|
||||||
ldata = json.loads(data)
|
ldata = json.loads(data)
|
||||||
assert credtools.validate_schema(ldata)
|
assert credtools.validate_schema(ldata)
|
||||||
|
@ -933,7 +942,7 @@ class SchemasImportAddView(SchemasMix):
|
||||||
_description = json.dumps(ldata.get('description', ''))
|
_description = json.dumps(ldata.get('description', ''))
|
||||||
|
|
||||||
schema = Schemas.objects.create(
|
schema = Schemas.objects.create(
|
||||||
file_schema=file_name,
|
file_schema=self.file_name,
|
||||||
data=data,
|
data=data,
|
||||||
type=title,
|
type=title,
|
||||||
_name=_name,
|
_name=_name,
|
||||||
|
@ -944,9 +953,9 @@ class SchemasImportAddView(SchemasMix):
|
||||||
schema.save()
|
schema.save()
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
def open_file(self, file_name):
|
def open_file(self):
|
||||||
data = ''
|
data = ''
|
||||||
filename = Path(settings.SCHEMAS_DIR).joinpath(file_name)
|
filename = Path(settings.SCHEMAS_DIR).joinpath(self.file_name)
|
||||||
with filename.open() as schema_file:
|
with filename.open() as schema_file:
|
||||||
data = schema_file.read()
|
data = schema_file.read()
|
||||||
|
|
||||||
|
@ -955,7 +964,7 @@ class SchemasImportAddView(SchemasMix):
|
||||||
def get_template_description(self):
|
def get_template_description(self):
|
||||||
context = {}
|
context = {}
|
||||||
template_name = 'credentials/{}'.format(
|
template_name = 'credentials/{}'.format(
|
||||||
self.schema.file_schema
|
self.file_name
|
||||||
)
|
)
|
||||||
tmpl = get_template(template_name)
|
tmpl = get_template(template_name)
|
||||||
return tmpl.render(context)
|
return tmpl.render(context)
|
||||||
|
@ -970,7 +979,7 @@ class SchemasImportAddView(SchemasMix):
|
||||||
class ImportView(ImportExport, SingleTableView):
|
class ImportView(ImportExport, SingleTableView):
|
||||||
template_name = "idhub/admin/import.html"
|
template_name = "idhub/admin/import.html"
|
||||||
table_class = DataTable
|
table_class = DataTable
|
||||||
subtitle = _('Import data')
|
subtitle = _('Imported data')
|
||||||
icon = ''
|
icon = ''
|
||||||
model = File_datas
|
model = File_datas
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,13 @@ class NotifyActivateUserByEmail:
|
||||||
html_email = loader.render_to_string(self.html_email_template_name, context)
|
html_email = loader.render_to_string(self.html_email_template_name, context)
|
||||||
email_message.attach_alternative(html_email, 'text/html')
|
email_message.attach_alternative(html_email, 'text/html')
|
||||||
try:
|
try:
|
||||||
if settings.DEVELOPMENT:
|
if settings.ENABLE_EMAIL:
|
||||||
logger.warning(to_email)
|
email_message.send()
|
||||||
logger.warning(body)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
email_message.send()
|
logger.warning(to_email)
|
||||||
|
logger.warning(body)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -23,11 +23,12 @@ class Command(BaseCommand):
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
ADMIN_EMAIL = config('ADMIN_EMAIL', 'admin@example.org')
|
ADMIN_EMAIL = config('ADMIN_EMAIL', 'admin@example.org')
|
||||||
ADMIN_PASSWORD = config('ADMIN_PASSWORD', '1234')
|
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_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
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
|
||||||
ORGANIZATION = os.path.join(BASE_DIR, settings.ORG_FILE)
|
ORGANIZATION = os.path.join(BASE_DIR, settings.ORG_FILE)
|
||||||
|
|
|
@ -674,7 +674,6 @@ class VerificableCredential(models.Model):
|
||||||
'organisation': settings.ORGANIZATION or '',
|
'organisation': settings.ORGANIZATION or '',
|
||||||
}
|
}
|
||||||
context.update(d)
|
context.update(d)
|
||||||
context['firstName'] = ""
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def render(self, domain):
|
def render(self, domain):
|
||||||
|
|
|
@ -5,14 +5,14 @@
|
||||||
|
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<h2>{% trans 'Doble Factor of Authentication' %}</h2>
|
<h2>{% trans 'Two-factor Authentication' %}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div>
|
<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>
|
||||||
</div><!-- /.row-fluid -->
|
</div><!-- /.row-fluid -->
|
||||||
</div><!--/.well-->
|
</div><!--/.well-->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% load i18n %}{% autoescape off %}
|
{% load i18n %}{% autoescape off %}
|
||||||
<p>
|
<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>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% load i18n %}{% autoescape off %}
|
{% 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" %}
|
{% trans "Please go to the following page" %}
|
||||||
{% block reset_link %}
|
{% block reset_link %}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
class="btn btn-primary form-control" id="submit-id-submit">
|
class="btn btn-primary form-control" id="submit-id-submit">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
<a href="{% url 'idhub:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,25 +2,7 @@
|
||||||
"@context": [
|
"@context": [
|
||||||
"https://www.w3.org/2018/credentials/v1",
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
"https://idhub.pangea.org/credentials/base/v1",
|
"https://idhub.pangea.org/credentials/base/v1",
|
||||||
{
|
"https://idhub.pangea.org/credentials/course-credential/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"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"id": "{{ vc_id }}",
|
"id": "{{ vc_id }}",
|
||||||
"type": [
|
"type": [
|
||||||
|
@ -59,6 +41,7 @@
|
||||||
"id": "{{ subject_did }}",
|
"id": "{{ subject_did }}",
|
||||||
"firstName": "{{ firstName }}",
|
"firstName": "{{ firstName }}",
|
||||||
"lastName": "{{ lastName }}",
|
"lastName": "{{ lastName }}",
|
||||||
|
"email": "{{ email }}",
|
||||||
"personalIdentifier": "{{ personalIdentifier }}",
|
"personalIdentifier": "{{ personalIdentifier }}",
|
||||||
"issuedDate": "{{ issuedDate }}",
|
"issuedDate": "{{ issuedDate }}",
|
||||||
"modeOfInstruction": "{{ modeOfInstruction }}",
|
"modeOfInstruction": "{{ modeOfInstruction }}",
|
||||||
|
|
|
@ -2,14 +2,7 @@
|
||||||
"@context": [
|
"@context": [
|
||||||
"https://www.w3.org/2018/credentials/v1",
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
"https://idhub.pangea.org/credentials/base/v1",
|
"https://idhub.pangea.org/credentials/base/v1",
|
||||||
{
|
"https://idhub.pangea.org/credentials/e-operator-claim/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"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"id": "{{ vc_id }}",
|
"id": "{{ vc_id }}",
|
||||||
"type": [
|
"type": [
|
||||||
|
@ -59,7 +52,8 @@
|
||||||
"operatorNumber": "{{ operatorNumber }}",
|
"operatorNumber": "{{ operatorNumber }}",
|
||||||
"limitJurisdiction": "{{ limitJurisdiction }}",
|
"limitJurisdiction": "{{ limitJurisdiction }}",
|
||||||
"accreditedFor": "{{ accreditedFor }}",
|
"accreditedFor": "{{ accreditedFor }}",
|
||||||
"role": "{{ role }}"
|
"role": "{{ role }}",
|
||||||
|
"email": "{{ email }}"
|
||||||
},
|
},
|
||||||
"credentialSchema": {
|
"credentialSchema": {
|
||||||
"id": "https://idhub.pangea.org/vc_schemas/federation-membership.json",
|
"id": "https://idhub.pangea.org/vc_schemas/federation-membership.json",
|
||||||
|
|
|
@ -2,26 +2,7 @@
|
||||||
"@context": [
|
"@context": [
|
||||||
"https://www.w3.org/2018/credentials/v1",
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
"https://idhub.pangea.org/credentials/base/v1",
|
"https://idhub.pangea.org/credentials/base/v1",
|
||||||
{
|
"https://idhub.pangea.org/credentials/federation-membership/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"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"id": "{{ vc_id }}",
|
"id": "{{ vc_id }}",
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
@ -20,38 +20,68 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
|
||||||
|
<div class="row mt-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
You must read the terms and conditions of this service and accept the
|
{{ form.accept_privacy }}
|
||||||
<a class="btn btn-green-admin" href="jacascript:void()" data-bs-toggle="modal" data-bs-target="#gdpr" title="{% trans 'GDPR' %}">Read GDPR</a>
|
{{ form.privacy_label|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row mt-2">
|
||||||
<div class="col-sm-4">
|
<div class="col">
|
||||||
{% bootstrap_form form %}
|
{{ form.accept_legal }}
|
||||||
|
{{ form.legal_label|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions-no-box">
|
<div class="row mt-2">
|
||||||
<a class="btn btn-grey" href="{% url 'idhub:admin_dashboard' %}">{% translate "Cancel" %}</a>
|
<div class="col">
|
||||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
{{ 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>
|
</div>
|
||||||
|
|
||||||
</form>
|
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal" id="gdpr" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
<div class="modal" id="gdpr" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<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>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<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>
|
||||||
<div class="modal-footer">
|
<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>
|
||||||
</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 %}
|
{% endblock %}
|
||||||
|
|
|
@ -87,8 +87,8 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if path == 'user_gdpr' %}active2{% endif %}" href="{% url 'idhub:user_gdpr' %}">
|
<a class="nav-link {% if path == 'user_terms_and_conditions' %}active2{% endif %}" href="{% url 'idhub:user_terms_and_conditions' %}">
|
||||||
{% trans 'GDPR info' %}
|
{% trans 'Data protection' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -150,9 +150,12 @@
|
||||||
</div>
|
</div>
|
||||||
</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="/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/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="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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -11,7 +11,12 @@
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-center">
|
<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>
|
<a href="javascript:void()" type="button" class="btn btn-green-user">{% trans 'Notice of Privacy' %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,38 +20,68 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
<div class="row mt-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
You must read the terms and conditions of this service and accept the
|
{{ form.accept_privacy }}
|
||||||
<a class="btn btn-green-user" href="jacascript:void()" data-bs-toggle="modal" data-bs-target="#gdpr" title="{% trans 'GDPR' %}">Read GDPR</a>
|
{{ form.privacy_label|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row mt-2">
|
||||||
<div class="col-sm-4">
|
<div class="col">
|
||||||
{% bootstrap_form form %}
|
{{ form.accept_legal }}
|
||||||
|
{{ form.legal_label|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions-no-box">
|
<div class="row mt-2">
|
||||||
<a class="btn btn-grey" href="{% url 'idhub:user_dashboard' %}">{% translate "Cancel" %}</a>
|
<div class="col">
|
||||||
<input class="btn btn-green-user" type="submit" name="submit" value="{% translate 'Save' %}" />
|
{{ 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>
|
</div>
|
||||||
|
|
||||||
</form>
|
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal" id="gdpr" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
<div class="modal" id="gdpr" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<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>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<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>
|
||||||
<div class="modal-footer">
|
<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>
|
||||||
</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 %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,6 +19,7 @@ from django.views.generic import RedirectView
|
||||||
from django.urls import path, reverse_lazy
|
from django.urls import path, reverse_lazy
|
||||||
from .views import (
|
from .views import (
|
||||||
LoginView,
|
LoginView,
|
||||||
|
PasswordResetView,
|
||||||
PasswordResetConfirmView,
|
PasswordResetConfirmView,
|
||||||
serve_did,
|
serve_did,
|
||||||
DobleFactorSendView,
|
DobleFactorSendView,
|
||||||
|
@ -34,16 +35,7 @@ urlpatterns = [
|
||||||
permanent=False)),
|
permanent=False)),
|
||||||
path('login/', LoginView.as_view(), name='login'),
|
path('login/', LoginView.as_view(), name='login'),
|
||||||
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
||||||
path('auth/password_reset/',
|
path('auth/password_reset/', PasswordResetView.as_view(), name='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/done/',
|
path('auth/password_reset/done/',
|
||||||
auth_views.PasswordResetDoneView.as_view(
|
auth_views.PasswordResetDoneView.as_view(
|
||||||
template_name='auth/password_reset_done.html'
|
template_name='auth/password_reset_done.html'
|
||||||
|
@ -53,13 +45,6 @@ urlpatterns = [
|
||||||
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
|
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
|
||||||
name='password_reset_confirm'
|
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/',
|
path('auth/reset/done/',
|
||||||
auth_views.PasswordResetCompleteView.as_view(
|
auth_views.PasswordResetCompleteView.as_view(
|
||||||
template_name='auth/password_reset_complete.html'
|
template_name='auth/password_reset_complete.html'
|
||||||
|
|
|
@ -15,8 +15,16 @@ class ProfileForm(forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class TermsConditionsForm(forms.Form):
|
class TermsConditionsForm(forms.Form):
|
||||||
accept = forms.BooleanField(
|
accept_privacy = forms.BooleanField(
|
||||||
label=_("Accept terms and conditions of the service"),
|
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
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,9 +32,33 @@ class TermsConditionsForm(forms.Form):
|
||||||
self.user = kwargs.pop('user', None)
|
self.user = kwargs.pop('user', None)
|
||||||
super().__init__(*args, **kwargs)
|
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):
|
def clean(self):
|
||||||
data = self.cleaned_data
|
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
|
self.user.accept_gdpr = True
|
||||||
else:
|
else:
|
||||||
self.user.accept_gdpr = False
|
self.user.accept_gdpr = False
|
||||||
|
|
|
@ -117,6 +117,13 @@ class DIDTable(tables.Table):
|
||||||
|
|
||||||
class CredentialsTable(tables.Table):
|
class CredentialsTable(tables.Table):
|
||||||
description = tables.Column(verbose_name="Details", empty_values=())
|
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):
|
def render_description(self, record):
|
||||||
return record.get_description()
|
return record.get_description()
|
||||||
|
@ -131,6 +138,9 @@ class CredentialsTable(tables.Table):
|
||||||
|
|
||||||
return (queryset, True)
|
return (queryset, True)
|
||||||
|
|
||||||
|
def render_view_credential(self):
|
||||||
|
return format_html('<i class="bi bi-eye"></i>')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VerificableCredential
|
model = VerificableCredential
|
||||||
template_name = "idhub/custom_table.html"
|
template_name = "idhub/custom_table.html"
|
||||||
|
|
|
@ -100,6 +100,13 @@ class ProfileView(MyProfile, UpdateView, SingleTableView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
return super().form_valid(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):
|
class RolesView(MyProfile, SingleTableView):
|
||||||
|
@ -116,7 +123,7 @@ class RolesView(MyProfile, SingleTableView):
|
||||||
|
|
||||||
class GDPRView(MyProfile, TemplateView):
|
class GDPRView(MyProfile, TemplateView):
|
||||||
template_name = "idhub/user/gdpr.html"
|
template_name = "idhub/user/gdpr.html"
|
||||||
subtitle = _('GDPR info')
|
subtitle = _('Data protection')
|
||||||
icon = 'bi bi-file-earmark-medical'
|
icon = 'bi bi-file-earmark-medical'
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,9 +142,9 @@ class CredentialsView(MyWallet, SingleTableView):
|
||||||
|
|
||||||
class TermsAndConditionsView(UserView, FormView):
|
class TermsAndConditionsView(UserView, FormView):
|
||||||
template_name = "idhub/user/terms_conditions.html"
|
template_name = "idhub/user/terms_conditions.html"
|
||||||
title = _("GDPR")
|
title = _("Data Protection")
|
||||||
section = ""
|
section = ""
|
||||||
subtitle = _('Accept Terms and Conditions')
|
subtitle = _('Terms and Conditions')
|
||||||
icon = 'bi bi-file-earmark-medical'
|
icon = 'bi bi-file-earmark-medical'
|
||||||
form_class = TermsConditionsForm
|
form_class = TermsConditionsForm
|
||||||
success_url = reverse_lazy('idhub:user_dashboard')
|
success_url = reverse_lazy('idhub:user_dashboard')
|
||||||
|
@ -145,7 +152,12 @@ class TermsAndConditionsView(UserView, FormView):
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['user'] = self.request.user
|
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
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
@ -16,6 +17,9 @@ from idhub.email.views import NotifyActivateUserByEmail
|
||||||
from trustchain_idhub import settings
|
from trustchain_idhub import settings
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class LoginView(auth_views.LoginView):
|
class LoginView(auth_views.LoginView):
|
||||||
template_name = 'auth/login.html'
|
template_name = 'auth/login.html'
|
||||||
extra_context = {
|
extra_context = {
|
||||||
|
@ -52,7 +56,7 @@ class LoginView(auth_views.LoginView):
|
||||||
# )
|
# )
|
||||||
# cache.set("KEY_DIDS", encryption_key, None)
|
# cache.set("KEY_DIDS", encryption_key, None)
|
||||||
cache.set("KEY_DIDS", sensitive_data_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())
|
self.request.session["2fauth"] = str(uuid.uuid4())
|
||||||
return redirect(reverse_lazy('idhub:confirm_send_2f'))
|
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')
|
success_url = reverse_lazy('idhub:password_reset_complete')
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
password = form.cleaned_data.get("password")
|
password = form.cleaned_data.get("new_password1")
|
||||||
user = form.get_user()
|
user = form.user
|
||||||
|
user.set_password(password)
|
||||||
user.set_encrypted_sensitive_data(password)
|
user.set_encrypted_sensitive_data(password)
|
||||||
user.save()
|
user.save()
|
||||||
return HttpResponseRedirect(self.success_url)
|
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):
|
def serve_did(request, did_id):
|
||||||
id_did = f'did:web:{settings.DOMAIN}:did-registry:{did_id}'
|
id_did = f'did:web:{settings.DOMAIN}:did-registry:{did_id}'
|
||||||
did = get_object_or_404(DID, did=id_did)
|
did = get_object_or_404(DID, did=id_did)
|
||||||
|
|
|
@ -155,3 +155,11 @@ class User(AbstractBaseUser):
|
||||||
pw = base64.b64decode(password.encode('utf-8')*4)
|
pw = base64.b64decode(password.encode('utf-8')*4)
|
||||||
sb_key = self.derive_key_from_password(pw)
|
sb_key = self.derive_key_from_password(pw)
|
||||||
return nacl.secret.SecretBox(sb_key)
|
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,8 +137,7 @@ class VerifyView(View):
|
||||||
vp_token.verifing()
|
vp_token.verifing()
|
||||||
response = vp_token.get_response_verify()
|
response = vp_token.get_response_verify()
|
||||||
vp_token.save()
|
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)
|
return JsonResponse(response)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ from promotion.models import Promotion
|
||||||
|
|
||||||
|
|
||||||
class WalletForm(forms.Form):
|
class WalletForm(forms.Form):
|
||||||
organization = forms.ChoiceField(choices=[])
|
organization = forms.ChoiceField(
|
||||||
|
choices=[],
|
||||||
|
widget=forms.Select(attrs={'class': 'form-select'})
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.presentation_definition = kwargs.pop('presentation_definition', [])
|
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">
|
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body id="body-login">
|
<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 class="navbar-nav navbar-sub-brand">
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
<form role="form" method="post">
|
<form role="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-4">
|
<div class="col">
|
||||||
{% bootstrap_form form %}
|
{% bootstrap_form form %}
|
||||||
</div>
|
</div>
|
||||||
</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
|
return kwargs
|
||||||
|
|
||||||
self.vp_token.get_user_info()
|
self.vp_token.get_user_info()
|
||||||
kwargs['initial']["nif"] = self.vp_token.user_info.get("nif", '')
|
kwargs['initial']["nif"] = self.vp_token.user_info.get("identityNumber", '')
|
||||||
kwargs['initial']["name"] = self.vp_token.user_info.get("name", '')
|
kwargs['initial']["name"] = self.vp_token.user_info.get("firstName", '')
|
||||||
kwargs['initial']["first_last_name"] = self.vp_token.user_info.get("first_last_name", '')
|
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']["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"] = self.vp_token.user_info.get("email", '')
|
||||||
kwargs['initial']["email_repeat"] = 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
|
fontTools==4.47.0
|
||||||
weasyprint==60.2
|
weasyprint==60.2
|
||||||
ujson==5.9.0
|
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
|
./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",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"title": "NGO Course Credential 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",
|
"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",
|
"type": "string",
|
||||||
"description": "The family name of the student"
|
"description": "The family name of the student"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "email"
|
||||||
|
},
|
||||||
"personalIdentifier": {
|
"personalIdentifier": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The personal identifier of the student, such as ID number"
|
"description": "The personal identifier of the student, such as ID number"
|
||||||
|
@ -116,6 +120,7 @@
|
||||||
"id",
|
"id",
|
||||||
"firstName",
|
"firstName",
|
||||||
"lastName",
|
"lastName",
|
||||||
|
"email",
|
||||||
"personalIdentifier",
|
"personalIdentifier",
|
||||||
"issuedDate",
|
"issuedDate",
|
||||||
"modeOfInstruction",
|
"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",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"title": "EOperatorClaim",
|
"title": "EOperatorClaim",
|
||||||
"description": "Product and waste electronics operator claim, as proposed by eReuse.org",
|
"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",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"title": "Federation membership",
|
"title": "Federation membership",
|
||||||
"description": "The federation membership specifies participation of a NGO into a NGO federation, as proposed by Lafede.cat",
|
"description": "The federation membership specifies participation of a NGO into a NGO federation, as proposed by Lafede.cat",
|
||||||
|
@ -113,6 +113,7 @@
|
||||||
"membershipStatus",
|
"membershipStatus",
|
||||||
"federation",
|
"federation",
|
||||||
"membershipSince",
|
"membershipSince",
|
||||||
|
"email",
|
||||||
"certificationDate"
|
"certificationDate"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ SECRET_KEY = config('SECRET_KEY')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = config('DEBUG', default=False, cast=bool)
|
DEBUG = config('DEBUG', default=False, cast=bool)
|
||||||
DEVELOPMENT = config('DEVELOPMENT', default=False, cast=bool)
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv())
|
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv())
|
||||||
CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='', cast=Csv())
|
CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='', cast=Csv())
|
||||||
|
@ -226,3 +225,7 @@ LOGGING = {
|
||||||
ORGANIZATION = config('ORGANIZATION', 'Pangea')
|
ORGANIZATION = config('ORGANIZATION', 'Pangea')
|
||||||
SYNC_ORG_DEV = config('SYNC_ORG_DEV', 'y')
|
SYNC_ORG_DEV = config('SYNC_ORG_DEV', 'y')
|
||||||
ORG_FILE = config('ORG_FILE', 'examples/organizations.csv')
|
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,21 +1,24 @@
|
||||||
|
import pandas as pd
|
||||||
import json
|
import json
|
||||||
#import jsonld
|
# import jsonld
|
||||||
import csv
|
import csv
|
||||||
import sys
|
import sys
|
||||||
import jsonschema
|
import jsonschema
|
||||||
from pyld import jsonld
|
# from jsonschema import validate, ValidationError
|
||||||
#from jsonschema import validate, ValidationError
|
|
||||||
import requests
|
import requests
|
||||||
from pyld import jsonld
|
from pyld import jsonld
|
||||||
import jsonref
|
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}
|
# return {k: v for k, v in dictionary.items() if v is not None}
|
||||||
|
|
||||||
def _remove_null_values(dictionary):
|
def _remove_null_values(dictionary):
|
||||||
filtered = {k: v for k, v in dictionary.items() if v is not None and v != ''}
|
filtered = {k: v for k, v in dictionary.items() if v is not None and v != ''}
|
||||||
dictionary.clear()
|
dictionary.clear()
|
||||||
dictionary.update(filtered)
|
dictionary.update(filtered)
|
||||||
|
|
||||||
|
|
||||||
def validate_context(jsld):
|
def validate_context(jsld):
|
||||||
"""Validate a @context string through expanding"""
|
"""Validate a @context string through expanding"""
|
||||||
|
@ -23,152 +26,262 @@ def validate_context(jsld):
|
||||||
# schema = jsld["credentialSchema"]
|
# schema = jsld["credentialSchema"]
|
||||||
# Validate the context
|
# Validate the context
|
||||||
try:
|
try:
|
||||||
jsonld.expand(context)
|
jsonld.expand(context)
|
||||||
print("Context is valid")
|
print("Context is valid")
|
||||||
except jsonld.JsonLdError:
|
except jsonld.JsonLdError:
|
||||||
print("Context is not valid")
|
print("Context is not valid")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def compact_js(doc, context):
|
def compact_js(doc, context):
|
||||||
"""Validate a @context string through compacting, returns compacted context"""
|
"""Validate a @context string through compacting, returns compacted context"""
|
||||||
try:
|
try:
|
||||||
compacted = jsonld.compact(doc, context)
|
compacted = jsonld.compact(doc, context)
|
||||||
print(json.dumps(compacted, indent=2))
|
print(json.dumps(compacted, indent=2))
|
||||||
except jsonld.JsonLdError as e:
|
except jsonld.JsonLdError as e:
|
||||||
print(f"Error compacting document: {e}")
|
print(f"Error compacting document: {e}")
|
||||||
return None
|
return None
|
||||||
return compacted
|
return compacted
|
||||||
|
|
||||||
|
|
||||||
def dereference_context_file(json_file):
|
def dereference_context_file(json_file):
|
||||||
"""Dereference and return json-ld context from file"""
|
"""Dereference and return json-ld context from file"""
|
||||||
json_text = open(json_file).read()
|
json_text = open(json_file).read()
|
||||||
json_dict = json.loads(json_text)
|
json_dict = json.loads(json_text)
|
||||||
return dereference_context(json_dict)
|
return dereference_context(json_dict)
|
||||||
|
|
||||||
|
|
||||||
def dereference_context(jsonld_dict):
|
def dereference_context(jsonld_dict):
|
||||||
"""Dereference and return json-ld context"""
|
"""Dereference and return json-ld context"""
|
||||||
try:
|
try:
|
||||||
# Extract the context from the parsed JSON-LD
|
# Extract the context from the parsed JSON-LD
|
||||||
context_urls = jsonld_dict.get('@context')
|
context_urls = jsonld_dict.get('@context')
|
||||||
if not context_urls:
|
if not context_urls:
|
||||||
raise ValueError("No context found in the JSON-LD string.")
|
raise ValueError("No context found in the JSON-LD string.")
|
||||||
return None
|
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:
|
# Dereference each context URL
|
||||||
print(f"An error occurred: {e}")
|
dereferenced_contexts = []
|
||||||
return None
|
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):
|
def validate_schema_file(json_schema_file):
|
||||||
"""Validate standalone schema from file"""
|
"""Validate standalone schema from file"""
|
||||||
try:
|
try:
|
||||||
json_schema = open(json_schema_file).read()
|
json_schema = json.loads(open(json_schema_file).read())
|
||||||
validate_schema(json_schema)
|
validate_schema(json_schema)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading file {json_schema_file} or validating schema {json_schema}: {e}")
|
print(f"Error loading file {json_schema_file} or validating schema {json_schema}: {e}")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def validate_schema(json_schema):
|
def validate_schema(json_schema):
|
||||||
"""Validate standalone schema, returns bool (uses Draft202012Validator, alt: Draft7Validator, alt: Draft4Validator, Draft6Validator )"""
|
"""Validate standalone schema, returns bool (uses Draft202012Validator, alt: Draft7Validator, alt: Draft4Validator, Draft6Validator )"""
|
||||||
try:
|
try:
|
||||||
jsonschema.validators.Draft202012Validator.check_schema(json_schema)
|
jsonschema.validators.Draft202012Validator.check_schema(json_schema)
|
||||||
# jsonschema.validators.Draft7Validator.check_schema(json_schema)
|
# jsonschema.validators.Draft7Validator.check_schema(json_schema)
|
||||||
return True
|
except jsonschema.exceptions.SchemaError as e:
|
||||||
except jsonschema.exceptions.SchemaError as e:
|
print(e)
|
||||||
print(e)
|
return False
|
||||||
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):
|
def validate_json(json_data, json_schema):
|
||||||
"""Validate json string basic (no format) with schema, returns bool"""
|
"""Validate json string basic (no format) with schema, returns bool"""
|
||||||
try:
|
try:
|
||||||
jsonschema.validate(instance=json_data, schema=json_schema)
|
jsonschema.validate(instance=json_data, schema=json_schema)
|
||||||
except jsonschema.exceptions.ValidationError as err:
|
except jsonschema.exceptions.ValidationError as err:
|
||||||
print('Validation error: ', json_data, '\n')
|
print('Validation error: ', json_data, '\n')
|
||||||
return False
|
return False
|
||||||
return True
|
print("Successful validation")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def validate_json_format(json_data, json_schema):
|
def validate_json_format(json_data, json_schema):
|
||||||
"""Validate a json string basic (including format) with schema, returns bool"""
|
"""Validate a json string basic (including format) with schema, returns bool"""
|
||||||
try:
|
try:
|
||||||
jsonschema.validate(instance=json_data, schema=json_schema, format_checker=FormatChecker())
|
jsonschema.validate(instance=json_data, schema=json_schema, format_checker=FormatChecker())
|
||||||
except jsonschema.exceptions.ValidationError as err:
|
except jsonschema.exceptions.ValidationError as err:
|
||||||
print('Validation error: ', json_data, '\n')
|
print('Validation error: ', json_data, '\n')
|
||||||
return False
|
return False
|
||||||
return True
|
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):
|
def schema_to_csv(schema, csv_file_path):
|
||||||
"""Extract headers from an schema and write to file, returns bool"""
|
"""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'
|
||||||
|
|
||||||
# Create a CSV file with the headers
|
# Get the keys of the matched objects
|
||||||
with open(csv_file_path, 'w', newline='') as csv_file:
|
headers = [key for match in matches for key in match.keys()]
|
||||||
writer = csv.writer(csv_file)
|
# print('\nHeaders: ', headers)
|
||||||
writer.writerow(headers)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
# 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):
|
def csv_to_json(csvFilePath, schema, jsonFilePath):
|
||||||
"""Read from a csv file, check schema, write to json file, returns bool"""
|
"""Read from a csv file, check schema, write to json file, returns bool"""
|
||||||
jsonArray = []
|
jsonArray = []
|
||||||
# Read CSV file
|
# Read CSV file
|
||||||
with open(csvFilePath, 'r') as csvf:
|
with open(csvFilePath, 'r') as csvf:
|
||||||
# Load CSV file data using csv library's dictionary reader
|
# Load CSV file data using csv library's dictionary reader
|
||||||
csvReader = csv.DictReader(csvf)
|
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):
|
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"""
|
"""Read from a csv file, write to json file (assumes a row 'No' is primary key), returns bool EXPERIMENT"""
|
||||||
# Create a dictionary
|
# Create a dictionary
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
# Open a csv reader called DictReader
|
# Open a csv reader called DictReader
|
||||||
with open(csv_file_path, encoding='utf-8') as csvf:
|
with open(csv_file_path, encoding='utf-8') as csvf:
|
||||||
csvReader = csv.DictReader(csvf)
|
csvReader = csv.DictReader(csvf)
|
||||||
|
|
||||||
# Convert each row into a dictionary and add it to data
|
# Convert each row into a dictionary and add it to data
|
||||||
for rows in csvReader:
|
for rows in csvReader:
|
||||||
# Assuming a column named 'No' to be the primary key
|
# Assuming a column named 'No' to be the primary key
|
||||||
key = rows['No']
|
key = rows['No']
|
||||||
data[key] = rows
|
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__":
|
if __name__ == "__main__":
|
||||||
sch_name = sys.argv[1]
|
# sch_name = sys.argv[1]
|
||||||
sch_file = sch_name + '-schema.json'
|
schemas = sys.argv[1:]
|
||||||
sch = json.loads(open(sch_file).read())
|
|
||||||
if validate_json(d, sch):
|
# credtools.py course-credential device-purchase e-operator-claim federation-membership financial-vulnerability membership-card
|
||||||
generate_csv_from_schema(sch, sch_name + '-template.csv')
|
#sch_name = 'e-operator-claim'
|
||||||
else:
|
|
||||||
print("Validation error: ", sch_name)
|
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)
|
||||||
|
|
Loading…
Reference in a new issue