resolve merge conflicts
This commit is contained in:
commit
646831a55e
|
@ -17,6 +17,7 @@ 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
|
||||||
|
@ -645,13 +646,14 @@ 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()
|
form.instance.set_did(cache.get("KEY_DIDS"))
|
||||||
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)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DidEditView(Credentials, UpdateView):
|
class DidEditView(Credentials, UpdateView):
|
||||||
template_name = "idhub/admin/did_register.html"
|
template_name = "idhub/admin/did_register.html"
|
||||||
subtitle = _('Organization Identities (DID)')
|
subtitle = _('Organization Identities (DID)')
|
||||||
|
|
|
@ -7,6 +7,7 @@ from utils import credtools
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.cache import cache
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from idhub.models import DID, Schemas
|
from idhub.models import DID, Schemas
|
||||||
from oidc4vp.models import Organization
|
from oidc4vp.models import Organization
|
||||||
|
@ -36,17 +37,25 @@ class Command(BaseCommand):
|
||||||
self.create_organizations(r[0].strip(), r[1].strip())
|
self.create_organizations(r[0].strip(), r[1].strip())
|
||||||
self.sync_credentials_organizations("pangea.org", "somconnexio.coop")
|
self.sync_credentials_organizations("pangea.org", "somconnexio.coop")
|
||||||
self.sync_credentials_organizations("local 8000", "local 9000")
|
self.sync_credentials_organizations("local 8000", "local 9000")
|
||||||
self.create_defaults_dids()
|
|
||||||
self.create_schemas()
|
self.create_schemas()
|
||||||
|
|
||||||
def create_admin_users(self, email, password):
|
def create_admin_users(self, email, password):
|
||||||
User.objects.create_superuser(email=email, password=password)
|
su = User.objects.create_superuser(email=email, password=password)
|
||||||
|
su.set_encrypted_sensitive_data(password)
|
||||||
|
su.save()
|
||||||
|
key = su.decrypt_sensitive_data(password)
|
||||||
|
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.save()
|
u.save()
|
||||||
|
key = u.decrypt_sensitive_data(password)
|
||||||
|
self.create_defaults_dids(u, key)
|
||||||
|
|
||||||
|
|
||||||
def create_organizations(self, name, url):
|
def create_organizations(self, name, url):
|
||||||
|
@ -61,12 +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):
|
did = DID(label="Default", user=u, type=DID.Types.KEY)
|
||||||
for u in User.objects.all():
|
did.set_did(password)
|
||||||
did = DID(label="Default", user=u, type=DID.Types.KEY)
|
did.save()
|
||||||
did.set_did()
|
|
||||||
did.save()
|
|
||||||
|
|
||||||
def create_schemas(self):
|
def create_schemas(self):
|
||||||
schemas_files = os.listdir(settings.SCHEMAS_DIR)
|
schemas_files = os.listdir(settings.SCHEMAS_DIR)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2024-01-16 13:04
|
# Generated by Django 4.2.5 on 2024-01-17 13:11
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -34,7 +34,7 @@ class Migration(migrations.Migration):
|
||||||
('created_at', models.DateTimeField(auto_now=True)),
|
('created_at', models.DateTimeField(auto_now=True)),
|
||||||
('label', models.CharField(max_length=50, verbose_name='Label')),
|
('label', models.CharField(max_length=50, verbose_name='Label')),
|
||||||
('did', models.CharField(max_length=250)),
|
('did', models.CharField(max_length=250)),
|
||||||
('key_material', models.CharField(max_length=250)),
|
('key_material', models.CharField(max_length=255)),
|
||||||
('didweb_document', models.TextField()),
|
('didweb_document', models.TextField()),
|
||||||
(
|
(
|
||||||
'user',
|
'user',
|
||||||
|
|
|
@ -5,8 +5,11 @@ import datetime
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
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,
|
||||||
keydid_from_controller_key,
|
keydid_from_controller_key,
|
||||||
|
@ -419,7 +422,7 @@ class DID(models.Model):
|
||||||
# In JWK format. Must be stored as-is and passed whole to library functions.
|
# In JWK format. Must be stored as-is and passed whole to library functions.
|
||||||
# Example key material:
|
# Example key material:
|
||||||
# '{"kty":"OKP","crv":"Ed25519","x":"oB2cPGFx5FX4dtS1Rtep8ac6B__61HAP_RtSzJdPxqs","d":"OJw80T1CtcqV0hUcZdcI-vYNBN1dlubrLaJa0_se_gU"}'
|
# '{"kty":"OKP","crv":"Ed25519","x":"oB2cPGFx5FX4dtS1Rtep8ac6B__61HAP_RtSzJdPxqs","d":"OJw80T1CtcqV0hUcZdcI-vYNBN1dlubrLaJa0_se_gU"}'
|
||||||
key_material = models.CharField(max_length=250)
|
key_material = models.CharField(max_length=255)
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
@ -428,25 +431,32 @@ class DID(models.Model):
|
||||||
)
|
)
|
||||||
didweb_document = models.TextField()
|
didweb_document = models.TextField()
|
||||||
|
|
||||||
|
def get_key_material(self, password):
|
||||||
|
return self.user.decrypt_data(self.key_material, password)
|
||||||
|
|
||||||
|
def set_key_material(self, value, password):
|
||||||
|
self.key_material = self.user.encrypt_data(value, password)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_organization_did(self):
|
def is_organization_did(self):
|
||||||
if not self.user:
|
if not self.user:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def set_did(self):
|
def set_did(self, password):
|
||||||
self.key_material = generate_did_controller_key()
|
new_key_material = generate_did_controller_key()
|
||||||
|
self.set_key_material(new_key_material, password)
|
||||||
|
|
||||||
if self.type == self.Types.KEY:
|
if self.type == self.Types.KEY:
|
||||||
self.did = keydid_from_controller_key(self.key_material)
|
self.did = keydid_from_controller_key(new_key_material)
|
||||||
elif self.type == self.Types.WEB:
|
elif self.type == self.Types.WEB:
|
||||||
didurl, document = webdid_from_controller_key(self.key_material)
|
didurl, document = webdid_from_controller_key(new_key_material)
|
||||||
self.did = didurl
|
self.did = didurl
|
||||||
self.didweb_document = document
|
self.didweb_document = document
|
||||||
|
|
||||||
def get_key(self):
|
def get_key(self):
|
||||||
return json.loads(self.key_material)
|
return json.loads(self.key_material)
|
||||||
|
|
||||||
|
|
||||||
class Schemas(models.Model):
|
class Schemas(models.Model):
|
||||||
type = models.CharField(max_length=250)
|
type = models.CharField(max_length=250)
|
||||||
file_schema = models.CharField(max_length=250)
|
file_schema = models.CharField(max_length=250)
|
||||||
|
@ -518,6 +528,14 @@ class VerificableCredential(models.Model):
|
||||||
related_name='vcredentials',
|
related_name='vcredentials',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_data(self, password):
|
||||||
|
if not self.data:
|
||||||
|
return ""
|
||||||
|
return self.user.decrypt_data(self.data, password)
|
||||||
|
|
||||||
|
def set_data(self, value, password):
|
||||||
|
self.data = self.user.encrypt_data(value, password)
|
||||||
|
|
||||||
def type(self):
|
def type(self):
|
||||||
return self.schema.type
|
return self.schema.type
|
||||||
|
|
||||||
|
@ -547,20 +565,23 @@ 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):
|
def issue(self, did, password):
|
||||||
if self.status == self.Status.ISSUED:
|
if self.status == self.Status.ISSUED:
|
||||||
return
|
return
|
||||||
|
|
||||||
# self.status = self.Status.ISSUED
|
self.status = self.Status.ISSUED
|
||||||
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)
|
||||||
d_ordered = ujson.loads(self.render())
|
issuer_pass = cache.get("KEY_DIDS")
|
||||||
d_minimum = self.filter_dict(d_ordered)
|
# issuer_pass = self.user.decrypt_data(
|
||||||
data = ujson.dumps(d_minimum)
|
# cache.get("KEY_DIDS"),
|
||||||
self.data = sign_credential(
|
# settings.SECRET_KEY,
|
||||||
data,
|
# )
|
||||||
self.issuer_did.key_material
|
data = sign_credential(
|
||||||
|
self.render(),
|
||||||
|
self.issuer_did.get_key_material(issuer_pass)
|
||||||
)
|
)
|
||||||
|
self.data = self.user.encrypt_data(data, password)
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
d = json.loads(self.csv_data)
|
d = json.loads(self.csv_data)
|
||||||
|
@ -596,7 +617,9 @@ class VerificableCredential(models.Model):
|
||||||
self.schema.file_schema
|
self.schema.file_schema
|
||||||
)
|
)
|
||||||
tmpl = get_template(template_name)
|
tmpl = get_template(template_name)
|
||||||
return tmpl.render(context)
|
d_ordered = ujson.loads(tmpl.render(context))
|
||||||
|
d_minimum = self.filter_dict(d_ordered)
|
||||||
|
return ujson.dumps(d_minimum)
|
||||||
|
|
||||||
|
|
||||||
def get_issued_on(self):
|
def get_issued_on(self):
|
||||||
|
|
|
@ -17,7 +17,7 @@ Including another URLconf
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
from django.urls import path, reverse_lazy
|
from django.urls import path, reverse_lazy
|
||||||
from .views import LoginView, serve_did
|
from .views import LoginView, PasswordResetConfirmView, serve_did
|
||||||
from .admin import views as views_admin
|
from .admin import views as views_admin
|
||||||
from .user import views as views_user
|
from .user import views as views_user
|
||||||
# from .verification_portal import views as views_verification_portal
|
# from .verification_portal import views as views_verification_portal
|
||||||
|
@ -45,13 +45,16 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
name='password_reset_done'
|
name='password_reset_done'
|
||||||
),
|
),
|
||||||
path('auth/reset/<uidb64>/<token>/',
|
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
|
||||||
auth_views.PasswordResetConfirmView.as_view(
|
|
||||||
template_name='auth/password_reset_confirm.html',
|
|
||||||
success_url=reverse_lazy('idhub:password_reset_complete')
|
|
||||||
),
|
|
||||||
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'
|
||||||
|
|
|
@ -24,6 +24,7 @@ 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)
|
||||||
|
@ -52,7 +53,8 @@ class RequestCredentialForm(forms.Form):
|
||||||
cred = cred[0]
|
cred = cred[0]
|
||||||
cred._domain = self._domain
|
cred._domain = self._domain
|
||||||
try:
|
try:
|
||||||
cred.issue(did)
|
if self.password:
|
||||||
|
cred.issue(did, self.password)
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,15 @@ class CredentialJsonView(MyWallet, TemplateView):
|
||||||
pk=pk,
|
pk=pk,
|
||||||
user=self.request.user
|
user=self.request.user
|
||||||
)
|
)
|
||||||
response = HttpResponse(self.object.data, content_type="application/json")
|
pass_enc = self.request.session.get("key_did")
|
||||||
|
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['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
response['Content-Disposition'] = 'attachment; filename={}'.format("credential.json")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -138,6 +146,15 @@ 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):
|
||||||
|
@ -208,7 +225,11 @@ 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
|
||||||
form.instance.set_did()
|
pw = self.request.user.decrypt_data(
|
||||||
|
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'))
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.cache import cache
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.contrib.auth import login as auth_login
|
from django.contrib.auth import login as auth_login
|
||||||
|
@ -25,13 +27,41 @@ 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)
|
||||||
|
|
||||||
|
sensitive_data_encryption_key = user.decrypt_sensitive_data(password)
|
||||||
|
|
||||||
if not user.is_anonymous and user.is_admin:
|
if not user.is_anonymous and user.is_admin:
|
||||||
admin_dashboard = reverse_lazy('idhub:admin_dashboard')
|
admin_dashboard = reverse_lazy('idhub:admin_dashboard')
|
||||||
self.extra_context['success_url'] = admin_dashboard
|
self.extra_context['success_url'] = admin_dashboard
|
||||||
auth_login(self.request, user)
|
# 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)
|
||||||
|
|
||||||
|
self.request.session["key_did"] = user.encrypt_data(
|
||||||
|
sensitive_data_encryption_key,
|
||||||
|
user.password+self.request.session._session_key
|
||||||
|
)
|
||||||
|
|
||||||
return HttpResponseRedirect(self.extra_context['success_url'])
|
return HttpResponseRedirect(self.extra_context['success_url'])
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
||||||
|
template_name = 'auth/password_reset_confirm.html'
|
||||||
|
success_url = reverse_lazy('idhub:password_reset_complete')
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
password = form.cleaned_data.get("password")
|
||||||
|
user = form.get_user()
|
||||||
|
user.set_encrypted_sensitive_data(password)
|
||||||
|
user.save()
|
||||||
|
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)
|
||||||
|
|
|
@ -31,4 +31,3 @@ class ProfileForm(forms.ModelForm):
|
||||||
|
|
||||||
return last_name
|
return last_name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2024-01-16 13:04
|
# Generated by Django 4.2.5 on 2024-01-17 13:11
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ class Migration(migrations.Migration):
|
||||||
blank=True, max_length=255, null=True, verbose_name='Last name'
|
blank=True, max_length=255, null=True, verbose_name='Last name'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
('encrypted_sensitive_data', models.CharField(max_length=255)),
|
||||||
|
('salt', models.CharField(max_length=255)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
|
import nacl
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from nacl import pwhash
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.cache import cache
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
|
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
|
||||||
|
|
||||||
|
@ -44,6 +49,8 @@ class User(AbstractBaseUser):
|
||||||
is_admin = models.BooleanField(default=False)
|
is_admin = models.BooleanField(default=False)
|
||||||
first_name = models.CharField(_("First name"), max_length=255, blank=True, null=True)
|
first_name = models.CharField(_("First name"), max_length=255, blank=True, null=True)
|
||||||
last_name = models.CharField(_("Last name"), max_length=255, blank=True, null=True)
|
last_name = models.CharField(_("Last name"), max_length=255, blank=True, null=True)
|
||||||
|
encrypted_sensitive_data = models.CharField(max_length=255)
|
||||||
|
salt = models.CharField(max_length=255)
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
@ -86,3 +93,65 @@ class User(AbstractBaseUser):
|
||||||
for r in s.service.rol.all():
|
for r in s.service.rol.all():
|
||||||
roles.append(r.name)
|
roles.append(r.name)
|
||||||
return ", ".join(set(roles))
|
return ", ".join(set(roles))
|
||||||
|
|
||||||
|
def derive_key_from_password(self, password):
|
||||||
|
kdf = pwhash.argon2i.kdf
|
||||||
|
ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE
|
||||||
|
mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE
|
||||||
|
return kdf(
|
||||||
|
nacl.secret.SecretBox.KEY_SIZE,
|
||||||
|
password,
|
||||||
|
self.get_salt(),
|
||||||
|
opslimit=ops,
|
||||||
|
memlimit=mem
|
||||||
|
)
|
||||||
|
|
||||||
|
def decrypt_sensitive_data(self, password, data=None):
|
||||||
|
sb_key = self.derive_key_from_password(password.encode('utf-8'))
|
||||||
|
sb = nacl.secret.SecretBox(sb_key)
|
||||||
|
if not data:
|
||||||
|
data = self.get_encrypted_sensitive_data()
|
||||||
|
if not isinstance(data, bytes):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
return sb.decrypt(data).decode('utf-8')
|
||||||
|
|
||||||
|
def encrypt_sensitive_data(self, password, data):
|
||||||
|
sb_key = self.derive_key_from_password(password.encode('utf-8'))
|
||||||
|
sb = nacl.secret.SecretBox(sb_key)
|
||||||
|
if not isinstance(data, bytes):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
return base64.b64encode(sb.encrypt(data)).decode('utf-8')
|
||||||
|
|
||||||
|
def get_salt(self):
|
||||||
|
return base64.b64decode(self.salt.encode('utf-8'))
|
||||||
|
|
||||||
|
def set_salt(self):
|
||||||
|
self.salt = base64.b64encode(nacl.utils.random(16)).decode('utf-8')
|
||||||
|
|
||||||
|
def get_encrypted_sensitive_data(self):
|
||||||
|
return base64.b64decode(self.encrypted_sensitive_data.encode('utf-8'))
|
||||||
|
|
||||||
|
def set_encrypted_sensitive_data(self, password):
|
||||||
|
key = base64.b64encode(nacl.utils.random(64))
|
||||||
|
self.set_salt()
|
||||||
|
|
||||||
|
key_crypted = self.encrypt_sensitive_data(password, key)
|
||||||
|
self.encrypted_sensitive_data = key_crypted
|
||||||
|
|
||||||
|
def encrypt_data(self, data, password):
|
||||||
|
sb = self.get_secret_box(password)
|
||||||
|
value_enc = sb.encrypt(data.encode('utf-8'))
|
||||||
|
return base64.b64encode(value_enc).decode('utf-8')
|
||||||
|
|
||||||
|
def decrypt_data(self, data, password):
|
||||||
|
# import pdb; pdb.set_trace()
|
||||||
|
sb = self.get_secret_box(password)
|
||||||
|
value = base64.b64decode(data.encode('utf-8'))
|
||||||
|
return sb.decrypt(value).decode('utf-8')
|
||||||
|
|
||||||
|
def get_secret_box(self, password):
|
||||||
|
pw = base64.b64decode(password.encode('utf-8')*4)
|
||||||
|
sb_key = self.derive_key_from_password(pw)
|
||||||
|
return nacl.secret.SecretBox(sb_key)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2024-01-16 13:04
|
# Generated by Django 4.2.5 on 2024-01-17 13:11
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.5 on 2024-01-16 13:04
|
# Generated by Django 4.2.5 on 2024-01-17 13:11
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
|
@ -12,6 +12,7 @@ requests==2.31.0
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
jsonref==1.1.0
|
jsonref==1.1.0
|
||||||
pyld==2.0.3
|
pyld==2.0.3
|
||||||
|
pynacl==1.5.0
|
||||||
more-itertools==10.1.0
|
more-itertools==10.1.0
|
||||||
dj-database-url==2.1.0
|
dj-database-url==2.1.0
|
||||||
ujson==5.9.0
|
ujson==5.9.0
|
||||||
|
|
|
@ -149,6 +149,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
Loading…
Reference in a new issue