django-pfx 1.4.dev44__tar.gz → 1.4.dev48__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/PKG-INFO +1 -1
  2. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/default_settings.py +2 -0
  4. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/otp_user_mixin.py +7 -3
  5. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_auth_api.py +54 -0
  6. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/.gitignore +0 -0
  7. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/.gitlab-ci.yml +0 -0
  8. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/.pre-commit-config.yaml +0 -0
  9. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/LICENSE +0 -0
  10. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/MANIFEST.in +0 -0
  11. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/README.md +0 -0
  12. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/django_pfx.egg-info/SOURCES.txt +0 -0
  13. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/django_pfx.egg-info/dependency_links.txt +0 -0
  14. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/django_pfx.egg-info/requires.txt +0 -0
  15. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/django_pfx.egg-info/top_level.txt +0 -0
  16. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/Makefile +0 -0
  17. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/conf.py +0 -0
  18. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/index.rst +0 -0
  19. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/api.views.rst +0 -0
  20. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/authentication.md +0 -0
  21. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/decorator.md +0 -0
  22. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/generate_openapi.md +0 -0
  23. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/getting_started.md +0 -0
  24. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/internationalisation.md +0 -0
  25. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/model.md +0 -0
  26. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/pfx_views.md +0 -0
  27. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/profiling.md +0 -0
  28. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/settings.md +0 -0
  29. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/doc/source/testing.md +0 -0
  30. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/img/pfx.png +0 -0
  31. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/img/pfx.svg +0 -0
  32. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/make_messages +0 -0
  33. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/manage.py +0 -0
  34. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/__init__.py +0 -0
  35. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/__init__.py +0 -0
  36. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/apidoc/__init__.py +0 -0
  37. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/apidoc/parameters.py +0 -0
  38. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/apidoc/schema.py +0 -0
  39. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/apidoc/tags.py +0 -0
  40. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/apps.py +0 -0
  41. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/decorator/__init__.py +0 -0
  42. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/decorator/rest.py +0 -0
  43. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/exceptions.py +0 -0
  44. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/fields.py +0 -0
  45. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/http/__init__.py +0 -0
  46. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/http/json_response.py +0 -0
  47. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  48. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
  49. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/management/__init__.py +0 -0
  50. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/management/commands/__init__.py +0 -0
  51. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  52. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/management/commands/profile.py +0 -0
  53. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/middleware/__init__.py +0 -0
  54. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/middleware/authentication.py +0 -0
  55. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/middleware/locale.py +0 -0
  56. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/middleware/profiling.py +0 -0
  57. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  58. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/migrations/__init__.py +0 -0
  59. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/__init__.py +0 -0
  60. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  61. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/cache_mixins.py +0 -0
  62. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/login_ban.py +0 -0
  63. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/not_null_fields.py +0 -0
  64. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/pfx_models.py +0 -0
  65. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/pfx_user.py +0 -0
  66. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  67. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/serializers/__init__.py +0 -0
  68. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/serializers/json.py +0 -0
  69. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/settings.py +0 -0
  70. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/shortcuts.py +0 -0
  71. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/storage/__init__.py +0 -0
  72. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/storage/s3_storage.py +0 -0
  73. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  74. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  75. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  76. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  77. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  78. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  79. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/test.py +0 -0
  80. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/urls.py +0 -0
  81. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/__init__.py +0 -0
  82. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/authentication_views.py +0 -0
  83. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/fields.py +0 -0
  84. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/filters_views.py +0 -0
  85. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/locale_views.py +0 -0
  86. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  87. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  88. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/groups.py +0 -0
  89. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  90. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  91. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  92. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  93. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  94. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  95. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  96. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  97. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  98. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset.py +0 -0
  99. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  100. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  101. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  102. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  103. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  104. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/pfxcore/views/rest_views.py +0 -0
  105. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/settings/__init__.py +0 -0
  106. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pfx/settings/dev.py +0 -0
  107. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/pyproject.toml +0 -0
  108. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/requirements.txt +0 -0
  109. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/serve-doc +0 -0
  110. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/setup.cfg +0 -0
  111. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/setup.py +0 -0
  112. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/__init__.py +0 -0
  113. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  114. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/models.py +0 -0
  115. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/__init__.py +0 -0
  116. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/ci.py +0 -0
  117. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/common.py +0 -0
  118. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/dev.py +0 -0
  119. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/dev_custom_example.py +0 -0
  120. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/settings/dev_default.py +0 -0
  121. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/__init__.py +0 -0
  122. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/basic_api_errors.py +0 -0
  123. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/basic_api_test.py +0 -0
  124. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_api_doc.py +0 -0
  125. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_body_mixin.py +0 -0
  126. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_cache.py +0 -0
  127. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_client.py +0 -0
  128. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_fields.py +0 -0
  129. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_filters.py +0 -0
  130. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_locale_api.py +0 -0
  131. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_perm_tests.py +0 -0
  132. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_perms_api.py +0 -0
  133. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_profiling_middleware.py +0 -0
  134. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_settings.py +0 -0
  135. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_shortcuts.py +0 -0
  136. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_timezone_middleware.py +0 -0
  137. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_tools.py +0 -0
  138. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_user_queryset.py +0 -0
  139. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_view_decorators.py +0 -0
  140. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/tests/test_view_fields.py +0 -0
  141. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/urls.py +0 -0
  142. {django_pfx-1.4.dev44 → django_pfx-1.4.dev48}/tests/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev44
3
+ Version: 1.4.dev48
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev44
3
+ Version: 1.4.dev48
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -2,6 +2,8 @@ PFX_TOKEN_SHORT_VALIDITY = {'hours': 12}
2
2
  PFX_TOKEN_LONG_VALIDITY = {'days': 30}
3
3
  PFX_TOKEN_OTP_VALIDITY = {'minutes': 15}
4
4
  PFX_HOTP_CODE_VALIDITY = 15
5
+ PFX_OTP_VALID_WINDOW = 1
6
+ PFX_OTP_IMAGE = None
5
7
 
6
8
  PFX_COOKIE_DOMAIN = None
7
9
  PFX_COOKIE_SECURE = True
@@ -69,10 +69,13 @@ class OtpUserMixin(models.Model):
69
69
  """Return the setup URL for OTP activation.
70
70
  """
71
71
  import pyotp
72
+ args = dict(
73
+ name=self.get_username(), issuer_name=settings.PFX_SITE_NAME)
74
+ if settings.PFX_OTP_IMAGE:
75
+ args['image'] = settings.PFX_OTP_IMAGE
72
76
  return pyotp.totp.TOTP(
73
77
  tmp and self.otp_secret_token_tmp or
74
- self.otp_secret_token).provisioning_uri(
75
- name=self.email, issuer_name=settings.PFX_SITE_NAME)
78
+ self.otp_secret_token).provisioning_uri(**args)
76
79
 
77
80
  def is_otp_valid(self, otp_code, tmp=False):
78
81
  """Verify an OTP code.
@@ -84,7 +87,8 @@ class OtpUserMixin(models.Model):
84
87
  """
85
88
  import pyotp
86
89
  totp = pyotp.parse_uri(self.get_otp_setup_uri(tmp=tmp))
87
- valid = totp.verify(otp_code)
90
+ valid = totp.verify(
91
+ otp_code, valid_window=settings.PFX_OTP_VALID_WINDOW)
88
92
  if not valid and timezone.now() <= self.hotp_expiry:
89
93
  hotp = pyotp.hotp.HOTP(
90
94
  tmp and self.otp_secret_token_tmp or
@@ -8,6 +8,7 @@ from django.conf import settings
8
8
  from django.contrib.auth.tokens import default_token_generator
9
9
  from django.core import mail
10
10
  from django.test import TransactionTestCase, modify_settings, override_settings
11
+ from django.utils import timezone
11
12
  from django.utils.encoding import force_bytes
12
13
  from django.utils.http import urlsafe_base64_encode
13
14
 
@@ -894,6 +895,7 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
894
895
  self.assertRC(response, 200)
895
896
  self.client.token = self.get_val(response, 'token')
896
897
 
898
+ @override_settings(PFX_OTP_IMAGE="https://example.org/fake.png")
897
899
  def test_otp_enable(self):
898
900
  self.client.login(username='jrr.tolkien', password='RIGHT PASSWORD')
899
901
 
@@ -901,7 +903,14 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
901
903
  response = self.client.get('/api/auth/otp/setup-uri')
902
904
  self.assertRC(response, 200)
903
905
  self.assertJIn(response, 'setup_uri', "otpauth://totp/")
906
+ self.assertJIn(response, 'setup_uri', "Books%20Demo:jrr.tolkien")
907
+ self.assertJIn(response, 'setup_uri', "issuer=Books%20Demo")
908
+ self.assertJIn(
909
+ response, 'setup_uri',
910
+ "image=https%3A%2F%2Fexample.org%2Ffake.png")
904
911
  self.user1.refresh_from_db()
912
+ self.assertJIn(
913
+ response, 'setup_uri', f"secret={self.user1.otp_secret_token_tmp}")
905
914
  self.assertIsNone(self.user1.otp_secret_token)
906
915
  self.assertEqual(len(self.user1.otp_secret_token_tmp), 32)
907
916
 
@@ -1080,6 +1089,51 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
1080
1089
  datetime.fromtimestamp(decoded['exp']),
1081
1090
  datetime(2023, 5, 2, 8, 10))
1082
1091
 
1092
+ @override_settings(
1093
+ PFX_OTP_VALID_WINDOW=0,
1094
+ PFX_TOKEN_SHORT_VALIDITY={'minutes': 30})
1095
+ def test_otp_login_valid_window_0(self):
1096
+ totp = self.enable_otp(self.user1)
1097
+
1098
+ with freeze_time("2023-05-01 08:00:00"):
1099
+ response = self.client.post('/api/auth/login', dict(
1100
+ username='jrr.tolkien',
1101
+ password='RIGHT PASSWORD'))
1102
+ self.assertRC(response, 200)
1103
+ token = self.get_val(response, 'token')
1104
+
1105
+ response = self.client.post('/api/auth/otp/login', dict(
1106
+ token=token, otp_code=totp.at(
1107
+ timezone.now() - timedelta(seconds=1))))
1108
+ self.assertRC(response, 422)
1109
+
1110
+ response = self.client.post('/api/auth/otp/login', dict(
1111
+ token=token, otp_code=totp.at(timezone.now())))
1112
+ self.assertRC(response, 200)
1113
+
1114
+ @override_settings(
1115
+ PFX_OTP_VALID_WINDOW=1,
1116
+ PFX_TOKEN_SHORT_VALIDITY={'minutes': 30})
1117
+ def test_otp_login_valid_window_1(self):
1118
+ totp = self.enable_otp(self.user1)
1119
+
1120
+ with freeze_time("2023-05-01 08:00:00"):
1121
+ response = self.client.post('/api/auth/login', dict(
1122
+ username='jrr.tolkien',
1123
+ password='RIGHT PASSWORD'))
1124
+ self.assertRC(response, 200)
1125
+ token = self.get_val(response, 'token')
1126
+
1127
+ response = self.client.post('/api/auth/otp/login', dict(
1128
+ token=token, otp_code=totp.at(
1129
+ timezone.now() - timedelta(seconds=31))))
1130
+ self.assertRC(response, 422)
1131
+
1132
+ response = self.client.post('/api/auth/otp/login', dict(
1133
+ token=token, otp_code=totp.at(
1134
+ timezone.now() - timedelta(seconds=30))))
1135
+ self.assertRC(response, 200)
1136
+
1083
1137
  @override_settings(
1084
1138
  PFX_TOKEN_SHORT_VALIDITY={'minutes': 30},
1085
1139
  PFX_LOGIN_BAN_FAILED_NUMBER=2,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes