encripted in reset password

This commit is contained in:
Cayo Puigdefabregas 2024-01-04 12:43:24 +01:00
parent f62348dcdb
commit bd84dbc3bb
4 changed files with 77 additions and 24 deletions

View file

@ -17,7 +17,7 @@ Including another URLconf
from django.contrib.auth import views as auth_views
from django.views.generic import RedirectView
from django.urls import path, reverse_lazy
from .views import LoginView
from .views import LoginView, PasswordResetConfirmView
from .admin import views as views_admin
from .user import views as views_user
@ -44,13 +44,16 @@ urlpatterns = [
),
name='password_reset_done'
),
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')
),
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
name='password_reset_confirm'
),
# path('auth/reset/<uidb64>/<token>/',
# auth_views.PasswordResetConfirmView.as_view(
# template_name='auth/password_reset_confirm.html',
# success_url=reverse_lazy('idhub:password_reset_complete')
# ),
# name='password_reset_confirm'
# ),
path('auth/reset/done/',
auth_views.PasswordResetCompleteView.as_view(
template_name='auth/password_reset_complete.html'

View file

@ -4,7 +4,6 @@ from django.utils.translation import gettext_lazy as _
from django.contrib.auth import views as auth_views
from django.contrib.auth import login as auth_login
from django.http import HttpResponseRedirect
from nacl import secret
class LoginView(auth_views.LoginView):
@ -23,7 +22,7 @@ class LoginView(auth_views.LoginView):
user = form.get_user()
# Decrypt the user's sensitive data encryption key and store it in the session.
password = form.cleaned_data.get("password")
sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password)
sensitive_data_encryption_key = user.decrypt_sensitive_data(password)
key_dids = cache.get("KEY_DIDS", {})
if not user.is_anonymous and user.is_admin:
user_dashboard = reverse_lazy('idhub:user_dashboard')
@ -41,3 +40,14 @@ class LoginView(auth_views.LoginView):
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)

View file

@ -2,7 +2,7 @@ import re
from django import forms
from django.utils.translation import gettext_lazy as _
from idhub_auth.models import User
from idhub_auth.models import User, gen_salt
class ProfileForm(forms.ModelForm):
@ -31,4 +31,3 @@ class ProfileForm(forms.ModelForm):
return last_name

View file

@ -1,6 +1,9 @@
import nacl
import base64
from django.db import models
from django.core.cache import cache
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from nacl import secret, pwhash
class UserManager(BaseUserManager):
@ -44,10 +47,8 @@ class User(AbstractBaseUser):
is_admin = models.BooleanField(default=False)
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
# TODO: Hay que generar una clave aleatoria para cada usuario cuando se le da de alta en el sistema.
encrypted_sensitive_data_encryption_key = models.BinaryField(max_length=255)
# TODO: Hay que generar un salt aleatorio para cada usuario cuando se le da de alta en el sistema.
salt_of_sensitive_data_encryption_key = models.BinaryField(max_length=255)
encrypted_sensitive_data = models.CharField(max_length=255)
salt = models.CharField(max_length=255)
objects = UserManager()
@ -92,14 +93,54 @@ class User(AbstractBaseUser):
return ", ".join(set(roles))
def derive_key_from_password(self, password):
kdf = pwhash.argon2i.kdf # TODO: Move the KDF choice to SETTINGS.PY
ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY
mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY
salt = self.salt_of_sensitive_data_encryption_key
return kdf(secret.SecretBox.KEY_SIZE, password, salt, opslimit=ops, memlimit=mem)
kdf = nacl.pwhash.argon2i.kdf
ops = nacl.pwhash.argon2i.OPSLIMIT_INTERACTIVE
mem = nacl.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 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))
key_dids = cache.get("KEY_DIDS", {})
if key_dids.get(user.id):
key = key_dids[user.id]
else:
self.set_salt()
key_crypted = self.encrypt_sensitive_data(password, key)
self.encrypted_sensitive_data = base64.b64encode(key_crypted).decode('utf-8')
def decrypt_sensitive_data_encryption_key(self, password):
sb_key = self.derive_key_from_password(password)
sb = secret.SecretBox(sb_key)
return sb.decrypt(self.encrypted_sensitive_data_encryption_key)