From 25e0bc11972184f6a01e7a5353edc8680c40c697 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Tue, 24 Aug 2021 21:41:21 +0200 Subject: [PATCH] Add two factor dependency --- poetry.lock | 118 +++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + src/newsreader/conf/base.py | 6 ++ src/newsreader/urls.py | 2 + 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index d098bd6..f812065 100644 --- a/poetry.lock +++ b/poetry.lock @@ -192,7 +192,7 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -298,6 +298,17 @@ python-versions = "*" [package.dependencies] six = ">=1.2" +[[package]] +name = "django-formtools" +version = "2.3" +description = "A set of high-level abstractions for Django forms" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Django = ">=2.2" + [[package]] name = "django-ipware" version = "4.0.0" @@ -306,6 +317,35 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +[[package]] +name = "django-otp" +version = "1.0.6" +description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +django = ">=2.2" + +[package.extras] +qrcode = ["qrcode"] + +[[package]] +name = "django-phonenumber-field" +version = "5.2.0" +description = "An international phone number field for django models." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Django = ">=2.2" + +[package.extras] +phonenumbers = ["phonenumbers (>=7.0.2)"] +phonenumberslite = ["phonenumberslite (>=7.0.2)"] + [[package]] name = "django-registration-redux" version = "2.9" @@ -329,6 +369,29 @@ pytz = "*" [package.extras] rest_framework = ["djangorestframework (>=3.0.0)"] +[[package]] +name = "django-two-factor-auth" +version = "1.13.1" +description = "Complete Two-Factor Authentication for Django" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = ">=2.2" +django-formtools = "*" +django-otp = ">=0.8.0" +django-phonenumber-field = ">=1.1.0,<6" +phonenumberslite = {version = ">=7.0.9,<8.99", optional = true, markers = "extra == \"phonenumberslite\""} +qrcode = ">=4.0.0,<6.99" + +[package.extras] +call = ["twilio (>=6.0)"] +phonenumbers = ["phonenumbers (>=7.0.9,<8.99)"] +phonenumberslite = ["phonenumberslite (>=7.0.9,<8.99)"] +sms = ["twilio (>=6.0)"] +yubikey = ["django-otp-yubikey"] + [[package]] name = "djangorestframework" version = "3.12.4" @@ -575,6 +638,14 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2" +[[package]] +name = "phonenumberslite" +version = "8.12.31" +description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "psycopg2-binary" version = "2.9.1" @@ -655,6 +726,24 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "qrcode" +version = "6.1" +description = "QR Code image generator" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +six = "*" + +[package.extras] +dev = ["tox", "pytest", "mock"] +maintainer = ["zest.releaser"] +pil = ["pillow"] +test = ["pytest", "pytest-cov", "mock"] + [[package]] name = "requests" version = "2.26.0" @@ -1007,10 +1096,22 @@ django-extensions = [ {file = "django-extensions-2.2.9.tar.gz", hash = "sha256:2f81b618ba4d1b0e58603e25012e5c74f88a4b706e0022a3b21f24f0322a6ce6"}, {file = "django_extensions-2.2.9-py2.py3-none-any.whl", hash = "sha256:b19182d101a441fe001c5753553a901e2ef3ff60e8fbbe38881eb4a61fdd17c4"}, ] +django-formtools = [ + {file = "django-formtools-2.3.tar.gz", hash = "sha256:9663b6eca64777b68d6d4142efad8597fe9a685924673b25aa8a1dcff4db00c3"}, + {file = "django_formtools-2.3-py3-none-any.whl", hash = "sha256:4699937e19ee041d803943714fe0c1c7ad4cab802600eb64bbf4cdd0a1bfe7d9"}, +] django-ipware = [ {file = "django-ipware-4.0.0.tar.gz", hash = "sha256:1294f916f3b3475e40e1b0ec1bd320aa2397978eae672721c81cbc2ed517e9ee"}, {file = "django_ipware-4.0.0-py2.py3-none-any.whl", hash = "sha256:116bd0d7940f09bf7ffd465943992e23d87e772a9d6c0d3a57b74040589a383b"}, ] +django-otp = [ + {file = "django-otp-1.0.6.tar.gz", hash = "sha256:0d56dd2a7fbb6ee6e54557e036ca64add0bd3596f471794bad673b7637d5e935"}, + {file = "django_otp-1.0.6-py3-none-any.whl", hash = "sha256:01b5888f0bde5125e139433aacb947e52d5c406fa56c9db43c3e8d75b5c323c4"}, +] +django-phonenumber-field = [ + {file = "django-phonenumber-field-5.2.0.tar.gz", hash = "sha256:52b2e5970133ec5ab701218b802f7ab237229854dc95fd239b7e9e77dc43731d"}, + {file = "django_phonenumber_field-5.2.0-py3-none-any.whl", hash = "sha256:5547fb2b2cc690a306ba77a5038419afc8fa8298a486fb7895008e9067cc7e75"}, +] django-registration-redux = [ {file = "django-registration-redux-2.9.tar.gz", hash = "sha256:e3d123354a1b8cbfa005d60f1ebb89ae8541f3eaffd6174d9f2aff529b57e430"}, {file = "django_registration_redux-2.9-py2.py3-none-any.whl", hash = "sha256:e94b8a945e1cbfa9ec6c32b549597270405328d4e26651985d287d0211120691"}, @@ -1019,6 +1120,10 @@ django-timezone-field = [ {file = "django-timezone-field-4.2.1.tar.gz", hash = "sha256:97780cde658daa5094ae515bb55ca97c1352928ab554041207ad515dee3fe971"}, {file = "django_timezone_field-4.2.1-py3-none-any.whl", hash = "sha256:6dc782e31036a58da35b553bd00c70f112d794700025270d8a6a4c1d2e5b26c6"}, ] +django-two-factor-auth = [ + {file = "django-two-factor-auth-1.13.1.tar.gz", hash = "sha256:a20e03d256fd9fd668988545f052cedcc47e5a981888562e5e27d0bb83deae89"}, + {file = "django_two_factor_auth-1.13.1-py2.py3-none-any.whl", hash = "sha256:d270d4288731233621a9462a89a8dfed2dcb86fa354125c816a89772d55f9e29"}, +] djangorestframework = [ {file = "djangorestframework-3.12.4-py3-none-any.whl", hash = "sha256:6d1d59f623a5ad0509fe0d6bfe93cbdfe17b8116ebc8eda86d45f6e16e819aaf"}, {file = "djangorestframework-3.12.4.tar.gz", hash = "sha256:f747949a8ddac876e879190df194b925c177cdeb725a099db1460872f7c0a7f2"}, @@ -1159,6 +1264,10 @@ packaging = [ {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, ] +phonenumberslite = [ + {file = "phonenumberslite-8.12.31-py2.py3-none-any.whl", hash = "sha256:c593d2716dee6726f30d8e13c2fabf4b6d15551adfeb6a424c893c65686fb829"}, + {file = "phonenumberslite-8.12.31.tar.gz", hash = "sha256:19ba2c15b0926707e670e58faafe80957344db4bae1479d74fa4ec34b3d8632a"}, +] psycopg2-binary = [ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"}, @@ -1217,6 +1326,10 @@ pytz = [ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] +qrcode = [ + {file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"}, + {file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"}, +] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, @@ -1260,6 +1373,9 @@ sentry-sdk = [ sgmllib3k = [ {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, ] +sgmllib3k = [ + {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index 6b3757f..bcaed9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ python-dotenv = "^0.12.0" sentry-sdk = {version = "^1.0.0", optional = true} ftfy = "^5.8" requests_oauthlib = "^1.3.0" +django-two-factor-auth = {extras = ["phonenumberslite"], version = "^1.13.1"} [tool.poetry.extras] sentry = ["sentry_sdk"] diff --git a/src/newsreader/conf/base.py b/src/newsreader/conf/base.py index cd51218..c1f3b16 100644 --- a/src/newsreader/conf/base.py +++ b/src/newsreader/conf/base.py @@ -42,6 +42,10 @@ INSTALLED_APPS = [ "django_celery_beat", "registration", "axes", + "django_otp", + "django_otp.plugins.otp_static", + "django_otp.plugins.otp_totp", + "two_factor", # app modules "newsreader.accounts", "newsreader.utils", @@ -61,6 +65,7 @@ MIDDLEWARE = [ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "django_otp.middleware.OTPMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "axes.middleware.AxesMiddleware", @@ -182,6 +187,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Authentication user model AUTH_USER_MODEL = "accounts.User" +LOGIN_URL = "two_factor:login" LOGIN_REDIRECT_URL = "/" # Internationalization diff --git a/src/newsreader/urls.py b/src/newsreader/urls.py index 0779b29..c80ba2f 100644 --- a/src/newsreader/urls.py +++ b/src/newsreader/urls.py @@ -5,6 +5,7 @@ from django.urls import include, path from drf_yasg import openapi from drf_yasg.views import get_schema_view +from two_factor.urls import urlpatterns as two_factor_urls from newsreader.accounts.urls import urlpatterns as login_urls from newsreader.news.core.views import NewsView @@ -18,6 +19,7 @@ schema_info = openapi.Info(title="Newsreader API", default_version="v1") schema_view = get_schema_view(schema_info, patterns=api_patterns) urlpatterns = [ + path("", include(two_factor_urls)), path("", login_required(NewsView.as_view()), name="index"), path("", include((news_patterns, "news"))), path("", include((api_patterns, "api"))),