diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af0b3f39..37cf9a8b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,3 +24,9 @@ repos: rev: 4.0.1 hooks: - id: flake8 + +- repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [--py37-plus] diff --git a/djoser/conf.py b/djoser/conf.py index 2c53c387..dc85a039 100644 --- a/djoser/conf.py +++ b/djoser/conf.py @@ -22,7 +22,7 @@ def __getattribute__(self, item): val = [import_string(v) if isinstance(v, str) else v for v in val] self[item] = val except KeyError: - val = super(ObjDict, self).__getattribute__(item) + val = super().__getattribute__(item) return val diff --git a/djoser/constants.py b/djoser/constants.py index 02949e13..ef1209c0 100644 --- a/djoser/constants.py +++ b/djoser/constants.py @@ -1,7 +1,7 @@ from django.utils.translation import gettext_lazy as _ -class Messages(object): +class Messages: INVALID_CREDENTIALS_ERROR = _("Unable to log in with provided credentials.") INACTIVE_ACCOUNT_ERROR = _("User account is disabled.") INVALID_TOKEN_ERROR = _("Invalid token for given user.") diff --git a/djoser/serializers.py b/djoser/serializers.py index ad7faa86..805bf01c 100644 --- a/djoser/serializers.py +++ b/djoser/serializers.py @@ -262,14 +262,12 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.username_field = settings.LOGIN_FIELD self._default_username_field = User.USERNAME_FIELD - self.fields["new_{}".format(self.username_field)] = self.fields.pop( - self.username_field - ) + self.fields[f"new_{self.username_field}"] = self.fields.pop(self.username_field) def save(self, **kwargs): if self.username_field != self._default_username_field: kwargs[User.USERNAME_FIELD] = self.validated_data.get( - "new_{}".format(self.username_field) + f"new_{self.username_field}" ) return super().save(**kwargs) @@ -288,7 +286,7 @@ def __init__(self, *args, **kwargs): def validate(self, attrs): attrs = super().validate(attrs) new_username = attrs[settings.LOGIN_FIELD] - if new_username != attrs["re_new_{}".format(settings.LOGIN_FIELD)]: + if new_username != attrs[f"re_new_{settings.LOGIN_FIELD}"]: self.fail("username_mismatch") else: return attrs diff --git a/djoser/utils.py b/djoser/utils.py index 8de2de10..bd3fe633 100644 --- a/djoser/utils.py +++ b/djoser/utils.py @@ -31,7 +31,7 @@ def logout_user(request): logout(request) -class ActionViewMixin(object): +class ActionViewMixin: def post(self, request, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/djoser/views.py b/djoser/views.py index f4915662..2e5f6fa0 100644 --- a/djoser/views.py +++ b/djoser/views.py @@ -262,7 +262,7 @@ def reset_password_confirm(self, request, *args, **kwargs): settings.EMAIL.password_changed_confirmation(self.request, context).send(to) return Response(status=status.HTTP_204_NO_CONTENT) - @action(["post"], detail=False, url_path="set_{}".format(User.USERNAME_FIELD)) + @action(["post"], detail=False, url_path=f"set_{User.USERNAME_FIELD}") def set_username(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -277,7 +277,7 @@ def set_username(self, request, *args, **kwargs): settings.EMAIL.username_changed_confirmation(self.request, context).send(to) return Response(status=status.HTTP_204_NO_CONTENT) - @action(["post"], detail=False, url_path="reset_{}".format(User.USERNAME_FIELD)) + @action(["post"], detail=False, url_path=f"reset_{User.USERNAME_FIELD}") def reset_username(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -290,9 +290,7 @@ def reset_username(self, request, *args, **kwargs): return Response(status=status.HTTP_204_NO_CONTENT) - @action( - ["post"], detail=False, url_path="reset_{}_confirm".format(User.USERNAME_FIELD) - ) + @action(["post"], detail=False, url_path=f"reset_{User.USERNAME_FIELD}_confirm") def reset_username_confirm(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/djoser/webauthn/serializers.py b/djoser/webauthn/serializers.py index 6cebea75..7304be23 100644 --- a/djoser/webauthn/serializers.py +++ b/djoser/webauthn/serializers.py @@ -1,5 +1,4 @@ from django.contrib.auth import get_user_model -from django.db import IntegrityError, transaction from rest_framework import serializers from djoser.conf import settings @@ -29,9 +28,7 @@ def create(self, validated_data): def validate_username(self, username): if User.objects.filter(username=username).exists(): - raise serializers.ValidationError( - "User {} already exists.".format(username) - ) + raise serializers.ValidationError(f"User {username} already exists.") return username diff --git a/djoser/webauthn/urls.py b/djoser/webauthn/urls.py index 698ef5b1..0f9c4095 100644 --- a/djoser/webauthn/urls.py +++ b/djoser/webauthn/urls.py @@ -1,18 +1,20 @@ -from django.conf.urls import url +from django.urls import re_path from . import views urlpatterns = [ - url( + re_path( r"^signup_request/$", views.SingupRequestView.as_view(), name="webauthn_signup_request", ), - url(r"^signup/(?P.+)/$", views.SignupView.as_view(), name="webauthn_signup"), - url( + re_path( + r"^signup/(?P.+)/$", views.SignupView.as_view(), name="webauthn_signup" + ), + re_path( r"^login_request/$", views.LoginRequestView.as_view(), name="webauthn_login_request", ), - url(r"^login/$", views.LoginView.as_view(), name="webauthn_login"), + re_path(r"^login/$", views.LoginView.as_view(), name="webauthn_login"), ] diff --git a/docs/source/conf.py b/docs/source/conf.py index 6c15ef20..2ad8c7f9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # djoser documentation build configuration file, created by # sphinx-quickstart on Fri Jun 2 10:56:45 2017. diff --git a/docs/source/webauthn.rst b/docs/source/webauthn.rst index 3cd8a27b..4795a18c 100644 --- a/docs/source/webauthn.rst +++ b/docs/source/webauthn.rst @@ -8,7 +8,8 @@ WebAuthn Configuration ============= -Assuming you have Djoser installed, add ``djoser.webauthn`` to ``INSTALLED_APPS``: +First make sure to install ``webauthn<1.0``. You can use webauthn as "extras" when installing djoser, e.g. ``pip install djoser[webauthn]`` or ``poetry add djoser -E webauthn``. +Then add ``djoser.webauthn`` to ``INSTALLED_APPS``: .. code-block:: python @@ -144,7 +145,7 @@ Running example app ------------------- .. code-block:: bash - + $ git clone git@github.com:sunscrapers/djoser.git $ pip install -r requirements.txt $ cd djoser/testproject diff --git a/poetry.lock b/poetry.lock index 31ebdb9a..7662fc66 100644 --- a/poetry.lock +++ b/poetry.lock @@ -72,6 +72,18 @@ jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] python2 = ["typed-ast (>=1.4.3)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "cbor2" +version = "5.4.2" +description = "Pure Python CBOR (de)serializer with extensive tag support" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.extras] +doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["pytest", "pytest-cov"] + [[package]] name = "certifi" version = "2021.10.8" @@ -293,9 +305,17 @@ python-versions = ">=3.7" docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +[[package]] +name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "main" +optional = true +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "identify" -version = "2.4.1" +version = "2.4.3" description = "File identification library for Python" category = "dev" optional = false @@ -487,6 +507,22 @@ dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] +[[package]] +name = "pyopenssl" +version = "21.0.0" +description = "Python wrapper module around the OpenSSL library" +category = "main" +optional = true +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" + +[package.dependencies] +cryptography = ">=3.3" +six = ">=1.5.2" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + [[package]] name = "pyparsing" version = "3.0.6" @@ -629,7 +665,7 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" @@ -741,7 +777,7 @@ python-versions = ">=3.6" [[package]] name = "urllib3" -version = "1.26.7" +version = "1.26.8" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -771,6 +807,21 @@ six = ">=1.9.0,<2" docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +[[package]] +name = "webauthn" +version = "0.4.7" +description = "A WebAuthn Python module." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +cbor2 = ">=4.0.1" +cryptography = ">=2.3.1" +future = ">=0.17.1" +pyOpenSSL = ">=16.0.0" +six = ">=1.11.0" + [[package]] name = "zipp" version = "3.7.0" @@ -785,11 +836,12 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [extras] test = ["pytest", "codecov", "coverage", "pytest-cov", "pytest-django", "pytest-pythonpath", "djet"] +webauthn = ["webauthn"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "c4570c105685677b48e31eb4e6fbf79317ae0e2c6e4c123e69048adc46d469a2" +content-hash = "49e4479bcf2db6321f19c554489a8bd111a22414c471ef33e6d867d968f21a8c" [metadata.files] asgiref = [ @@ -812,6 +864,21 @@ black = [ {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, ] +cbor2 = [ + {file = "cbor2-5.4.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0153635a78e62d70f26f5b3469cb8de822420eda69c996304fb3d0dc1a53d7f3"}, + {file = "cbor2-5.4.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fb47a4bca70ae2d1b2b6c9d5ed6c898494739966653f7c84737f795b26d754"}, + {file = "cbor2-5.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:10f178697e66eaae51534c3ef9acce1abf2f747e63c841e8702b9453c5bc4cfe"}, + {file = "cbor2-5.4.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:4c112b21fad53b2d936ef2d55c698641a6d49025c1a9cf73db3ef24684514db1"}, + {file = "cbor2-5.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93baf1ee33a305acf8f1391fed4e63ea2e837b561294fd6275ce8785acb795fa"}, + {file = "cbor2-5.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d9f64241bb30439f6ebde74101683e9dceecab95afe105560ac9d0e54d1b3c2e"}, + {file = "cbor2-5.4.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:2757a7b624478d3b6707adf8ab7aef03d81fdaca6bea981b2323069660b9360f"}, + {file = "cbor2-5.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32c1d00d8ad1a89f2358bf444fd43cacc3ca61f3c3feb1a2f2b2bea8ba4853d2"}, + {file = "cbor2-5.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:79c10306d9128258dea110c01abbe9c58c48ee2ffcf995f982fb063f1a82e2ee"}, + {file = "cbor2-5.4.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0e27c52abdccadadab858437f6d98d5e8711aa84f0af7417dd1404d16127db7f"}, + {file = "cbor2-5.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aec8f415f039ab3be6cb58861c21bd2202f5c0ad6537b31956da532ea74ad"}, + {file = "cbor2-5.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:310c7d7925f7aa6cb90791606be17c164bbf3b28d4d17047b5d19d303f2fe817"}, + {file = "cbor2-5.4.2.tar.gz", hash = "sha256:e283e70b55a049ff364cc5e648fde587e4d9b0e87e4b2664c69e639135e6b3b8"}, +] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, @@ -1000,9 +1067,12 @@ filelock = [ {file = "filelock-3.4.2-py3-none-any.whl", hash = "sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146"}, {file = "filelock-3.4.2.tar.gz", hash = "sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80"}, ] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] identify = [ - {file = "identify-2.4.1-py2.py3-none-any.whl", hash = "sha256:0192893ff68b03d37fed553e261d4a22f94ea974093aefb33b29df2ff35fed3c"}, - {file = "identify-2.4.1.tar.gz", hash = "sha256:64d4885e539f505dd8ffb5e93c142a1db45480452b1594cacd3e91dca9a984e9"}, + {file = "identify-2.4.3-py2.py3-none-any.whl", hash = "sha256:ac9c919c8ac5b90d03f8e1bce6dedcbd8a3b2a59c46737c04ed852e2d307af29"}, + {file = "identify-2.4.3.tar.gz", hash = "sha256:8d80d877441073e7222ff272da45ca5ca1d901412790544b446e7d363ad5e9f0"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, @@ -1139,6 +1209,10 @@ pyjwt = [ {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"}, {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"}, ] +pyopenssl = [ + {file = "pyOpenSSL-21.0.0-py2.py3-none-any.whl", hash = "sha256:8935bd4920ab9abfebb07c41a4f58296407ed77f04bd1a92914044b848ba1ed6"}, + {file = "pyOpenSSL-21.0.0.tar.gz", hash = "sha256:5e2d8c5e46d0d865ae933bef5230090bdaf5506281e9eec60fa250ee80600cb3"}, +] pyparsing = [ {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, @@ -1268,13 +1342,17 @@ uritemplate = [ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] urllib3 = [ - {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, - {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, + {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, + {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, ] virtualenv = [ {file = "virtualenv-20.13.0-py2.py3-none-any.whl", hash = "sha256:339f16c4a86b44240ba7223d0f93a7887c3ca04b5f9c8129da7958447d079b09"}, {file = "virtualenv-20.13.0.tar.gz", hash = "sha256:d8458cf8d59d0ea495ad9b34c2599487f8a7772d796f9910858376d1600dd2dd"}, ] +webauthn = [ + {file = "webauthn-0.4.7-py2.py3-none-any.whl", hash = "sha256:238391b2e2cc60fb51a2cd2d2d6be149920b9af6184651353d9f95856617a9e7"}, + {file = "webauthn-0.4.7.tar.gz", hash = "sha256:8ad9072ff1d6169f3be30d4dc8733ea563dd266962397bc58b40f674a6af74ac"}, +] zipp = [ {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, diff --git a/pyproject.toml b/pyproject.toml index bd81d0b2..6d696fdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,8 +47,12 @@ pytest-django = {version="^4.5.2", optional=true} pytest-pythonpath = {version="^0.7.3", optional=true} djet = {version="^0.3.0", optional=true} pytz = "^2021.3" +webauthn = {version="<1.0", optional=true} +[tool.poetry.group.dev.dependencies] +pyupgrade = "^2.31.0" + [tool.poetry.dev-dependencies] pre-commit = "^2.7.1" tox = "^3.20.0" @@ -66,7 +70,9 @@ test = [ "pytest-pythonpath", "djet", ] - +webauthn = [ + "webauthn", +] [tool.black] line-length = 88 diff --git a/testproject/testapp/static/js/base64.js b/testproject/testapp/static/js/base64.js index 3ebe0a92..69fcb107 100644 --- a/testproject/testapp/static/js/base64.js +++ b/testproject/testapp/static/js/base64.js @@ -115,4 +115,4 @@ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' exports.toByteArray = b64ToByteArray exports.fromByteArray = uint8ToBase64 -}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) \ No newline at end of file +}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) diff --git a/testproject/testapp/static/js/webauthn.js b/testproject/testapp/static/js/webauthn.js index d5d428c7..a5767d04 100644 --- a/testproject/testapp/static/js/webauthn.js +++ b/testproject/testapp/static/js/webauthn.js @@ -47,7 +47,7 @@ const transformCredentialCreateOptions = (credentialCreateOptionsFromServer) => challenge = Uint8Array.from( atob(credentialCreateOptionsFromServer.challenge), c => c.charCodeAt(0)); - + const transformedCredentialCreateOptions = Object.assign( {}, credentialCreateOptionsFromServer, {challenge, user}); @@ -62,7 +62,7 @@ const transformNewAssertionForServer = (newAssertion) => { newAssertion.response.clientDataJSON); const rawId = new Uint8Array( newAssertion.rawId); - + const registrationClientExtensions = newAssertion.getClientExtensionResults(); return { @@ -137,7 +137,7 @@ async function handleSignup(e) { const displayName = document.getElementById('signup-displayname').value; const credentialOptions = await getCredentialOptions(username, displayName); - + const rawAssertionForServer = await navigator.credentials.create({ publicKey: credentialOptions, }); diff --git a/testproject/testapp/tests/common.py b/testproject/testapp/tests/common.py index 3a8d9175..e0c2146a 100644 --- a/testproject/testapp/tests/common.py +++ b/testproject/testapp/tests/common.py @@ -6,7 +6,7 @@ try: from unittest import mock except ImportError: - import mock + from unittest import mock __all__ = [ "get_user_model", diff --git a/testproject/testapp/tests/test_reset_username.py b/testproject/testapp/tests/test_reset_username.py index e6b85e73..2829db19 100644 --- a/testproject/testapp/tests/test_reset_username.py +++ b/testproject/testapp/tests/test_reset_username.py @@ -20,7 +20,7 @@ class UsernameResetViewTest( APITestCase, assertions.StatusCodeAssertionsMixin, assertions.EmailAssertionsMixin ): def setUp(self): - self.base_url = reverse("user-reset-{}".format(User.USERNAME_FIELD)) + self.base_url = reverse(f"user-reset-{User.USERNAME_FIELD}") def test_post_should_send_email_to_user_with_username_reset_link(self): user = create_user() diff --git a/testproject/testapp/tests/test_reset_username_confirm.py b/testproject/testapp/tests/test_reset_username_confirm.py index de615de8..e9272617 100644 --- a/testproject/testapp/tests/test_reset_username_confirm.py +++ b/testproject/testapp/tests/test_reset_username_confirm.py @@ -19,7 +19,7 @@ class UsernameResetConfirmViewTest( APITestCase, assertions.EmailAssertionsMixin, assertions.StatusCodeAssertionsMixin ): def setUp(self): - self.base_url = reverse("user-reset-{}-confirm".format(User.USERNAME_FIELD)) + self.base_url = reverse(f"user-reset-{User.USERNAME_FIELD}-confirm") def test_post_set_new_username(self): user = create_user() diff --git a/testproject/testapp/tests/test_set_username.py b/testproject/testapp/tests/test_set_username.py index e1e37b1f..d2165e98 100644 --- a/testproject/testapp/tests/test_set_username.py +++ b/testproject/testapp/tests/test_set_username.py @@ -15,7 +15,7 @@ class SetUsernameViewTest( APITestCase, assertions.EmailAssertionsMixin, assertions.StatusCodeAssertionsMixin ): def setUp(self): - self.base_url = reverse("user-set-{}".format(User.USERNAME_FIELD)) + self.base_url = reverse(f"user-set-{User.USERNAME_FIELD}") def test_post_set_new_username(self): user = create_user() diff --git a/testproject/testapp/tests/webauthn/test_login_request.py b/testproject/testapp/tests/webauthn/test_login_request.py index 76a57cb2..da23dbf1 100644 --- a/testproject/testapp/tests/webauthn/test_login_request.py +++ b/testproject/testapp/tests/webauthn/test_login_request.py @@ -3,7 +3,6 @@ from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APITestCase - from testapp.tests.common import create_user from .utils import create_credential_options @@ -28,7 +27,7 @@ def test_post_with_username_not_registered_with_webauthn_should_return_400(self) self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) def test_post_with_username_registered_with_webauthn_should_return_login_assertion( - self + self, ): co = create_credential_options(with_user=True) data = {"username": co.username} diff --git a/testproject/testapp/tests/webauthn/test_signup_request.py b/testproject/testapp/tests/webauthn/test_signup_request.py index 9f2b9303..5431261d 100644 --- a/testproject/testapp/tests/webauthn/test_signup_request.py +++ b/testproject/testapp/tests/webauthn/test_signup_request.py @@ -2,9 +2,9 @@ from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APITestCase +from testapp.tests.common import create_user from djoser.webauthn.models import CredentialOptions -from testapp.tests.common import create_user class SignupRequestViewTest( diff --git a/testproject/testapp/tests/webauthn/utils.py b/testproject/testapp/tests/webauthn/utils.py index 82c7a32c..c1907b39 100644 --- a/testproject/testapp/tests/webauthn/utils.py +++ b/testproject/testapp/tests/webauthn/utils.py @@ -1,6 +1,7 @@ -from djoser.webauthn.models import CredentialOptions from testapp.tests.common import create_user +from djoser.webauthn.models import CredentialOptions + def create_credential_options( challenge="f00", diff --git a/testproject/testapp/validators.py b/testproject/testapp/validators.py index 23bea93e..f4d24201 100644 --- a/testproject/testapp/validators.py +++ b/testproject/testapp/validators.py @@ -1,7 +1,7 @@ from django.core.exceptions import ValidationError -class Is666(object): +class Is666: def validate(self, password, *args, **kwargs): if password == "666": raise ValidationError("Password 666 is not allowed.", code="no666") diff --git a/tox.ini b/tox.ini index d52ddfbf..3e62ba22 100644 --- a/tox.ini +++ b/tox.ini @@ -20,6 +20,7 @@ deps = drf313: djangorestframework>=3.13,<3.14 extras = test + webauthn commands = django-admin --version