Coverage for src/backend/InvenTree/users/migrations/0013_migrate_mfa_20240408_1659.py: 60%

21 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1# Generated by Django 3.2.23 on 2024-01-19 16:59 

2 

3import base64 

4 

5from django.db import migrations 

6 

7from allauth.mfa.adapter import get_adapter 

8from allauth.mfa.models import Authenticator 

9from django_otp.plugins.otp_static.models import StaticDevice 

10from django_otp.plugins.otp_totp.models import TOTPDevice 

11 

12 

13def move_mfa(apps, schema_editor): 

14 """Data migration to switch to django-allauth's new built-in MFA.""" 

15 adapter = get_adapter() 

16 authenticators = [] 

17 for totp in TOTPDevice.objects.filter(confirmed=True).iterator(): 17 ↛ 18line 17 didn't jump to line 18 because the loop on line 17 never started

18 recovery_codes = set() 

19 for sdevice in StaticDevice.objects.filter( 

20 confirmed=True, user_id=totp.user_id 

21 ).iterator(): 

22 recovery_codes.update(sdevice.token_set.values_list('token', flat=True)) 

23 secret = base64.b32encode(bytes.fromhex(totp.key)).decode('ascii') 

24 totp_authenticator = Authenticator( 

25 user_id=totp.user_id, 

26 type=Authenticator.Type.TOTP, 

27 data={'secret': adapter.encrypt(secret)}, 

28 ) 

29 authenticators.append(totp_authenticator) 

30 authenticators.append( 

31 Authenticator( 

32 user_id=totp.user_id, 

33 type=Authenticator.Type.RECOVERY_CODES, 

34 data={'migrated_codes': [adapter.encrypt(c) for c in recovery_codes]}, 

35 ) 

36 ) 

37 Authenticator.objects.bulk_create(authenticators) 

38 

39 

40class Migration(migrations.Migration): 

41 dependencies = [('users', '0012_alter_ruleset_can_view'), 

42 ('otp_static', '0002_throttling'), 

43 ('otp_totp', '0002_auto_20190420_0723'), 

44 ('mfa', '0002_authenticator_timestamps'),] 

45 

46 operations = [ 

47 migrations.RunPython(move_mfa, reverse_code=migrations.RunPython.noop) 

48 ]