new encriptation system of sensible datas
This commit is contained in:
parent
93f2432edb
commit
0247c5007d
|
@ -1,23 +1,19 @@
|
||||||
import csv
|
|
||||||
import json
|
import json
|
||||||
import copy
|
|
||||||
import base64
|
import base64
|
||||||
import jsonschema
|
import jsonschema
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from pyhanko.sign import signers
|
from nacl.exceptions import CryptoError
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from utils import credtools, certs
|
from utils import certs
|
||||||
from idhub.models import (
|
from idhub.models import (
|
||||||
DID,
|
DID,
|
||||||
File_datas,
|
File_datas,
|
||||||
Membership,
|
Membership,
|
||||||
Schemas,
|
Schemas,
|
||||||
Service,
|
|
||||||
UserRol,
|
UserRol,
|
||||||
VerificableCredential,
|
VerificableCredential,
|
||||||
)
|
)
|
||||||
|
@ -51,6 +47,37 @@ class TermsConditionsForm2(forms.Form):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class EncryptionKeyForm(forms.Form):
|
||||||
|
key = forms.CharField(
|
||||||
|
label=_("Key for encrypt the secrets of all system"),
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
data = self.cleaned_data
|
||||||
|
self._key = data["key"]
|
||||||
|
if not DID.objects.exists():
|
||||||
|
return data
|
||||||
|
|
||||||
|
did = DID.objects.first()
|
||||||
|
cache.set("KEY_DIDS", self._key, None)
|
||||||
|
try:
|
||||||
|
did.get_key_material()
|
||||||
|
except CryptoError:
|
||||||
|
cache.set("KEY_DIDS", None)
|
||||||
|
txt = _("Key no valid!")
|
||||||
|
raise ValidationError(txt)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
cache.set("KEY_DIDS", self._key, None)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class TermsConditionsForm(forms.Form):
|
class TermsConditionsForm(forms.Form):
|
||||||
accept_privacy = forms.BooleanField(
|
accept_privacy = forms.BooleanField(
|
||||||
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||||
|
@ -133,7 +160,7 @@ class ImportForm(forms.Form):
|
||||||
self.fields['did'].choices = [
|
self.fields['did'].choices = [
|
||||||
(x.did, x.label) for x in dids.filter(eidas1=False)
|
(x.did, x.label) for x in dids.filter(eidas1=False)
|
||||||
]
|
]
|
||||||
self.fields['schema'].choices = [
|
self.fields['schema'].choices = [(0, _('Select one'))] + [
|
||||||
(x.id, x.name) for x in Schemas.objects.filter()
|
(x.id, x.name) for x in Schemas.objects.filter()
|
||||||
]
|
]
|
||||||
if dids.filter(eidas1=True).exists():
|
if dids.filter(eidas1=True).exists():
|
||||||
|
@ -197,6 +224,9 @@ class ImportForm(forms.Form):
|
||||||
if not data_pd:
|
if not data_pd:
|
||||||
self.exception("This file is empty!")
|
self.exception("This file is empty!")
|
||||||
|
|
||||||
|
if not self._schema:
|
||||||
|
return data
|
||||||
|
|
||||||
for n in range(df.last_valid_index()+1):
|
for n in range(df.last_valid_index()+1):
|
||||||
row = {}
|
row = {}
|
||||||
for k in data_pd.keys():
|
for k in data_pd.keys():
|
||||||
|
@ -382,7 +412,6 @@ class ImportCertificateForm(forms.Form):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def new_did(self):
|
def new_did(self):
|
||||||
cert = self.pfx_file
|
|
||||||
keys = {
|
keys = {
|
||||||
"cert": base64.b64encode(self.pfx_file).decode('utf-8'),
|
"cert": base64.b64encode(self.pfx_file).decode('utf-8'),
|
||||||
"passphrase": self._pss
|
"passphrase": self._pss
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
import pandas as pd
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from jsonschema import validate
|
|
||||||
from smtplib import SMTPException
|
from smtplib import SMTPException
|
||||||
from django_tables2 import SingleTableView
|
from django_tables2 import SingleTableView
|
||||||
|
|
||||||
|
@ -18,7 +15,6 @@ from django.views.generic.edit import (
|
||||||
UpdateView,
|
UpdateView,
|
||||||
)
|
)
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.core.cache import cache
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
@ -28,12 +24,13 @@ from idhub_auth.forms import ProfileForm
|
||||||
from idhub.mixins import AdminView, Http403
|
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 (
|
||||||
|
EncryptionKeyForm,
|
||||||
|
ImportCertificateForm,
|
||||||
ImportForm,
|
ImportForm,
|
||||||
MembershipForm,
|
MembershipForm,
|
||||||
TermsConditionsForm,
|
TermsConditionsForm,
|
||||||
SchemaForm,
|
SchemaForm,
|
||||||
UserRolForm,
|
UserRolForm
|
||||||
ImportCertificateForm,
|
|
||||||
)
|
)
|
||||||
from idhub.admin.tables import (
|
from idhub.admin.tables import (
|
||||||
DashboardTable,
|
DashboardTable,
|
||||||
|
@ -79,7 +76,27 @@ class TermsAndConditionsView(AdminView, FormView):
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
user = form.save()
|
form.save()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class EncryptionKeyView(AdminView, FormView):
|
||||||
|
template_name = "idhub/admin/encryption_key.html"
|
||||||
|
title = _('Encryption Key')
|
||||||
|
section = ""
|
||||||
|
subtitle = _('Encryption Key')
|
||||||
|
icon = 'bi bi-key'
|
||||||
|
form_class = EncryptionKeyForm
|
||||||
|
success_url = reverse_lazy('idhub:admin_dashboard')
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if self.admin_validated:
|
||||||
|
return redirect(self.success_url)
|
||||||
|
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
@ -649,7 +666,7 @@ class CredentialJsonView(Credentials):
|
||||||
VerificableCredential,
|
VerificableCredential,
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
response = HttpResponse(self.object.data, content_type="application/json")
|
response = HttpResponse(self.object.get_data(), content_type="application/json")
|
||||||
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -730,7 +747,7 @@ class DidRegisterView(Credentials, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
form.instance.set_did(cache.get("KEY_DIDS"))
|
form.instance.set_did()
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID created successfully'))
|
messages.success(self.request, _('DID created successfully'))
|
||||||
Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance)
|
Event.set_EV_ORG_DID_CREATED_BY_ADMIN(form.instance)
|
||||||
|
@ -752,7 +769,7 @@ class DidEditView(Credentials, UpdateView):
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
user = form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID updated successfully'))
|
messages.success(self.request, _('DID updated successfully'))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ 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')
|
||||||
|
KEY_DIDS = config('KEY_DIDS', '1234')
|
||||||
|
cache.set("KEY_DIDS", KEY_DIDS, None)
|
||||||
|
|
||||||
self.create_admin_users(ADMIN_EMAIL, ADMIN_PASSWORD)
|
self.create_admin_users(ADMIN_EMAIL, ADMIN_PASSWORD)
|
||||||
if settings.CREATE_TEST_USERS:
|
if settings.CREATE_TEST_USERS:
|
||||||
|
@ -43,21 +45,17 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def create_admin_users(self, email, password):
|
def create_admin_users(self, email, password):
|
||||||
su = User.objects.create_superuser(email=email, password=password)
|
su = User.objects.create_superuser(email=email, password=password)
|
||||||
su.set_encrypted_sensitive_data(password)
|
su.set_encrypted_sensitive_data()
|
||||||
su.save()
|
su.save()
|
||||||
key = su.decrypt_sensitive_data(password)
|
self.create_defaults_dids(su)
|
||||||
key_dids = {su.id: key}
|
|
||||||
cache.set("KEY_DIDS", key_dids, None)
|
|
||||||
self.create_defaults_dids(su, key)
|
|
||||||
|
|
||||||
|
|
||||||
def create_users(self, email, password):
|
def create_users(self, email, password):
|
||||||
u = User.objects.create(email=email, password=password)
|
u = User.objects.create(email=email, password=password)
|
||||||
u.set_password(password)
|
u.set_password(password)
|
||||||
u.set_encrypted_sensitive_data(password)
|
u.set_encrypted_sensitive_data()
|
||||||
u.save()
|
u.save()
|
||||||
key = u.decrypt_sensitive_data(password)
|
self.create_defaults_dids(u)
|
||||||
self.create_defaults_dids(u, key)
|
|
||||||
|
|
||||||
|
|
||||||
def create_organizations(self, name, url):
|
def create_organizations(self, name, url):
|
||||||
|
@ -72,9 +70,10 @@ class Command(BaseCommand):
|
||||||
org1.my_client_secret = org2.client_secret
|
org1.my_client_secret = org2.client_secret
|
||||||
org1.save()
|
org1.save()
|
||||||
org2.save()
|
org2.save()
|
||||||
def create_defaults_dids(self, u, password):
|
|
||||||
|
def create_defaults_dids(self, u):
|
||||||
did = DID(label="Default", user=u, type=DID.Types.WEB)
|
did = DID(label="Default", user=u, type=DID.Types.WEB)
|
||||||
did.set_did(password)
|
did.set_did()
|
||||||
did.save()
|
did.save()
|
||||||
|
|
||||||
def create_schemas(self):
|
def create_schemas(self):
|
||||||
|
|
|
@ -12,8 +12,8 @@ class Http403(PermissionDenied):
|
||||||
default_detail = _('Permission denied. User is not authenticated')
|
default_detail = _('Permission denied. User is not authenticated')
|
||||||
default_code = 'forbidden'
|
default_code = 'forbidden'
|
||||||
|
|
||||||
def __init__(self, detail=None, code=None):
|
def __init__(self, details=None, code=None):
|
||||||
if detail is not None:
|
if details is not None:
|
||||||
self.detail = details or self.default_details
|
self.detail = details or self.default_details
|
||||||
if code is not None:
|
if code is not None:
|
||||||
self.code = code or self.default_code
|
self.code = code or self.default_code
|
||||||
|
@ -22,15 +22,30 @@ class Http403(PermissionDenied):
|
||||||
class UserView(LoginRequiredMixin):
|
class UserView(LoginRequiredMixin):
|
||||||
login_url = "/login/"
|
login_url = "/login/"
|
||||||
wallet = False
|
wallet = False
|
||||||
|
admin_validated = False
|
||||||
path_terms = [
|
path_terms = [
|
||||||
'admin_terms_and_conditions',
|
'admin_terms_and_conditions',
|
||||||
'user_terms_and_conditions',
|
'user_terms_and_conditions',
|
||||||
'user_gdpr',
|
'user_gdpr',
|
||||||
|
'user_waiting',
|
||||||
|
'user_waiting',
|
||||||
|
'encryption_key',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.admin_validated = cache.get("KEY_DIDS")
|
self.admin_validated = cache.get("KEY_DIDS")
|
||||||
response = super().get(request, *args, **kwargs)
|
response = super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
if not self.admin_validated:
|
||||||
|
actual_path = resolve(self.request.path).url_name
|
||||||
|
if not self.request.user.is_admin:
|
||||||
|
if actual_path != 'user_waiting':
|
||||||
|
return redirect(reverse_lazy("idhub:user_waiting"))
|
||||||
|
|
||||||
|
if self.request.user.is_admin:
|
||||||
|
if actual_path != 'encryption_key':
|
||||||
|
return redirect(reverse_lazy("idhub:encryption_key"))
|
||||||
|
|
||||||
url = self.check_gdpr()
|
url = self.check_gdpr()
|
||||||
|
|
||||||
return url or response
|
return url or response
|
||||||
|
|
|
@ -9,7 +9,6 @@ from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from nacl import secret
|
|
||||||
|
|
||||||
from utils.idhub_ssikit import (
|
from utils.idhub_ssikit import (
|
||||||
generate_did_controller_key,
|
generate_did_controller_key,
|
||||||
|
@ -34,26 +33,27 @@ class Event(models.Model):
|
||||||
EV_DID_CREATED = 9, "DID created"
|
EV_DID_CREATED = 9, "DID created"
|
||||||
EV_DID_DELETED = 10, "DID deleted"
|
EV_DID_DELETED = 10, "DID deleted"
|
||||||
EV_CREDENTIAL_DELETED_BY_USER = 11, "Credential deleted by user"
|
EV_CREDENTIAL_DELETED_BY_USER = 11, "Credential deleted by user"
|
||||||
EV_CREDENTIAL_DELETED = 12, "Credential deleted"
|
EV_CREDENTIAL_DELETED_BY_ADMIN = 12, "Credential deleted by admin"
|
||||||
EV_CREDENTIAL_ISSUED_FOR_USER = 13, "Credential issued for user"
|
EV_CREDENTIAL_DELETED = 13, "Credential deleted"
|
||||||
EV_CREDENTIAL_ISSUED = 14, "Credential issued"
|
EV_CREDENTIAL_ISSUED_FOR_USER = 14, "Credential issued for user"
|
||||||
EV_CREDENTIAL_PRESENTED_BY_USER = 15, "Credential presented by user"
|
EV_CREDENTIAL_ISSUED = 15, "Credential issued"
|
||||||
EV_CREDENTIAL_PRESENTED = 16, "Credential presented"
|
EV_CREDENTIAL_PRESENTED_BY_USER = 16, "Credential presented by user"
|
||||||
EV_CREDENTIAL_ENABLED = 17, "Credential enabled"
|
EV_CREDENTIAL_PRESENTED = 17, "Credential presented"
|
||||||
EV_CREDENTIAL_CAN_BE_REQUESTED = 18, "Credential available"
|
EV_CREDENTIAL_ENABLED = 18, "Credential enabled"
|
||||||
EV_CREDENTIAL_REVOKED_BY_ADMIN = 19, "Credential revoked by admin"
|
EV_CREDENTIAL_CAN_BE_REQUESTED = 19, "Credential available"
|
||||||
EV_CREDENTIAL_REVOKED = 20, "Credential revoked"
|
EV_CREDENTIAL_REVOKED_BY_ADMIN = 20, "Credential revoked by admin"
|
||||||
EV_ROLE_CREATED_BY_ADMIN = 21, "Role created by admin"
|
EV_CREDENTIAL_REVOKED = 21, "Credential revoked"
|
||||||
EV_ROLE_MODIFIED_BY_ADMIN = 22, "Role modified by admin"
|
EV_ROLE_CREATED_BY_ADMIN = 22, "Role created by admin"
|
||||||
EV_ROLE_DELETED_BY_ADMIN = 23, "Role deleted by admin"
|
EV_ROLE_MODIFIED_BY_ADMIN = 23, "Role modified by admin"
|
||||||
EV_SERVICE_CREATED_BY_ADMIN = 24, "Service created by admin"
|
EV_ROLE_DELETED_BY_ADMIN = 24, "Role deleted by admin"
|
||||||
EV_SERVICE_MODIFIED_BY_ADMIN = 25, "Service modified by admin"
|
EV_SERVICE_CREATED_BY_ADMIN = 25, "Service created by admin"
|
||||||
EV_SERVICE_DELETED_BY_ADMIN = 26, "Service deleted by admin"
|
EV_SERVICE_MODIFIED_BY_ADMIN = 26, "Service modified by admin"
|
||||||
EV_ORG_DID_CREATED_BY_ADMIN = 27, "Organisational DID created by admin"
|
EV_SERVICE_DELETED_BY_ADMIN = 27, "Service deleted by admin"
|
||||||
EV_ORG_DID_DELETED_BY_ADMIN = 28, "Organisational DID deleted by admin"
|
EV_ORG_DID_CREATED_BY_ADMIN = 28, "Organisational DID created by admin"
|
||||||
EV_USR_DEACTIVATED_BY_ADMIN = 29, "User deactivated"
|
EV_ORG_DID_DELETED_BY_ADMIN = 29, "Organisational DID deleted by admin"
|
||||||
EV_USR_ACTIVATED_BY_ADMIN = 30, "User activated"
|
EV_USR_DEACTIVATED_BY_ADMIN = 30, "User deactivated"
|
||||||
EV_USR_SEND_VP = 31, "User send Verificable Presentation"
|
EV_USR_ACTIVATED_BY_ADMIN = 31, "User activated"
|
||||||
|
EV_USR_SEND_VP = 32, "User send Verificable Presentation"
|
||||||
|
|
||||||
created = models.DateTimeField(_("Date"), auto_now=True)
|
created = models.DateTimeField(_("Date"), auto_now=True)
|
||||||
message = models.CharField(_("Description"), max_length=350)
|
message = models.CharField(_("Description"), max_length=350)
|
||||||
|
@ -99,9 +99,8 @@ class Event(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_EV_DATA_UPDATE_REQUESTED_BY_USER(cls, user):
|
def set_EV_DATA_UPDATE_REQUESTED_BY_USER(cls, user):
|
||||||
msg = _("The user '{username}' has request the update of the following information: ")
|
msg = _("The user '{username}' has request the update of the following information: ")
|
||||||
msg += "['field1':'value1', 'field2':'value2'>,...]".format(
|
msg += "['field1':'value1', 'field2':'value2'>,...]"
|
||||||
username=user.username,
|
msg = msg.format(username=user.username)
|
||||||
)
|
|
||||||
cls.objects.create(
|
cls.objects.create(
|
||||||
type=cls.Types.EV_DATA_UPDATE_REQUESTED_BY_USER,
|
type=cls.Types.EV_DATA_UPDATE_REQUESTED_BY_USER,
|
||||||
message=msg,
|
message=msg,
|
||||||
|
@ -444,11 +443,11 @@ class DID(models.Model):
|
||||||
# JSON-serialized DID document
|
# JSON-serialized DID document
|
||||||
didweb_document = models.TextField()
|
didweb_document = models.TextField()
|
||||||
|
|
||||||
def get_key_material(self, password):
|
def get_key_material(self):
|
||||||
return self.user.decrypt_data(self.key_material, password)
|
return self.user.decrypt_data(self.key_material)
|
||||||
|
|
||||||
def set_key_material(self, value, password):
|
def set_key_material(self, value):
|
||||||
self.key_material = self.user.encrypt_data(value, password)
|
self.key_material = self.user.encrypt_data(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_organization_did(self):
|
def is_organization_did(self):
|
||||||
|
@ -456,9 +455,9 @@ class DID(models.Model):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def set_did(self, password):
|
def set_did(self):
|
||||||
new_key_material = generate_did_controller_key()
|
new_key_material = generate_did_controller_key()
|
||||||
self.set_key_material(new_key_material, password)
|
self.set_key_material(new_key_material)
|
||||||
|
|
||||||
if self.type == self.Types.KEY:
|
if self.type == self.Types.KEY:
|
||||||
self.did = keydid_from_controller_key(new_key_material)
|
self.did = keydid_from_controller_key(new_key_material)
|
||||||
|
@ -621,17 +620,14 @@ class VerificableCredential(models.Model):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_data(self, password):
|
def get_data(self):
|
||||||
if not self.data:
|
if not self.data:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
if self.eidas1_did:
|
return self.user.decrypt_data(self.data)
|
||||||
return self.data
|
|
||||||
|
|
||||||
return self.user.decrypt_data(self.data, password)
|
|
||||||
|
|
||||||
def set_data(self, value, password):
|
def set_data(self, value):
|
||||||
self.data = self.user.encrypt_data(value, password)
|
self.data = self.user.encrypt_data(value)
|
||||||
|
|
||||||
def get_description(self):
|
def get_description(self):
|
||||||
return self.schema._description or ''
|
return self.schema._description or ''
|
||||||
|
@ -652,32 +648,24 @@ class VerificableCredential(models.Model):
|
||||||
data = json.loads(self.csv_data).items()
|
data = json.loads(self.csv_data).items()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def issue(self, did, password, domain=settings.DOMAIN.strip("/")):
|
def issue(self, did, domain=settings.DOMAIN.strip("/")):
|
||||||
if self.status == self.Status.ISSUED:
|
if self.status == self.Status.ISSUED:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.subject_did = did
|
self.subject_did = did
|
||||||
self.issued_on = datetime.datetime.now().astimezone(pytz.utc)
|
self.issued_on = datetime.datetime.now().astimezone(pytz.utc)
|
||||||
issuer_pass = cache.get("KEY_DIDS")
|
|
||||||
# issuer_pass = self.user.decrypt_data(
|
|
||||||
# cache.get("KEY_DIDS"),
|
|
||||||
# settings.SECRET_KEY,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# hash of credential without sign
|
# hash of credential without sign
|
||||||
self.hash = hashlib.sha3_256(self.render(domain).encode()).hexdigest()
|
self.hash = hashlib.sha3_256(self.render(domain).encode()).hexdigest()
|
||||||
data = sign_credential(
|
data = sign_credential(
|
||||||
self.render(domain),
|
self.render(domain),
|
||||||
self.issuer_did.get_key_material(issuer_pass)
|
self.issuer_did.get_key_material()
|
||||||
)
|
)
|
||||||
valid, reason = verify_credential(data)
|
valid, reason = verify_credential(data)
|
||||||
if not valid:
|
if not valid:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.eidas1_did:
|
self.data = self.user.encrypt_data(data)
|
||||||
self.data = data
|
|
||||||
else:
|
|
||||||
self.data = self.user.encrypt_data(data, password)
|
|
||||||
|
|
||||||
self.status = self.Status.ISSUED
|
self.status = self.Status.ISSUED
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<strong>{% trans 'Issuance date' %}:</strong>
|
<strong>{% trans 'Issuance date' %}:</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col bg-light text-secondary">
|
<div class="col bg-light text-secondary">
|
||||||
{{ object.issuer_on|default_if_none:"" }}
|
{{ object.issued_on|default_if_none:"" }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<strong>{% trans 'Issuance date' %}:</strong>
|
<strong>{% trans 'Issuance date' %}:</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col bg-light text-secondary">
|
<div class="col bg-light text-secondary">
|
||||||
{{ object.issuer_on|default_if_none:"" }}
|
{{ object.issued_on|default_if_none:"" }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
|
|
|
@ -88,6 +88,9 @@ urlpatterns = [
|
||||||
path('user/terms/', views_user.TermsAndConditionsView.as_view(),
|
path('user/terms/', views_user.TermsAndConditionsView.as_view(),
|
||||||
name='user_terms_and_conditions'),
|
name='user_terms_and_conditions'),
|
||||||
|
|
||||||
|
path('waiting/', views_user.WaitingView.as_view(),
|
||||||
|
name='user_waiting'),
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
path('admin/dashboard/', views_admin.DashboardView.as_view(),
|
path('admin/dashboard/', views_admin.DashboardView.as_view(),
|
||||||
name='admin_dashboard'),
|
name='admin_dashboard'),
|
||||||
|
@ -173,6 +176,7 @@ urlpatterns = [
|
||||||
name='admin_terms_and_conditions'),
|
name='admin_terms_and_conditions'),
|
||||||
path('admin/import/new', views_admin.ImportAddView.as_view(),
|
path('admin/import/new', views_admin.ImportAddView.as_view(),
|
||||||
name='admin_import_add'),
|
name='admin_import_add'),
|
||||||
|
path('admin/enc/', views_admin.EncryptionKeyView.as_view(), name='encryption_key'),
|
||||||
path('admin/auth/<uuid:admin2fauth>', views_admin.DobleFactorAuthView.as_view(),
|
path('admin/auth/<uuid:admin2fauth>', views_admin.DobleFactorAuthView.as_view(),
|
||||||
name='admin_2fauth'),
|
name='admin_2fauth'),
|
||||||
path('admin/auth/2f/', DobleFactorSendView.as_view(), name='confirm_send_2f'),
|
path('admin/auth/2f/', DobleFactorSendView.as_view(), name='confirm_send_2f'),
|
||||||
|
|
|
@ -81,7 +81,6 @@ class RequestCredentialForm(forms.Form):
|
||||||
self.user = kwargs.pop('user', None)
|
self.user = kwargs.pop('user', None)
|
||||||
self.lang = kwargs.pop('lang', None)
|
self.lang = kwargs.pop('lang', None)
|
||||||
self._domain = kwargs.pop('domain', None)
|
self._domain = kwargs.pop('domain', None)
|
||||||
self.password = kwargs.pop('password', None)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['did'].choices = [
|
self.fields['did'].choices = [
|
||||||
(x.did, x.label) for x in DID.objects.filter(user=self.user)
|
(x.did, x.label) for x in DID.objects.filter(user=self.user)
|
||||||
|
@ -109,8 +108,7 @@ class RequestCredentialForm(forms.Form):
|
||||||
did = did[0]
|
did = did[0]
|
||||||
cred = cred[0]
|
cred = cred[0]
|
||||||
try:
|
try:
|
||||||
if self.password:
|
cred.issue(did, domain=self._domain)
|
||||||
cred.issue(did, self.password, domain=self._domain)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -161,10 +161,25 @@ class TermsAndConditionsView(UserView, FormView):
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
user = form.save()
|
form.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class WaitingView(UserView, TemplateView):
|
||||||
|
template_name = "idhub/user/waiting.html"
|
||||||
|
title = _("Comunication with admin")
|
||||||
|
subtitle = _('Service temporary close')
|
||||||
|
section = ""
|
||||||
|
icon = 'bi bi-file-earmark-medical'
|
||||||
|
success_url = reverse_lazy('idhub:user_dashboard')
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if cache.get("KEY_DIDS"):
|
||||||
|
return redirect(self.success_url)
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CredentialView(MyWallet, TemplateView):
|
class CredentialView(MyWallet, TemplateView):
|
||||||
template_name = "idhub/user/credential.html"
|
template_name = "idhub/user/credential.html"
|
||||||
subtitle = _('Credential')
|
subtitle = _('Credential')
|
||||||
|
@ -194,7 +209,8 @@ class CredentialPdfView(MyWallet, TemplateView):
|
||||||
file_name = "certificate.pdf"
|
file_name = "certificate.pdf"
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.admin_validated = cache.get("KEY_DIDS")
|
if not self.admin_validated:
|
||||||
|
return redirect(reverse_lazy('idhub:user_dashboard'))
|
||||||
pk = kwargs['pk']
|
pk = kwargs['pk']
|
||||||
self.user = self.request.user
|
self.user = self.request.user
|
||||||
self.object = get_object_or_404(
|
self.object = get_object_or_404(
|
||||||
|
@ -282,10 +298,9 @@ class CredentialPdfView(MyWallet, TemplateView):
|
||||||
|
|
||||||
def get_pfx_data(self):
|
def get_pfx_data(self):
|
||||||
did = self.object.eidas1_did
|
did = self.object.eidas1_did
|
||||||
pw = self.admin_validated
|
if not did:
|
||||||
if not did or not pw:
|
|
||||||
return None, None
|
return None, None
|
||||||
key_material = json.loads(did.get_key_material(pw))
|
key_material = json.loads(did.get_key_material())
|
||||||
cert = key_material.get("cert")
|
cert = key_material.get("cert")
|
||||||
passphrase = key_material.get("passphrase")
|
passphrase = key_material.get("passphrase")
|
||||||
if cert and passphrase:
|
if cert and passphrase:
|
||||||
|
@ -337,14 +352,7 @@ class CredentialJsonView(MyWallet, TemplateView):
|
||||||
pk=pk,
|
pk=pk,
|
||||||
user=self.request.user
|
user=self.request.user
|
||||||
)
|
)
|
||||||
pass_enc = self.request.session.get("key_did")
|
data = self.object.get_data()
|
||||||
data = ""
|
|
||||||
if pass_enc:
|
|
||||||
user_pass = self.request.user.decrypt_data(
|
|
||||||
pass_enc,
|
|
||||||
self.request.user.password+self.request.session._session_key
|
|
||||||
)
|
|
||||||
data = self.object.get_data(user_pass)
|
|
||||||
response = HttpResponse(data, content_type="application/json")
|
response = HttpResponse(data, content_type="application/json")
|
||||||
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
||||||
return response
|
return response
|
||||||
|
@ -383,15 +391,6 @@ class CredentialsRequestView(MyWallet, FormView):
|
||||||
kwargs['lang'] = self.request.LANGUAGE_CODE
|
kwargs['lang'] = self.request.LANGUAGE_CODE
|
||||||
domain = "{}://{}".format(self.request.scheme, self.request.get_host())
|
domain = "{}://{}".format(self.request.scheme, self.request.get_host())
|
||||||
kwargs['domain'] = domain
|
kwargs['domain'] = domain
|
||||||
pass_enc = self.request.session.get("key_did")
|
|
||||||
if pass_enc:
|
|
||||||
user_pass = self.request.user.decrypt_data(
|
|
||||||
pass_enc,
|
|
||||||
self.request.user.password+self.request.session._session_key
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
pass_enc = None
|
|
||||||
kwargs['password'] = user_pass
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -468,11 +467,7 @@ class DidRegisterView(MyWallet, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
pw = self.request.user.decrypt_data(
|
form.instance.set_did()
|
||||||
self.request.session.get("key_did"),
|
|
||||||
self.request.user.password+self.request.session._session_key
|
|
||||||
)
|
|
||||||
form.instance.set_did(pw)
|
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID created successfully'))
|
messages.success(self.request, _('DID created successfully'))
|
||||||
|
|
||||||
|
@ -496,7 +491,7 @@ class DidEditView(MyWallet, UpdateView):
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
user = form.save()
|
form.save()
|
||||||
messages.success(self.request, _('DID updated successfully'))
|
messages.success(self.request, _('DID updated successfully'))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||||
|
|
||||||
from idhub.models import DID, VerificableCredential
|
from idhub.models import DID, VerificableCredential
|
||||||
from idhub.email.views import NotifyActivateUserByEmail
|
from idhub.email.views import NotifyActivateUserByEmail
|
||||||
from trustchain_idhub import settings
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -46,30 +45,20 @@ class LoginView(auth_views.LoginView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
user = form.get_user()
|
user = form.get_user()
|
||||||
password = form.cleaned_data.get("password")
|
|
||||||
auth_login(self.request, user)
|
auth_login(self.request, user)
|
||||||
|
|
||||||
sensitive_data_encryption_key = user.decrypt_sensitive_data(password)
|
if user.is_anonymous:
|
||||||
|
return redirect(reverse_lazy("idhub:login"))
|
||||||
|
|
||||||
if not user.is_anonymous and user.is_admin:
|
if user.is_admin:
|
||||||
admin_dashboard = reverse_lazy('idhub:admin_dashboard')
|
|
||||||
self.extra_context['success_url'] = admin_dashboard
|
|
||||||
# encryption_key = user.encrypt_data(
|
|
||||||
# sensitive_data_encryption_key,
|
|
||||||
# settings.SECRET_KEY
|
|
||||||
# )
|
|
||||||
# cache.set("KEY_DIDS", encryption_key, None)
|
|
||||||
cache.set("KEY_DIDS", sensitive_data_encryption_key, None)
|
|
||||||
if settings.ENABLE_2FACTOR_AUTH:
|
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'))
|
||||||
|
|
||||||
self.request.session["key_did"] = user.encrypt_data(
|
admin_dashboard = reverse_lazy('idhub:admin_dashboard')
|
||||||
sensitive_data_encryption_key,
|
self.extra_context['success_url'] = admin_dashboard
|
||||||
user.password+self.request.session._session_key
|
|
||||||
)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(self.extra_context['success_url'])
|
return redirect(self.extra_context['success_url'])
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import nacl
|
import nacl
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from nacl import pwhash
|
from nacl import pwhash, secret
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -95,21 +95,24 @@ class User(AbstractBaseUser):
|
||||||
roles.append(r.name)
|
roles.append(r.name)
|
||||||
return ", ".join(set(roles))
|
return ", ".join(set(roles))
|
||||||
|
|
||||||
def derive_key_from_password(self, password):
|
def derive_key_from_password(self, password=None):
|
||||||
|
if not password:
|
||||||
|
password = cache.get("KEY_DIDS").encode('utf-8')
|
||||||
|
|
||||||
kdf = pwhash.argon2i.kdf
|
kdf = pwhash.argon2i.kdf
|
||||||
ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE
|
ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE
|
||||||
mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE
|
mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE
|
||||||
return kdf(
|
return kdf(
|
||||||
nacl.secret.SecretBox.KEY_SIZE,
|
secret.SecretBox.KEY_SIZE,
|
||||||
password,
|
password,
|
||||||
self.get_salt(),
|
self.get_salt(),
|
||||||
opslimit=ops,
|
opslimit=ops,
|
||||||
memlimit=mem
|
memlimit=mem
|
||||||
)
|
)
|
||||||
|
|
||||||
def decrypt_sensitive_data(self, password, data=None):
|
def decrypt_sensitive_data(self, data=None):
|
||||||
sb_key = self.derive_key_from_password(password.encode('utf-8'))
|
sb_key = self.derive_key_from_password()
|
||||||
sb = nacl.secret.SecretBox(sb_key)
|
sb = secret.SecretBox(sb_key)
|
||||||
if not data:
|
if not data:
|
||||||
data = self.get_encrypted_sensitive_data()
|
data = self.get_encrypted_sensitive_data()
|
||||||
if not isinstance(data, bytes):
|
if not isinstance(data, bytes):
|
||||||
|
@ -117,9 +120,9 @@ class User(AbstractBaseUser):
|
||||||
|
|
||||||
return sb.decrypt(data).decode('utf-8')
|
return sb.decrypt(data).decode('utf-8')
|
||||||
|
|
||||||
def encrypt_sensitive_data(self, password, data):
|
def encrypt_sensitive_data(self, data):
|
||||||
sb_key = self.derive_key_from_password(password.encode('utf-8'))
|
sb_key = self.derive_key_from_password()
|
||||||
sb = nacl.secret.SecretBox(sb_key)
|
sb = secret.SecretBox(sb_key)
|
||||||
if not isinstance(data, bytes):
|
if not isinstance(data, bytes):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
@ -134,32 +137,33 @@ class User(AbstractBaseUser):
|
||||||
def get_encrypted_sensitive_data(self):
|
def get_encrypted_sensitive_data(self):
|
||||||
return base64.b64decode(self.encrypted_sensitive_data.encode('utf-8'))
|
return base64.b64decode(self.encrypted_sensitive_data.encode('utf-8'))
|
||||||
|
|
||||||
def set_encrypted_sensitive_data(self, password):
|
def set_encrypted_sensitive_data(self):
|
||||||
key = base64.b64encode(nacl.utils.random(64))
|
key = base64.b64encode(nacl.utils.random(64))
|
||||||
self.set_salt()
|
self.set_salt()
|
||||||
|
|
||||||
key_crypted = self.encrypt_sensitive_data(password, key)
|
key_crypted = self.encrypt_sensitive_data(key)
|
||||||
self.encrypted_sensitive_data = key_crypted
|
self.encrypted_sensitive_data = key_crypted
|
||||||
|
|
||||||
def encrypt_data(self, data, password):
|
def encrypt_data(self, data):
|
||||||
sb = self.get_secret_box(password)
|
sb = self.get_secret_box()
|
||||||
value_enc = sb.encrypt(data.encode('utf-8'))
|
value_enc = sb.encrypt(data.encode('utf-8'))
|
||||||
return base64.b64encode(value_enc).decode('utf-8')
|
return base64.b64encode(value_enc).decode('utf-8')
|
||||||
|
|
||||||
def decrypt_data(self, data, password):
|
def decrypt_data(self, data):
|
||||||
sb = self.get_secret_box(password)
|
sb = self.get_secret_box()
|
||||||
value = base64.b64decode(data.encode('utf-8'))
|
value = base64.b64decode(data.encode('utf-8'))
|
||||||
return sb.decrypt(value).decode('utf-8')
|
return sb.decrypt(value).decode('utf-8')
|
||||||
|
|
||||||
def get_secret_box(self, password):
|
def get_secret_box(self):
|
||||||
pw = base64.b64decode(password.encode('utf-8')*4)
|
sb_key = self.derive_key_from_password()
|
||||||
sb_key = self.derive_key_from_password(pw)
|
return secret.SecretBox(sb_key)
|
||||||
return nacl.secret.SecretBox(sb_key)
|
|
||||||
|
|
||||||
def change_password(self, old_password, new_password):
|
def change_password_key(self, new_password):
|
||||||
sensitive_data = self.decrypt_sensitive_data(old_password)
|
data = self.decrypt_sensitive_data()
|
||||||
self.encrypted_sensitive_data = self.encrypt_sensitive_data(
|
sb_key = self.derive_key_from_password(new_password)
|
||||||
new_password,
|
sb = secret.SecretBox(sb_key)
|
||||||
sensitive_data
|
if not isinstance(data, bytes):
|
||||||
)
|
data = data.encode('utf-8')
|
||||||
self.set_password(new_password)
|
|
||||||
|
encrypted_data = base64.b64encode(sb.encrypt(data)).decode('utf-8')
|
||||||
|
self.encrypted_sensitive_data = encrypted_data
|
||||||
|
|
Loading…
Reference in New Issue