diff --git a/docker-compose.yml b/docker-compose.yml index 3ceb73f6c..d34c39c43 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,8 +23,6 @@ services: - DB_HOST=db - CACHE_DEFAULT=redis:6379/0 - CACHE_AXES=redis:6379/0 - - TWO_FACTOR_PATCH_ADMIN=no - - TWO_FACTOR_FORCE_OTP_ADMIN=no - CELERY_BROKER_URL=redis://redis:6379/0 - CELERY_RESULT_BACKEND=redis://redis:6379/0 - CELERY_LOGLEVEL=DEBUG diff --git a/dotenv.example b/dotenv.example index 6e0612257..c3ff2a195 100644 --- a/dotenv.example +++ b/dotenv.example @@ -8,3 +8,6 @@ DB_USER="sdg" DB_PASSWORD="" DB_HOST="" DB_PORT="" + +# Applies to dev settings module only! +DISABLE_2FA=yes diff --git a/package-lock.json b/package-lock.json index 679641cda..2027dbde6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sdg", - "version": "1.8.2", + "version": "1.8.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "sdg", - "version": "1.8.2", + "version": "1.8.4", "license": "UNLICENSED", "dependencies": { "@fortawesome/fontawesome-free": "^6.1.1", diff --git a/requirements/base.in b/requirements/base.in index 1d238d0ea..552109023 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -10,7 +10,7 @@ pydantic email-validator # Framework libraries -django==4.2.0 +django==3.2.0 django-admin-index django-better-admin-arrayfield django-axes[ipware] @@ -27,7 +27,7 @@ django-filter django-timeline-logger celery django-celery-beat -maykin-django-two-factor-auth[phonenumbers] +maykin-2fa # API libraries djangorestframework diff --git a/requirements/base.txt b/requirements/base.txt index a756f0240..8e9fc4bcc 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -10,6 +10,8 @@ annotated-types==0.6.0 # via pydantic asgiref==3.7.2 # via django +asn1crypto==1.5.1 + # via webauthn async-timeout==4.0.3 # via redis attrs==23.2.0 @@ -31,12 +33,14 @@ boltons==23.1.1 # via # face # glom +cbor2==5.6.1 + # via webauthn celery==5.3.6 # via # -r requirements/base.in # django-celery-beat # notifications-api-common -certifi==2023.11.17 +certifi==2024.2.2 # via # django-simple-certmanager # elastic-apm @@ -60,14 +64,15 @@ click-repl==0.3.0 # via celery cron-descriptor==1.4.3 # via django-celery-beat -cryptography==42.0.1 +cryptography==42.0.2 # via # django-simple-certmanager # pyjwt # pyopenssl + # webauthn defusedxml==0.7.1 # via python3-openid -django==3.2.23 +django==3.2 # via # -r requirements/base.in # django-admin-index @@ -96,20 +101,21 @@ django==3.2.23 # django-solo # django-timeline-logger # django-timezone-field + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular - # maykin-django-two-factor-auth + # maykin-2fa # notifications-api-common # vng-api-common # zgw-consumers django-admin-index==3.1.0 # via -r requirements/base.in -django-allauth==0.60.1 +django-allauth==0.61.1 # via -r requirements/base.in django-appconf==1.0.6 # via django-timeline-logger -django-axes==6.3.0 +django-axes[ipware]==6.3.0 # via -r requirements/base.in django-better-admin-arrayfield==1.4.2 # via -r requirements/base.in @@ -119,8 +125,6 @@ django-choices==2.0.0 # via # -r requirements/base.in # vng-api-common -django-compat==1.0.15 - # via django-hijack-admin django-decorator-include==3.0 # via -r requirements/base.in django-filter==23.5 @@ -128,13 +132,11 @@ django-filter==23.5 # -r requirements/base.in # vng-api-common django-formtools==2.5.1 - # via maykin-django-two-factor-auth -django-hijack==2.3.0 - # via - # -r requirements/base.in - # django-hijack-admin -django-hijack-admin==2.1.10 + # via django-two-factor-auth +django-hijack==3.4.5 # via -r requirements/base.in +django-ipware==6.0.4 + # via django-axes django-markdownify==0.9.3 # via -r requirements/base.in django-markdownx==4.0.7 @@ -142,9 +144,9 @@ django-markdownx==4.0.7 django-ordered-model==3.7.4 # via django-admin-index django-otp==1.3.0 - # via maykin-django-two-factor-auth -django-phonenumber-field==5.2.0 - # via maykin-django-two-factor-auth + # via django-two-factor-auth +django-phonenumber-field==7.3.0 + # via django-two-factor-auth django-privates==2.0.0.post0 # via django-simple-certmanager django-redis==5.4.0 @@ -174,6 +176,8 @@ django-timeline-logger==3.0.0 # via -r requirements/base.in django-timezone-field==6.1.0 # via django-celery-beat +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via maykin-2fa djangorestframework==3.12.4 # via # -r requirements/base.in @@ -202,7 +206,7 @@ email-validator==2.1.0.post1 # via -r requirements/base.in face==20.1.1 # via glom -faker==22.6.0 +faker==23.2.0 # via zgw-consumers gemma-zds-client==1.0.1 # via @@ -233,7 +237,7 @@ markdown==3.5.2 # via # django-markdownify # django-markdownx -maykin-django-two-factor-auth[phonenumbers]==2.0.4 +maykin-2fa==1.0.0 # via -r requirements/base.in notifications-api-common==0.2.2 # via vng-api-common @@ -241,8 +245,8 @@ oauthlib==3.2.2 # via requests-oauthlib oyaml==1.0 # via vng-api-common -phonenumbers==8.13.29 - # via maykin-django-two-factor-auth +phonenumberslite==8.13.30 + # via django-two-factor-auth pillow==10.2.0 # via # -r requirements/base.in @@ -255,9 +259,9 @@ psycopg2==2.8.6 # via -r requirements/base.in pycparser==2.21 # via cffi -pydantic==2.6.0 +pydantic==2.6.1 # via -r requirements/base.in -pydantic-core==2.16.1 +pydantic-core==2.16.2 # via pydantic pyjwt[crypto]==2.8.0 # via @@ -268,7 +272,10 @@ pyjwt[crypto]==2.8.0 pyopenssl==24.0.0 # via # django-simple-certmanager + # webauthn # zgw-consumers +pypng==0.20220715.0 + # via qrcode python-crontab==3.0.0 # via django-celery-beat python-dateutil==2.8.2 @@ -281,9 +288,11 @@ python-decouple==3.8 # via -r requirements/base.in python-dotenv==1.0.1 # via -r requirements/base.in +python-ipware==2.0.1 + # via django-ipware python3-openid==3.2.0 # via django-allauth -pytz==2023.4 +pytz==2024.1 # via # -r requirements/base.in # django @@ -293,8 +302,8 @@ pyyaml==6.0.1 # gemma-zds-client # oyaml # vng-api-common -qrcode==6.1 - # via maykin-django-two-factor-auth +qrcode==7.4.2 + # via django-two-factor-auth redis==5.0.1 # via django-redis referencing==0.33.0 @@ -314,19 +323,17 @@ requests-mock==1.11.0 # via zgw-consumers requests-oauthlib==1.3.1 # via django-allauth -rpds-py==0.17.1 +rpds-py==0.18.0 # via # jsonschema # referencing -sentry-sdk==1.39.2 +sentry-sdk==1.40.4 # via -r requirements/base.in six==1.16.0 # via # bleach - # django-compat # isodate # python-dateutil - # qrcode # requests-mock soupsieve==2.5 # via beautifulsoup4 @@ -339,18 +346,19 @@ typing-extensions==4.9.0 # asgiref # pydantic # pydantic-core -tzdata==2023.4 + # qrcode +tzdata==2024.1 # via # celery # django-celery-beat uritemplate==4.1.1 # via drf-spectacular -urllib3==2.1.0 +urllib3==2.2.0 # via # elastic-apm # requests # sentry-sdk -uwsgi==2.0.23 +uwsgi==2.0.24 # via -r requirements/base.in vine==5.1.0 # via @@ -361,6 +369,8 @@ vng-api-common==2.0.5 # via -r requirements/base.in wcwidth==0.2.13 # via prompt-toolkit +webauthn==2.0.0 + # via django-two-factor-auth webencodings==0.5.1 # via # bleach diff --git a/requirements/ci.txt b/requirements/ci.txt index 4bd400967..d388819a0 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -19,7 +19,12 @@ asgiref==3.7.2 # -c requirements/base.txt # -r requirements/base.txt # django -astroid==3.0.2 +asn1crypto==1.5.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # webauthn +astroid==3.0.3 # via pylint async-timeout==4.0.3 # via @@ -44,7 +49,7 @@ billiard==4.2.0 # -c requirements/base.txt # -r requirements/base.txt # celery -black==24.1.1 +black==24.2.0 # via -r requirements/test-tools.in bleach[css]==6.1.0 # via @@ -58,13 +63,18 @@ boltons==23.1.1 # -r requirements/base.txt # face # glom +cbor2==5.6.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # webauthn celery==5.3.6 # via # -c requirements/base.txt # -r requirements/base.txt # django-celery-beat # notifications-api-common -certifi==2023.11.17 +certifi==2024.2.2 # via # -c requirements/base.txt # -r requirements/base.txt @@ -113,13 +123,14 @@ cron-descriptor==1.4.3 # -c requirements/base.txt # -r requirements/base.txt # django-celery-beat -cryptography==42.0.1 +cryptography==42.0.2 # via # -c requirements/base.txt # -r requirements/base.txt # django-simple-certmanager # pyjwt # pyopenssl + # webauthn cssselect==1.2.0 # via pyquery ddt-api-calls==0.3.2 @@ -131,7 +142,7 @@ defusedxml==0.7.1 # python3-openid dill==0.3.8 # via pylint -django==3.2.23 +django==3.2 # via # -c requirements/base.txt # -r requirements/base.txt @@ -165,10 +176,11 @@ django==3.2.23 # django-solo # django-timeline-logger # django-timezone-field + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular - # maykin-django-two-factor-auth + # maykin-2fa # notifications-api-common # vng-api-common # zgw-consumers @@ -176,7 +188,7 @@ django-admin-index==3.1.0 # via # -c requirements/base.txt # -r requirements/base.txt -django-allauth==0.60.1 +django-allauth==0.61.1 # via # -c requirements/base.txt # -r requirements/base.txt @@ -185,10 +197,11 @@ django-appconf==1.0.6 # -c requirements/base.txt # -r requirements/base.txt # django-timeline-logger -django-axes==6.3.0 +django-axes[ipware]==6.3.0 # via # -c requirements/base.txt # -r requirements/base.txt + # django-axes django-better-admin-arrayfield==1.4.2 # via # -c requirements/base.txt @@ -202,12 +215,7 @@ django-choices==2.0.0 # -c requirements/base.txt # -r requirements/base.txt # vng-api-common -django-compat==1.0.15 - # via - # -c requirements/base.txt - # -r requirements/base.txt - # django-hijack-admin -django-debug-toolbar==4.2.0 +django-debug-toolbar==3.5.0 # via -r requirements/test-tools.in django-decorator-include==3.0 # via @@ -224,16 +232,16 @@ django-formtools==2.5.1 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth -django-hijack==2.3.0 + # django-two-factor-auth +django-hijack==3.4.5 # via # -c requirements/base.txt # -r requirements/base.txt - # django-hijack-admin -django-hijack-admin==2.1.10 +django-ipware==6.0.4 # via # -c requirements/base.txt # -r requirements/base.txt + # django-axes django-jenkins==0.110.0 # via -r requirements/test-tools.in django-markdownify==0.9.3 @@ -253,12 +261,12 @@ django-otp==1.3.0 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth -django-phonenumber-field==5.2.0 + # django-two-factor-auth +django-phonenumber-field==7.3.0 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-privates==2.0.0.post0 # via # -c requirements/base.txt @@ -321,6 +329,12 @@ django-timezone-field==6.1.0 # -c requirements/base.txt # -r requirements/base.txt # django-celery-beat +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-two-factor-auth + # maykin-2fa django-webtest==1.9.11 # via -r requirements/test-tools.in djangorestframework==3.12.4 @@ -372,7 +386,7 @@ face==20.1.1 # glom factory-boy==3.3.0 # via -r requirements/test-tools.in -faker==22.6.0 +faker==23.2.0 # via # -c requirements/base.txt # -r requirements/base.txt @@ -444,11 +458,10 @@ markdown==3.5.2 # -r requirements/base.txt # django-markdownify # django-markdownx -maykin-django-two-factor-auth[phonenumbers]==2.0.4 +maykin-2fa==1.0.0 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth mccabe==0.7.0 # via # flake8 @@ -476,17 +489,17 @@ pathspec==0.12.1 # via black pep8==1.7.1 # via -r requirements/test-tools.in -phonenumbers==8.13.29 +phonenumberslite==8.13.30 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth pillow==10.2.0 # via # -c requirements/base.txt # -r requirements/base.txt # django-markdownx -platformdirs==4.1.0 +platformdirs==4.2.0 # via # black # pylint @@ -511,11 +524,11 @@ pycparser==2.21 # -c requirements/base.txt # -r requirements/base.txt # cffi -pydantic==2.6.0 +pydantic==2.6.1 # via # -c requirements/base.txt # -r requirements/base.txt -pydantic-core==2.16.1 +pydantic-core==2.16.2 # via # -c requirements/base.txt # -r requirements/base.txt @@ -537,7 +550,13 @@ pyopenssl==24.0.0 # -c requirements/base.txt # -r requirements/base.txt # django-simple-certmanager + # webauthn # zgw-consumers +pypng==0.20220715.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # qrcode pyquery==2.0.0 # via -r requirements/test-tools.in python-crontab==3.0.0 @@ -562,12 +581,17 @@ python-dotenv==1.0.1 # via # -c requirements/base.txt # -r requirements/base.txt +python-ipware==2.0.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-ipware python3-openid==3.2.0 # via # -c requirements/base.txt # -r requirements/base.txt # django-allauth -pytz==2023.4 +pytz==2024.1 # via # -c requirements/base.txt # -r requirements/base.txt @@ -580,11 +604,11 @@ pyyaml==6.0.1 # gemma-zds-client # oyaml # vng-api-common -qrcode==6.1 +qrcode==7.4.2 # via # -c requirements/base.txt # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth redis==5.0.1 # via # -c requirements/base.txt @@ -620,13 +644,13 @@ requests-oauthlib==1.3.1 # -c requirements/base.txt # -r requirements/base.txt # django-allauth -rpds-py==0.17.1 +rpds-py==0.18.0 # via # -c requirements/base.txt # -r requirements/base.txt # jsonschema # referencing -sentry-sdk==1.39.2 +sentry-sdk==1.40.4 # via # -c requirements/base.txt # -r requirements/base.txt @@ -635,10 +659,8 @@ six==1.16.0 # -c requirements/base.txt # -r requirements/base.txt # bleach - # django-compat # isodate # python-dateutil - # qrcode # requests-mock soupsieve==2.5 # via @@ -673,7 +695,8 @@ typing-extensions==4.9.0 # black # pydantic # pydantic-core -tzdata==2023.4 + # qrcode +tzdata==2024.1 # via # -c requirements/base.txt # -r requirements/base.txt @@ -684,14 +707,14 @@ uritemplate==4.1.1 # -c requirements/base.txt # -r requirements/base.txt # drf-spectacular -urllib3==2.1.0 +urllib3==2.2.0 # via # -c requirements/base.txt # -r requirements/base.txt # elastic-apm # requests # sentry-sdk -uwsgi==2.0.23 +uwsgi==2.0.24 # via # -c requirements/base.txt # -r requirements/base.txt @@ -706,13 +729,18 @@ vng-api-common==2.0.5 # via # -c requirements/base.txt # -r requirements/base.txt -waitress==2.1.2 +waitress==3.0.0 # via webtest wcwidth==0.2.13 # via # -c requirements/base.txt # -r requirements/base.txt # prompt-toolkit +webauthn==2.0.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-two-factor-auth webencodings==0.5.1 # via # -c requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index 2538883e1..16031fc6f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -21,7 +21,12 @@ asgiref==3.7.2 # -c requirements/ci.txt # -r requirements/ci.txt # django -astroid==3.0.2 +asn1crypto==1.5.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # webauthn +astroid==3.0.3 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -51,7 +56,7 @@ billiard==4.2.0 # -c requirements/ci.txt # -r requirements/ci.txt # celery -black==24.1.1 +black==24.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -71,13 +76,18 @@ build==1.0.3 # via pip-tools bump2version==1.0.1 # via -r requirements/dev.in +cbor2==5.6.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # webauthn celery==5.3.6 # via # -c requirements/ci.txt # -r requirements/ci.txt # django-celery-beat # notifications-api-common -certifi==2023.11.17 +certifi==2024.2.2 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -131,13 +141,14 @@ cron-descriptor==1.4.3 # -c requirements/ci.txt # -r requirements/ci.txt # django-celery-beat -cryptography==42.0.1 +cryptography==42.0.2 # via # -c requirements/ci.txt # -r requirements/ci.txt # django-simple-certmanager # pyjwt # pyopenssl + # webauthn cssselect==1.2.0 # via # -c requirements/ci.txt @@ -159,7 +170,7 @@ dill==0.3.8 # pylint distlib==0.3.8 # via virtualenv -django==3.2.23 +django==3.2 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -193,10 +204,11 @@ django==3.2.23 # django-solo # django-timeline-logger # django-timezone-field + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular - # maykin-django-two-factor-auth + # maykin-2fa # notifications-api-common # vng-api-common # zgw-consumers @@ -204,7 +216,7 @@ django-admin-index==3.1.0 # via # -c requirements/ci.txt # -r requirements/ci.txt -django-allauth==0.60.1 +django-allauth==0.61.1 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -213,10 +225,11 @@ django-appconf==1.0.6 # -c requirements/ci.txt # -r requirements/ci.txt # django-timeline-logger -django-axes==6.3.0 +django-axes[ipware]==6.3.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-axes django-better-admin-arrayfield==1.4.2 # via # -c requirements/ci.txt @@ -230,12 +243,7 @@ django-choices==2.0.0 # -c requirements/ci.txt # -r requirements/ci.txt # vng-api-common -django-compat==1.0.15 - # via - # -c requirements/ci.txt - # -r requirements/ci.txt - # django-hijack-admin -django-debug-toolbar==4.2.0 +django-debug-toolbar==3.5.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -256,16 +264,16 @@ django-formtools==2.5.1 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth -django-hijack==2.3.0 + # django-two-factor-auth +django-hijack==3.4.5 # via # -c requirements/ci.txt # -r requirements/ci.txt - # django-hijack-admin -django-hijack-admin==2.1.10 +django-ipware==6.0.4 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-axes django-jenkins==0.110.0 # via # -c requirements/ci.txt @@ -287,12 +295,12 @@ django-otp==1.3.0 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth -django-phonenumber-field==5.2.0 + # django-two-factor-auth +django-phonenumber-field==7.3.0 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-privates==2.0.0.post0 # via # -c requirements/ci.txt @@ -355,6 +363,12 @@ django-timezone-field==6.1.0 # -c requirements/ci.txt # -r requirements/ci.txt # django-celery-beat +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-two-factor-auth + # maykin-2fa django-webtest==1.9.11 # via # -c requirements/ci.txt @@ -414,7 +428,7 @@ factory-boy==3.3.0 # via # -c requirements/ci.txt # -r requirements/ci.txt -faker==22.6.0 +faker==23.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -445,7 +459,7 @@ glom==23.5.0 # via # -c requirements/ci.txt # -r requirements/ci.txt -identify==2.5.33 +identify==2.5.34 # via pre-commit idna==3.6 # via @@ -503,13 +517,12 @@ markdown==3.5.2 # -r requirements/ci.txt # django-markdownify # django-markdownx -markupsafe==2.1.4 +markupsafe==2.1.5 # via jinja2 -maykin-django-two-factor-auth[phonenumbers]==2.0.4 +maykin-2fa==1.0.0 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth mccabe==0.7.0 # via # -c requirements/ci.txt @@ -554,11 +567,11 @@ pep8==1.7.1 # via # -c requirements/ci.txt # -r requirements/ci.txt -phonenumbers==8.13.29 +phonenumberslite==8.13.30 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth pillow==10.2.0 # via # -c requirements/ci.txt @@ -566,7 +579,7 @@ pillow==10.2.0 # django-markdownx pip-tools==7.3.0 # via -r requirements/dev.in -platformdirs==4.1.0 +platformdirs==4.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -578,7 +591,7 @@ polib==1.2.0 # -c requirements/ci.txt # -r requirements/ci.txt # django-rosetta -pre-commit==3.6.0 +pre-commit==3.6.1 # via -r requirements/dev.in prompt-toolkit==3.0.43 # via @@ -599,11 +612,11 @@ pycparser==2.21 # -c requirements/ci.txt # -r requirements/ci.txt # cffi -pydantic==2.6.0 +pydantic==2.6.1 # via # -c requirements/ci.txt # -r requirements/ci.txt -pydantic-core==2.16.1 +pydantic-core==2.16.2 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -632,7 +645,13 @@ pyopenssl==24.0.0 # -c requirements/ci.txt # -r requirements/ci.txt # django-simple-certmanager + # webauthn # zgw-consumers +pypng==0.20220715.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # qrcode pyproject-hooks==1.0.0 # via build pyquery==2.0.0 @@ -661,12 +680,17 @@ python-dotenv==1.0.1 # via # -c requirements/ci.txt # -r requirements/ci.txt +python-ipware==2.0.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-ipware python3-openid==3.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt # django-allauth -pytz==2023.4 +pytz==2024.1 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -680,11 +704,11 @@ pyyaml==6.0.1 # oyaml # pre-commit # vng-api-common -qrcode==6.1 +qrcode==7.4.2 # via # -c requirements/ci.txt # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth redis==5.0.1 # via # -c requirements/ci.txt @@ -720,13 +744,13 @@ requests-oauthlib==1.3.1 # -c requirements/ci.txt # -r requirements/ci.txt # django-allauth -rpds-py==0.17.1 +rpds-py==0.18.0 # via # -c requirements/ci.txt # -r requirements/ci.txt # jsonschema # referencing -sentry-sdk==1.39.2 +sentry-sdk==1.40.4 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -735,10 +759,8 @@ six==1.16.0 # -c requirements/ci.txt # -r requirements/ci.txt # bleach - # django-compat # isodate # python-dateutil - # qrcode # requests-mock smmap==5.0.1 # via gitdb @@ -808,7 +830,8 @@ typing-extensions==4.9.0 # black # pydantic # pydantic-core -tzdata==2023.4 + # qrcode +tzdata==2024.1 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -819,14 +842,14 @@ uritemplate==4.1.1 # -c requirements/ci.txt # -r requirements/ci.txt # drf-spectacular -urllib3==2.1.0 +urllib3==2.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt # elastic-apm # requests # sentry-sdk -uwsgi==2.0.23 +uwsgi==2.0.24 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -843,7 +866,7 @@ vng-api-common==2.0.5 # via # -c requirements/ci.txt # -r requirements/ci.txt -waitress==2.1.2 +waitress==3.0.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -853,6 +876,11 @@ wcwidth==0.2.13 # -c requirements/ci.txt # -r requirements/ci.txt # prompt-toolkit +webauthn==2.0.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-two-factor-auth webencodings==0.5.1 # via # -c requirements/ci.txt diff --git a/src/sdg/accounts/admin.py b/src/sdg/accounts/admin.py index 27bdfb62a..ca8cb2156 100644 --- a/src/sdg/accounts/admin.py +++ b/src/sdg/accounts/admin.py @@ -4,9 +4,9 @@ from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin as _UserAdmin from django.utils.translation import gettext_lazy as _ +from django.utils.html import format_html from allauth.account.models import EmailAddress -from compat import format_html from django_otp.plugins.otp_totp.admin import TOTPDeviceAdmin from ..core.events import post_event diff --git a/src/sdg/accounts/tests/test_admin.py b/src/sdg/accounts/tests/test_admin.py index b5f4dc292..12cf0f692 100644 --- a/src/sdg/accounts/tests/test_admin.py +++ b/src/sdg/accounts/tests/test_admin.py @@ -4,15 +4,12 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from django_webtest import WebTest - -from sdg.tests.utils import disable_2fa +from sdg.core.tests.utils import WebTest from ..models import UserInvitation from .factories import SuperUserFactory -@disable_2fa class AdminTests(WebTest): @classmethod def setUpTestData(cls): diff --git a/src/sdg/conf/base.py b/src/sdg/conf/base.py index 851985257..ea7ade2bd 100644 --- a/src/sdg/conf/base.py +++ b/src/sdg/conf/base.py @@ -109,6 +109,8 @@ "django_otp.plugins.otp_totp", "rijkshuisstijl", "two_factor", + "two_factor.plugins.webauthn", # USB key/hardware token support + "maykin_2fa", # Optional applications. "ordered_model", "django_admin_index", @@ -120,7 +122,6 @@ "django_better_admin_arrayfield", "axes", "sniplates", - "compat", # Part of hijack "hijack", "hijack.contrib.admin", "markdownx", @@ -160,7 +161,7 @@ "django.middleware.clickjacking.XFrameOptionsMiddleware", "hijack.middleware.HijackUserMiddleware", "axes.middleware.AxesMiddleware", - "django_otp.middleware.OTPMiddleware", + "maykin_2fa.middleware.OTPMiddleware", "allauth.account.middleware.AccountMiddleware", ] @@ -337,6 +338,7 @@ # Allow logging in with both username+password and email+password AUTHENTICATION_BACKENDS = [ "axes.backends.AxesBackend", + "sdg.accounts.backends.UserModelEmailBackend", "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ] @@ -445,12 +447,23 @@ SENTRY_DSN = config("SENTRY_DSN", None) # -# Maykin fork of DJANGO-TWO-FACTOR-AUTH +# MAYKIN-2FA # -TWO_FACTOR_FORCE_OTP_ADMIN = config("TWO_FACTOR_FORCE_OTP_ADMIN", default=not DEBUG) -TWO_FACTOR_PATCH_ADMIN = config("TWO_FACTOR_PATCH_ADMIN", default=True) -if "test" in sys.argv: # Allow testing with 2FA - TWO_FACTOR_FORCE_OTP = False + +# Uses django-two-factor-auth under the hood, so relevant upstream package settings +# apply too. +# + +# we run the admin site monkeypatch instead. +TWO_FACTOR_PATCH_ADMIN = False +# Relying Party name for WebAuthn (hardware tokens) +TWO_FACTOR_WEBAUTHN_RP_NAME = "SDG Invoervoorziening - admin" +# use platform for fingerprint readers etc., or remove the setting to allow any. +# cross-platform would limit the options to devices like phones/yubikeys +TWO_FACTOR_WEBAUTHN_AUTHENTICATOR_ATTACHMENT = "cross-platform" +# add entries from AUTHENTICATION_BACKENDS that already enforce their own two-factor +# auth, avoiding having some set up MFA again in the project. +MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = [] if SENTRY_DSN: SENTRY_CONFIG = { diff --git a/src/sdg/conf/dev.py b/src/sdg/conf/dev.py index 0e74c2f0f..afcbf650b 100644 --- a/src/sdg/conf/dev.py +++ b/src/sdg/conf/dev.py @@ -74,7 +74,7 @@ ELASTIC_APM["DEBUG"] = True # Django debug toolbar -INSTALLED_APPS += ["debug_toolbar", "ddt_api_calls"] +INSTALLED_APPS += ["debug_toolbar"] MIDDLEWARE += [ "debug_toolbar.middleware.DebugToolbarMiddleware", ] @@ -94,9 +94,12 @@ "debug_toolbar.panels.logging.LoggingPanel", "debug_toolbar.panels.redirects.RedirectsPanel", "debug_toolbar.panels.profiling.ProfilingPanel", - "ddt_api_calls.panels.APICallsPanel", ] +# None of the authentication backends require two-factor authentication. +if config("DISABLE_2FA", default=True): # pragma: no cover + MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS + # THOU SHALT NOT USE NAIVE DATETIMES warnings.filterwarnings( "error", diff --git a/src/sdg/conf/local_example.py b/src/sdg/conf/local_example.py index 24710de55..2131c4ca8 100644 --- a/src/sdg/conf/local_example.py +++ b/src/sdg/conf/local_example.py @@ -1,11 +1,6 @@ # # Any machine specific settings when using development settings. # -import os - -os.environ.setdefault("TWO_FACTOR_PATCH_ADMIN", "no") -os.environ.setdefault("TWO_FACTOR_FORCE_OTP_ADMIN", "no") - DATABASES = { "default": { diff --git a/src/sdg/conf/local_vng.py b/src/sdg/conf/local_vng.py index fea115a8c..ed8086e33 100644 --- a/src/sdg/conf/local_vng.py +++ b/src/sdg/conf/local_vng.py @@ -1,11 +1,6 @@ # # Any machine specific settings when using development settings. # -import os - -os.environ.setdefault("TWO_FACTOR_PATCH_ADMIN", "no"), -os.environ.setdefault("TWO_FACTOR_FORCE_OTP_ADMIN", "no"), - DATABASES = { "default": { diff --git a/src/sdg/core/tests/test_cms_enabled.py b/src/sdg/core/tests/test_cms_enabled.py index 913efa054..3a31867fe 100644 --- a/src/sdg/core/tests/test_cms_enabled.py +++ b/src/sdg/core/tests/test_cms_enabled.py @@ -1,19 +1,16 @@ import uuid -import django from django.test import override_settings from django.urls import reverse -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.tests.factories import RoleFactory, SuperUserFactory from sdg.core.tests.factories.catalogus import ProductenCatalogusFactory from sdg.core.tests.factories.logius import OverheidsorganisatieFactory from sdg.organisaties.tests.factories.overheid import LokaleOverheidFactory -from sdg.tests.utils import disable_2fa -@disable_2fa class CMSUrlsPathTest(WebTest): def setUp(self): super().setUp() diff --git a/src/sdg/core/tests/test_config.py b/src/sdg/core/tests/test_config.py index dbdac90c0..44703000a 100644 --- a/src/sdg/core/tests/test_config.py +++ b/src/sdg/core/tests/test_config.py @@ -1,6 +1,6 @@ from django.test import TestCase -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.tests.factories import UserFactory from sdg.conf.types.organization import ( diff --git a/src/sdg/core/tests/test_views.py b/src/sdg/core/tests/test_views.py index a8fcbb1df..935fa50b8 100644 --- a/src/sdg/core/tests/test_views.py +++ b/src/sdg/core/tests/test_views.py @@ -1,7 +1,7 @@ from django.test import override_settings from django.urls import reverse -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.tests.factories import RoleFactory, UserFactory from sdg.conf.utils import org_type_cfg diff --git a/src/sdg/core/tests/utils.py b/src/sdg/core/tests/utils.py index 9cf634e0e..433f01937 100644 --- a/src/sdg/core/tests/utils.py +++ b/src/sdg/core/tests/utils.py @@ -1,3 +1,13 @@ +from django_webtest import WebTest as DjangoWebTest + +from maykin_2fa.test import disable_admin_mfa as disable_mfa + + +@disable_mfa() +class WebTest(DjangoWebTest): + pass + + def hard_refresh_from_db(obj): """Retrieve the same object from database. Clears any annotations and cached properties.""" obj.refresh_from_db() diff --git a/src/sdg/fixtures/admin_index.json b/src/sdg/fixtures/admin_index.json index d9885c7b5..f5fbe980c 100644 --- a/src/sdg/fixtures/admin_index.json +++ b/src/sdg/fixtures/admin_index.json @@ -91,6 +91,10 @@ [ "otp_totp", "totpdevice" + ], + [ + "two_factor_webauthn", + "webauthndevice" ] ] } diff --git a/src/sdg/organisaties/management/commands/send_notification_mail.py b/src/sdg/organisaties/management/commands/send_notification_mail.py index f45ba2716..a6eef344d 100644 --- a/src/sdg/organisaties/management/commands/send_notification_mail.py +++ b/src/sdg/organisaties/management/commands/send_notification_mail.py @@ -6,8 +6,8 @@ from django.db.models import Q from django.utils.html import strip_tags from django.utils.timezone import now +from django.template.loader import render_to_string -from compat import render_to_string from dateutil.relativedelta import relativedelta from sdg.accounts.models import User diff --git a/src/sdg/organisaties/tests/test_bevoegde_organisaties.py b/src/sdg/organisaties/tests/test_bevoegde_organisaties.py index 2a321f666..b3d386bdf 100644 --- a/src/sdg/organisaties/tests/test_bevoegde_organisaties.py +++ b/src/sdg/organisaties/tests/test_bevoegde_organisaties.py @@ -1,6 +1,6 @@ from django.urls import reverse_lazy -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.tests.factories import RoleFactory, UserFactory from sdg.core.tests.factories.logius import OverheidsorganisatieFactory diff --git a/src/sdg/organisaties/tests/test_invitation.py b/src/sdg/organisaties/tests/test_invitation.py index 8a04c5f05..6cd4ab242 100644 --- a/src/sdg/organisaties/tests/test_invitation.py +++ b/src/sdg/organisaties/tests/test_invitation.py @@ -5,7 +5,7 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.models import UserInvitation from sdg.accounts.tests.factories import RoleFactory, UserFactory diff --git a/src/sdg/organisaties/tests/test_notificaties.py b/src/sdg/organisaties/tests/test_notificaties.py index c2c633864..b7ba0794b 100644 --- a/src/sdg/organisaties/tests/test_notificaties.py +++ b/src/sdg/organisaties/tests/test_notificaties.py @@ -4,7 +4,7 @@ from django.urls import reverse from django.utils.timezone import now -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.models import UserInvitation from sdg.accounts.tests.factories import RoleFactory, UserFactory diff --git a/src/sdg/organisaties/tests/test_overheden.py b/src/sdg/organisaties/tests/test_overheden.py index b0f5ffd86..cb29fdf1d 100644 --- a/src/sdg/organisaties/tests/test_overheden.py +++ b/src/sdg/organisaties/tests/test_overheden.py @@ -5,12 +5,11 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from freezegun import freeze_time from sdg.accounts.tests.factories import RoleFactory, UserFactory from sdg.core.constants import GenericProductStatus -from sdg.core.models import ProductenCatalogus from sdg.core.tests.factories.catalogus import ProductenCatalogusFactory from sdg.core.tests.factories.logius import OverheidsorganisatieFactory from sdg.organisaties.tests.factories.overheid import ( diff --git a/src/sdg/organisaties/tests/test_roles.py b/src/sdg/organisaties/tests/test_roles.py index fcd6a6977..295aa3f62 100644 --- a/src/sdg/organisaties/tests/test_roles.py +++ b/src/sdg/organisaties/tests/test_roles.py @@ -1,6 +1,6 @@ from django.urls import reverse_lazy -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from sdg.accounts.models import Role from sdg.accounts.tests.factories import RoleFactory, UserFactory diff --git a/src/sdg/producten/tests/test_product.py b/src/sdg/producten/tests/test_product.py index f5de2b2a0..6e73c2933 100644 --- a/src/sdg/producten/tests/test_product.py +++ b/src/sdg/producten/tests/test_product.py @@ -5,7 +5,7 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from django_webtest import WebTest +from sdg.core.tests.utils import WebTest from freezegun import freeze_time from sdg.accounts.tests.factories import RoleFactory, UserFactory diff --git a/src/sdg/templates/admin/base_site.html b/src/sdg/templates/admin/base_site.html index 24c12c748..df2ff0fb2 100644 --- a/src/sdg/templates/admin/base_site.html +++ b/src/sdg/templates/admin/base_site.html @@ -34,9 +34,9 @@

{% trans 'Wachtwoord wijzigen' %} / {% endif %} - {% url 'admin:two_factor:profile' as 2fa_profile_url %} - {% if 2fa_profile_url %} - {% trans "Beheer two-factor auth" %} / + {% url 'maykin_2fa:account_security' as 2fa_account_security_url %} + {% if 2fa_account_security_url %} + {% trans "Beheer two-factor auth" %} / {% endif %} {% trans 'Uitloggen' %} diff --git a/src/sdg/templates/two_factor/core/login.html b/src/sdg/templates/two_factor/core/login.html index 8319d2946..26cb66c0c 100644 --- a/src/sdg/templates/two_factor/core/login.html +++ b/src/sdg/templates/two_factor/core/login.html @@ -1,5 +1,5 @@ {% extends "account/base.html" %} -{% load i18n two_factor utils static %} +{% load i18n utils static %} {% block nav-global %}{% endblock %} {% block django_admin_index_menu %}{% endblock %} @@ -35,16 +35,6 @@

Inloggen

{# hidden submit button to enable [enter] key #} - {% if other_devices %} -

{% trans "Or, alternatively, use one of your backup phones:" %}

-

- {% for other in other_devices %} - - {% endfor %}

- {% endif %} {% if backup_tokens %}

{% trans "As a last resort, you can use a backup token:" %}

diff --git a/src/sdg/tests/utils.py b/src/sdg/tests/utils.py deleted file mode 100644 index 4c22ecd12..000000000 --- a/src/sdg/tests/utils.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import override_settings - -disable_2fa = override_settings(TWO_FACTOR_PATCH_ADMIN=False) diff --git a/src/sdg/urls.py b/src/sdg/urls.py index 1596bfe17..80eeb149e 100644 --- a/src/sdg/urls.py +++ b/src/sdg/urls.py @@ -9,12 +9,17 @@ from decorator_include import decorator_include from two_factor.urls import urlpatterns as tf_urls +from maykin_2fa import monkeypatch_admin +from maykin_2fa.urls import urlpatterns, webauthn_urlpatterns from sdg import miscellaneous_urls from sdg.accounts.views.password_reset import PasswordResetView, ResendInventation from sdg.decorators import enabled from sdg.organisaties.views.notificaties import ProductVersieListView +# Configure admin +monkeypatch_admin() + handler500 = "sdg.utils.views.server_error" admin.site.site_header = _("PDC voor de SDG") admin.site.site_title = _("PDC voor de SDG") @@ -35,6 +40,8 @@ ), path("admin/hijack/", include("hijack.urls")), path("admin/resend_invite", ResendInventation.as_view(), name="resend_token"), + path("admin/", include((urlpatterns, "maykin_2fa"))), + path("admin/", include((webauthn_urlpatterns, "admin_two_factor"))), path("admin/", admin.site.urls), path("api/", include("sdg.api.urls", namespace="api")), # cms - these urls can be disabled if desired though the setting SDG_CMS_ENABLED