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
« 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
3import base64
5from django.db import migrations
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
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)
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'),]
46 operations = [
47 migrations.RunPython(move_mfa, reverse_code=migrations.RunPython.noop)
48 ]