This commit is contained in:
Gabriel Augendre 2021-12-26 23:24:33 +01:00
parent fcff91d027
commit c95e097f34
6 changed files with 161 additions and 12 deletions

139
poetry.lock generated
View file

@ -97,7 +97,7 @@ unicode_backport = ["unicodedata2"]
name = "colorama" name = "colorama"
version = "0.4.4" version = "0.4.4"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@ -200,13 +200,83 @@ python-versions = ">=3.6"
Django = ">=2.2" Django = ">=2.2"
sqlparse = ">=0.2.0" sqlparse = ">=0.2.0"
[[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-otp"
version = "1.1.3"
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-two-factor-auth"
version = "1.13"
description = ""
category = "main"
optional = false
python-versions = "*"
develop = false
[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)"]
sms = ["twilio (>=6.0)"]
yubikey = ["django-otp-yubikey"]
phonenumbers = ["phonenumbers (>=7.0.9,<8.99)"]
phonenumberslite = ["phonenumberslite (>=7.0.9,<8.99)"]
[package.source]
type = "git"
url = "https://github.com/Bouke/django-two-factor-auth.git"
reference = "ffe4422e"
resolved_reference = "ffe4422e6f68bfb84ad2b44ca83c15abb0af5e7c"
[[package]] [[package]]
name = "filelock" name = "filelock"
version = "3.4.0" version = "3.4.2"
description = "A platform independent file lock." description = "A platform independent file lock."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.extras] [package.extras]
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
@ -340,6 +410,14 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "phonenumberslite"
version = "8.12.40"
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "8.4.0" version = "8.4.0"
@ -350,11 +428,11 @@ python-versions = ">=3.6"
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "2.4.0" version = "2.4.1"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.extras] [package.extras]
docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
@ -559,6 +637,24 @@ category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[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]] [[package]]
name = "rcssmin" name = "rcssmin"
version = "1.1.0" version = "1.1.0"
@ -621,7 +717,7 @@ python-versions = ">=3.6.*"
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
description = "Python 2 and 3 compatibility utilities" description = "Python 2 and 3 compatibility utilities"
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
@ -748,7 +844,7 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "8ce7193640d549da552b67f5d485214f3e74931c801116164ad21614be32846e" content-hash = "30053d4662f7e86ae956249a199beeaed34b338fe420d09ad2db0ae4f9d2d8bd"
[metadata.files] [metadata.files]
asgiref = [ asgiref = [
@ -898,9 +994,22 @@ django-debug-toolbar = [
{file = "django-debug-toolbar-3.2.4.tar.gz", hash = "sha256:644bbd5c428d3283aa9115722471769cac1bec189edf3a0c855fd8ff870375a9"}, {file = "django-debug-toolbar-3.2.4.tar.gz", hash = "sha256:644bbd5c428d3283aa9115722471769cac1bec189edf3a0c855fd8ff870375a9"},
{file = "django_debug_toolbar-3.2.4-py3-none-any.whl", hash = "sha256:6b633b6cfee24f232d73569870f19aa86c819d750e7f3e833f2344a9eb4b4409"}, {file = "django_debug_toolbar-3.2.4-py3-none-any.whl", hash = "sha256:6b633b6cfee24f232d73569870f19aa86c819d750e7f3e833f2344a9eb4b4409"},
] ]
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-otp = [
{file = "django-otp-1.1.3.tar.gz", hash = "sha256:f002c71d4ea7f514590be00492980d3c87397b73dc20542e1c4fc00b66f2dda1"},
{file = "django_otp-1.1.3-py3-none-any.whl", hash = "sha256:8637be826c0465d0fd1710e4472efe9fc83883853a2141fefdbace9358d20003"},
]
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-two-factor-auth = []
filelock = [ filelock = [
{file = "filelock-3.4.0-py3-none-any.whl", hash = "sha256:2e139a228bcf56dd8b2274a65174d005c4a6b68540ee0bdbb92c76f43f29f7e8"}, {file = "filelock-3.4.2-py3-none-any.whl", hash = "sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146"},
{file = "filelock-3.4.0.tar.gz", hash = "sha256:93d512b32a23baf4cac44ffd72ccf70732aeff7b8050fcaf6d3ec406d954baf4"}, {file = "filelock-3.4.2.tar.gz", hash = "sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80"},
] ]
gunicorn = [ gunicorn = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
@ -1083,6 +1192,10 @@ packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
] ]
phonenumberslite = [
{file = "phonenumberslite-8.12.40-py2.py3-none-any.whl", hash = "sha256:e6fe6cad1091f8928e34a98570cade4758f4cf4e70e9e32ff7eca517ce98e273"},
{file = "phonenumberslite-8.12.40.tar.gz", hash = "sha256:153885eefec397058c8ce91fb987f55545b2cfa945f22a904584ddf162aa82b1"},
]
pillow = [ pillow = [
{file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"},
{file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"},
@ -1127,8 +1240,8 @@ pillow = [
{file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
] ]
platformdirs = [ platformdirs = [
{file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"},
{file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"},
] ]
pluggy = [ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@ -1227,6 +1340,10 @@ pyyaml = [
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
] ]
qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
]
rcssmin = [ rcssmin = [
{file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2211a5c91ea14a5937b57904c9121f8bfef20987825e55368143da7d25446e3b"}, {file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2211a5c91ea14a5937b57904c9121f8bfef20987825e55368143da7d25446e3b"},
{file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7085d1b51dd2556f3aae03947380f6e9e1da29fb1eeadfa6766b7f105c54c9ff"}, {file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7085d1b51dd2556f3aae03947380f6e9e1da29fb1eeadfa6766b7f105c54c9ff"},

View file

@ -22,6 +22,7 @@ django-debug-toolbar = "^3.2"
whitenoise = {extras = ["brotli"], version = "^5.2.0"} whitenoise = {extras = ["brotli"], version = "^5.2.0"}
rcssmin = "^1.0.6" rcssmin = "^1.0.6"
django-csp = "^3.7" django-csp = "^3.7"
django-two-factor-auth = {extras = ["phonenumberslite"], git = "https://github.com/Bouke/django-two-factor-auth.git", rev = "ffe4422e"}
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pre-commit = "^2.7" pre-commit = "^2.7"

View file

@ -0,0 +1,14 @@
.d-none {
display: none !important;
}
.float-right {
float: right;
}
td, th, tr, tbody, tr:nth-child(2n) {
background-color: inherit;
border: none;
padding-left: 0;
padding-right: 0;
}

View file

@ -0,0 +1,5 @@
{% extends "articles/base.html" %}
{% load static %}
{% block append_css %}
<link rel="stylesheet" href="{% static "login.css" %}">
{% endblock %}

View file

@ -71,6 +71,10 @@ INSTALLED_APPS = [
"anymail", "anymail",
"django_cleanup.apps.CleanupConfig", "django_cleanup.apps.CleanupConfig",
"debug_toolbar", "debug_toolbar",
"django_otp",
"django_otp.plugins.otp_static",
"django_otp.plugins.otp_totp",
"two_factor",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -82,6 +86,7 @@ MIDDLEWARE = [
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django_otp.middleware.OTPMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"csp.middleware.CSPMiddleware", "csp.middleware.CSPMiddleware",
@ -207,7 +212,12 @@ SHORTPIXEL_RESIZE_HEIGHT = int(os.getenv("SHORTPIXEL_RESIZE_HEIGHT", 10000))
GOATCOUNTER_DOMAIN = os.getenv("GOATCOUNTER_DOMAIN") GOATCOUNTER_DOMAIN = os.getenv("GOATCOUNTER_DOMAIN")
LOGIN_URL = "admin:login" LOGIN_URL = "two_factor:login"
LOGIN_REDIRECT_URL = "two_factor:profile"
LOGOUT_REDIRECT_URL = "articles-list"
TWO_FACTOR_REMEMBER_COOKIE_AGE = 86400 * 30
TWO_FACTOR_REMEMBER_COOKIE_SECURE = True
TWO_FACTOR_REMEMBER_COOKIE_SAMESITE = "Strict"
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin" SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_INCLUDE_SUBDOMAINS = True

View file

@ -18,10 +18,12 @@ from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from two_factor.urls import urlpatterns as tf_urls
from blog import settings from blog import settings
urlpatterns = [ urlpatterns = [
path("", include(tf_urls)),
path( path(
"robots.txt", "robots.txt",
TemplateView.as_view( TemplateView.as_view(