Skip to content

Commit 406a7ff

Browse files
authored
fix: portal backend 376 (#150)
* fix: new otp secret * fix: new otp secret
1 parent 29e7116 commit 406a7ff

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

codeforlife/user/signals/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"""
55

66
# NOTE: Need to import signals so they are discoverable by Django.
7+
from .auth_factor import auth_factor__post_delete
78
from .teacher import teacher_receiver
89
from .user import user_receiver
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
© Ocado Group
3+
Created on 17/01/2025 at 15:55:22(+00:00).
4+
"""
5+
6+
import pyotp
7+
from django.db.models import signals
8+
from django.dispatch import receiver
9+
10+
from ..models import AuthFactor
11+
12+
# pylint: disable=missing-function-docstring
13+
# pylint: disable=unused-argument
14+
15+
16+
@receiver(signals.post_delete, sender=AuthFactor)
17+
def auth_factor__post_delete(sender, instance: AuthFactor, **kwargs):
18+
# Create new secret to ensure secrets are not recycled.
19+
if instance.type == AuthFactor.Type.OTP:
20+
otp_secret = instance.user.userprofile.otp_secret
21+
# Ensure the randomly generated new secret is different to the previous.
22+
while otp_secret == instance.user.userprofile.otp_secret:
23+
instance.user.userprofile.otp_secret = pyotp.random_base32()
24+
25+
instance.user.userprofile.save(update_fields=["otp_secret"])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
© Ocado Group
3+
Created on 17/01/2025 at 16:04:46(+00:00).
4+
"""
5+
6+
from django.test import TestCase
7+
8+
from ..models import AuthFactor
9+
10+
11+
# pylint: disable-next=missing-class-docstring
12+
class TestAuthFactor(TestCase):
13+
fixtures = ["school_2"]
14+
15+
def test_post_delete(self):
16+
"""Deleting an otp-auth-factor assigns a new otp-secret to its user."""
17+
auth_factor = AuthFactor.objects.filter(
18+
type=AuthFactor.Type.OTP
19+
).first()
20+
assert auth_factor
21+
22+
userprofile = auth_factor.user.userprofile
23+
otp_secret = userprofile.otp_secret
24+
25+
auth_factor.delete()
26+
27+
userprofile.refresh_from_db()
28+
assert otp_secret != userprofile.otp_secret

0 commit comments

Comments
 (0)