new encriptation system of sensible datas

This commit is contained in:
Cayo Puigdefabregas 2024-02-20 17:50:45 +01:00
parent 93f2432edb
commit 0247c5007d
12 changed files with 192 additions and 154 deletions

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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):
self.data = self.user.encrypt_data(value)
def set_data(self, value, password):
self.data = self.user.encrypt_data(value, password)
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

View File

@ -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">

View File

@ -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">

View File

@ -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'),

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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