From 9dea2d37148e76c718a48b64f421b8f84a68ce64 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:55:18 +0100 Subject: [PATCH 01/15] Upgrade `django-axes` to `6.3.0` Updated settings from the default project --- requirements/base.txt | 4 +--- requirements/ci.txt | 6 +----- requirements/dev.txt | 6 +----- src/objects/conf/base.py | 10 +++------- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index feaa7bd3..428f4ba0 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -67,7 +67,7 @@ django==3.2.23 # zgw-consumers django-admin-index==3.1.0 # via -r requirements/base.in -django-axes==5.41.1 +django-axes==6.3.0 # via -r requirements/base.in django-choices==2.0.0 # via vng-api-common @@ -77,8 +77,6 @@ django-filter==2.4.0 # vng-api-common django-formtools==2.3 # via maykin-django-two-factor-auth -django-ipware==3.0.2 - # via django-axes django-jsonform==2.21.4 # via mozilla-django-oidc-db django-markup==1.3 diff --git a/requirements/ci.txt b/requirements/ci.txt index 0f320a93..31091ce0 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -86,7 +86,7 @@ django==3.2.23 # zgw-consumers django-admin-index==3.1.0 # via -r requirements/base.txt -django-axes==5.41.1 +django-axes==6.3.0 # via -r requirements/base.txt django-choices==2.0.0 # via @@ -100,10 +100,6 @@ django-formtools==2.3 # via # -r requirements/base.txt # maykin-django-two-factor-auth -django-ipware==3.0.2 - # via - # -r requirements/base.txt - # django-axes django-jsonform==2.21.4 # via # -r requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index 1f2e664a..b17b1b37 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -110,7 +110,7 @@ django==3.2.23 # zgw-consumers django-admin-index==3.1.0 # via -r requirements/ci.txt -django-axes==5.41.1 +django-axes==6.3.0 # via -r requirements/ci.txt django-choices==2.0.0 # via @@ -128,10 +128,6 @@ django-formtools==2.3 # via # -r requirements/ci.txt # maykin-django-two-factor-auth -django-ipware==3.0.2 - # via - # -r requirements/ci.txt - # django-axes django-jsonform==2.21.4 # via # -r requirements/ci.txt diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index d77e8afb..ca8adce0 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -331,7 +331,7 @@ "objects.utils.admin_index.should_display_dropdown_menu" ) -# Django-Axes (4.0+) +# Django-Axes # # The number of login attempts allowed before a record is created for the # failed logins. Default: 3 @@ -340,15 +340,11 @@ # will be forgotten. Can be set to a python timedelta object or an integer. If # an integer, will be interpreted as a number of hours. Default: None AXES_COOLOFF_TIME = 1 -# If True only locks based on user id and never locks by IP if attempts limit -# exceed, otherwise utilize the existing IP and user locking logic Default: -# False -AXES_ONLY_USER_FAILURES = True # If set, specifies a template to render when a user is locked out. Template # receives cooloff_time and failure_limit as context variables. Default: None AXES_LOCKOUT_TEMPLATE = "account_blocked.html" -AXES_USE_USER_AGENT = True # Default: False -AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP = True # Default: False +AXES_LOCKOUT_TEMPLATE = "account_blocked.html" +AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent", "username"]] # The default meta precedence order IPWARE_META_PRECEDENCE_ORDER = ( From c8be30b67413885b0d9adfaeb625d0c94ecf06fe Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 13 Feb 2024 11:17:17 +0100 Subject: [PATCH 02/15] Upgrade dependencies to be compatible with `4.2` These are the straightforward ones --- requirements/base.txt | 7 +++---- requirements/ci.txt | 7 +++---- requirements/dev.txt | 7 +++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 428f4ba0..d269bf31 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -71,7 +71,7 @@ django-axes==6.3.0 # via -r requirements/base.in django-choices==2.0.0 # via vng-api-common -django-filter==2.4.0 +django-filter==23.5 # via # -r requirements/base.in # vng-api-common @@ -89,7 +89,7 @@ django-phonenumber-field==5.2.0 # via maykin-django-two-factor-auth django-privates==2.0.0.post0 # via django-simple-certmanager -django-redis==5.2.0 +django-redis==5.4.0 # via -r requirements/base.in django-relativedelta==2.0.0 # via zgw-consumers @@ -154,7 +154,7 @@ jinja2==3.1.3 # via coreschema josepy==1.9.0 # via mozilla-django-oidc -jsonschema==3.2.0 +jsonschema==4.17.3 # via # -r requirements/base.in # drf-spectacular @@ -234,7 +234,6 @@ six==1.15.0 # via # django-markup # isodate - # jsonschema # python-dateutil # qrcode # requests-mock diff --git a/requirements/ci.txt b/requirements/ci.txt index 31091ce0..8de3075a 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -92,7 +92,7 @@ django-choices==2.0.0 # via # -r requirements/base.txt # vng-api-common -django-filter==2.4.0 +django-filter==23.5 # via # -r requirements/base.txt # vng-api-common @@ -122,7 +122,7 @@ django-privates==2.0.0.post0 # via # -r requirements/base.txt # django-simple-certmanager -django-redis==5.2.0 +django-redis==5.4.0 # via -r requirements/base.txt django-relativedelta==2.0.0 # via @@ -227,7 +227,7 @@ josepy==1.9.0 # via # -r requirements/base.txt # mozilla-django-oidc -jsonschema==3.2.0 +jsonschema==4.17.3 # via # -r requirements/base.txt # drf-spectacular @@ -345,7 +345,6 @@ six==1.15.0 # -r requirements/base.txt # django-markup # isodate - # jsonschema # python-dateutil # qrcode # requests-mock diff --git a/requirements/dev.txt b/requirements/dev.txt index b17b1b37..907bbc4c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -120,7 +120,7 @@ django-debug-toolbar==4.2.0 # via -r requirements/dev.in django-extensions==3.2.3 # via -r requirements/dev.in -django-filter==2.4.0 +django-filter==23.5 # via # -r requirements/ci.txt # vng-api-common @@ -150,7 +150,7 @@ django-privates==2.0.0.post0 # via # -r requirements/ci.txt # django-simple-certmanager -django-redis==5.2.0 +django-redis==5.4.0 # via -r requirements/ci.txt django-relativedelta==2.0.0 # via @@ -268,7 +268,7 @@ josepy==1.9.0 # via # -r requirements/ci.txt # mozilla-django-oidc -jsonschema==3.2.0 +jsonschema==4.17.3 # via # -r requirements/ci.txt # drf-spectacular @@ -414,7 +414,6 @@ six==1.15.0 # -r requirements/ci.txt # django-markup # isodate - # jsonschema # python-dateutil # qrcode # requests-mock From 7ed2dd6175176cfeb1e001dc2eb43c79c65446e5 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:32:03 +0100 Subject: [PATCH 03/15] Add Celery configuration This will be needed for the new notifications_api_common library --- Dockerfile | 1 + bin/celery_worker.sh | 23 +++++++++++++++++++++++ docker-compose.yml | 21 ++++++++++++++++++--- src/objects/celery.py | 9 +++++++++ src/objects/conf/base.py | 12 ++++++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100755 bin/celery_worker.sh create mode 100644 src/objects/celery.py diff --git a/Dockerfile b/Dockerfile index 2d5e1778..906a7155 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,6 +51,7 @@ COPY --from=backend-build /usr/local/bin/uwsgi /usr/local/bin/uwsgi # Stage 3.2 - Copy source code WORKDIR /app COPY ./bin/docker_start.sh /start.sh +COPY ./bin/celery_worker.sh /celery_worker.sh RUN mkdir /app/log /app/config # copy frontend build statics diff --git a/bin/celery_worker.sh b/bin/celery_worker.sh new file mode 100755 index 00000000..33fdf84a --- /dev/null +++ b/bin/celery_worker.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +LOGLEVEL=${CELERY_LOGLEVEL:-INFO} +CONCURRENCY=${CELERY_WORKER_CONCURRENCY:-1} + +QUEUE=${1:-${CELERY_WORKER_QUEUE:=celery}} +WORKER_NAME=${2:-${CELERY_WORKER_NAME:="${QUEUE}"@%n}} + +_binary=$(which celery) + +if [[ "$ENABLE_COVERAGE" ]]; then + _binary="coverage run $_binary" +fi + +echo "Starting celery worker $WORKER_NAME with queue $QUEUE" +exec $_binary --workdir src --app objects.celery worker \ + -Q $QUEUE \ + -n $WORKER_NAME \ + -l $LOGLEVEL \ + -O fair \ + -c $CONCURRENCY diff --git a/docker-compose.yml b/docker-compose.yml index db1a1f51..12a173d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,9 +9,14 @@ services: - POSTGRES_USER=${DB_USER:-objects} - POSTGRES_PASSWORD=${DB_PASSWORD:-objects} + redis: + image: redis:7 + command: ["redis-server", "--appendonly", "yes"] + web: - build: . - environment: + build: &web_build + context: . + environment: &web_env - DJANGO_SETTINGS_MODULE=objects.conf.docker - SECRET_KEY=${SECRET_KEY:-1(@f(-6s_u(5fd&1sg^uvu2s(c-9sapw)1era8q&)g)h@cwxxg} - OBJECTS_SUPERUSER_USERNAME=admin @@ -22,8 +27,18 @@ services: - 8000:8000 depends_on: - db - volumes: + - redis + volumes: &web_volumes - media:/app/media # Shared media volume to get access to saved OAS files + celery: + build: *web_build + environment: *web_env + command: /celery_worker.sh + depends_on: + - db + - redis + volumes: *web_volumes + volumes: media: diff --git a/src/objects/celery.py b/src/objects/celery.py new file mode 100644 index 00000000..f5c5e0e0 --- /dev/null +++ b/src/objects/celery.py @@ -0,0 +1,9 @@ +from celery import Celery + +from .setup import setup_env + +setup_env() + +app = Celery("objects") +app.config_from_object("django.conf:settings", namespace="CELERY") +app.autodiscover_tasks() diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index ca8adce0..6a60a6b4 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -433,3 +433,15 @@ OIDC_AUTHENTICATE_CLASS = "mozilla_django_oidc_db.views.OIDCAuthenticationRequestView" MOZILLA_DJANGO_OIDC_DB_CACHE = "oidc" MOZILLA_DJANGO_OIDC_DB_CACHE_TIMEOUT = 5 * 60 + +# +# CELERY - async task queue +# +CELERY_BROKER_URL = config("CELERY_BROKER_URL", "redis://localhost:6379/0") +CELERY_RESULT_BACKEND = config("CELERY_RESULT_BACKEND", "redis://localhost:6379/0") + +# Add (by default) 5 (soft), 15 (hard) minute timeouts to all Celery tasks. +CELERY_TASK_TIME_LIMIT = config("CELERY_TASK_HARD_TIME_LIMIT", default=15 * 60) # hard +CELERY_TASK_SOFT_TIME_LIMIT = config( + "CELERY_TASK_SOFT_TIME_LIMIT", default=5 * 60 +) # soft From 51da9af4f5aac76003f32a25785e8e228e1aec27 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:11:48 +0100 Subject: [PATCH 04/15] Switch to commonground-api-common --- requirements/base.in | 4 +- requirements/base.txt | 91 ++++++++++---- requirements/ci.txt | 111 ++++++++++++++---- requirements/dev.txt | 101 ++++++++++++---- src/objects/__init__.py | 3 + src/objects/api/kanalen.py | 2 +- src/objects/api/mixins.py | 10 +- src/objects/conf/base.py | 2 +- src/objects/fixtures/default_admin_index.json | 2 +- .../tests/v1/test_notifications_send.py | 49 +++----- .../tests/v2/test_notifications_kanaal.py | 46 +++++--- .../tests/v2/test_notifications_send.py | 49 +++----- src/objects/urls.py | 2 +- 13 files changed, 311 insertions(+), 161 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index 82102590..14052f5d 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,4 +1,5 @@ # Core python libraries +celery glom # data represenation based on spec Pillow # handle images psycopg2 # database driver @@ -30,5 +31,6 @@ sentry-sdk # error monitoring elastic-apm # Elastic APM integration # Common ground libraries -vng-api-common[markdown_docs]>=1.6.4 +notifications-api-common +commonground-api-common[markdown_docs] zgw-consumers # external api auths diff --git a/requirements/base.txt b/requirements/base.txt index d269bf31..f7245d2e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,16 +4,24 @@ # # pip-compile --no-emit-index-url requirements/base.in # +amqp==5.2.0 + # via kombu asgiref==3.7.2 # via django attrs==20.3.0 # via # glom # jsonschema +billiard==3.6.4.0 + # via celery boltons==21.0.0 # via # face # glom +celery==5.2.2 + # via + # -r requirements/base.in + # notifications-api-common certifi==2020.12.5 # via # django-simple-certmanager @@ -24,8 +32,24 @@ cffi==1.16.0 # via cryptography chardet==4.0.0 # via requests +click==8.1.7 + # via + # celery + # click-didyoumean + # click-plugins + # click-repl +click-didyoumean==0.3.0 + # via celery +click-plugins==1.1.1 + # via celery +click-repl==0.3.0 + # via celery +commonground-api-common[markdown-docs,markdown_docs]==1.12.1 + # via -r requirements/base.in coreapi==2.3.3 - # via drf-yasg + # via + # commonground-api-common + # drf-yasg coreschema==0.0.4 # via # coreapi @@ -39,9 +63,9 @@ cryptography==41.0.7 django==3.2.23 # via # -r requirements/base.in + # commonground-api-common # django-admin-index # django-axes - # django-choices # django-filter # django-formtools # django-jsonform @@ -63,24 +87,22 @@ django==3.2.23 # maykin-django-two-factor-auth # mozilla-django-oidc # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers django-admin-index==3.1.0 # via -r requirements/base.in django-axes==6.3.0 # via -r requirements/base.in -django-choices==2.0.0 - # via vng-api-common django-filter==23.5 # via # -r requirements/base.in - # vng-api-common + # commonground-api-common django-formtools==2.3 # via maykin-django-two-factor-auth django-jsonform==2.21.4 # via mozilla-django-oidc-db django-markup==1.3 - # via vng-api-common + # via commonground-api-common django-ordered-model==3.7.4 # via django-admin-index django-otp==1.0.6 @@ -94,7 +116,7 @@ django-redis==5.4.0 django-relativedelta==2.0.0 # via zgw-consumers django-rest-framework-condition==0.1.1 - # via vng-api-common + # via commonground-api-common django-rosetta==0.9.8 # via -r requirements/base.in django-sendfile2==0.7.0 @@ -103,27 +125,31 @@ django-simple-certmanager==1.4.1 # via zgw-consumers django-solo==2.2.0 # via + # commonground-api-common # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers djangorestframework==3.12.4 # via # -r requirements/base.in + # commonground-api-common # djangorestframework-gis # drf-nested-routers # drf-spectacular # drf-yasg - # vng-api-common + # notifications-api-common djangorestframework-camel-case==1.2.0 - # via vng-api-common + # via + # commonground-api-common + # notifications-api-common djangorestframework-gis==1.0 # via -r requirements/base.in drf-nested-routers==0.93.3 - # via vng-api-common + # via commonground-api-common drf-spectacular==0.16.0 # via -r requirements/base.in drf-yasg==1.20.0 - # via vng-api-common + # via commonground-api-common elastic-apm==6.1.1 # via -r requirements/base.in face==20.1.1 @@ -132,7 +158,8 @@ faker==8.1.0 # via zgw-consumers gemma-zds-client==1.0.1 # via - # vng-api-common + # commonground-api-common + # notifications-api-common # zgw-consumers glom==23.5.0 # via @@ -145,9 +172,9 @@ inflection==0.5.1 # drf-spectacular # drf-yasg iso-639==0.4.5 - # via vng-api-common + # via commonground-api-common isodate==0.6.0 - # via vng-api-common + # via commonground-api-common itypes==1.2.0 # via coreapi jinja2==3.1.3 @@ -158,8 +185,10 @@ jsonschema==4.17.3 # via # -r requirements/base.in # drf-spectacular +kombu==5.3.5 + # via celery markdown==3.3.4 - # via vng-api-common + # via commonground-api-common markupsafe==2.1.3 # via jinja2 maykin-django-two-factor-auth[phonenumbers]==2.0.3 @@ -168,8 +197,12 @@ mozilla-django-oidc==4.0.0 # via mozilla-django-oidc-db mozilla-django-oidc-db==0.14.1 # via -r requirements/base.in +notifications-api-common==0.2.2 + # via + # -r requirements/base.in + # commonground-api-common oyaml==1.0 - # via vng-api-common + # via commonground-api-common packaging==23.2 # via drf-yasg phonenumbers==8.12.29 @@ -178,14 +211,16 @@ pillow==10.2.0 # via -r requirements/base.in polib==1.1.1 # via django-rosetta +prompt-toolkit==3.0.43 + # via click-repl psycopg2==2.8.6 # via -r requirements/base.in pycparser==2.20 # via cffi pyjwt==2.4.0 # via + # commonground-api-common # gemma-zds-client - # vng-api-common pyopenssl==23.3.0 # via # django-simple-certmanager @@ -204,25 +239,26 @@ python-dotenv==1.0.0 pytz==2021.1 # via # -r requirements/base.in + # celery # django pyyaml==6.0.1 # via + # commonground-api-common # drf-spectacular # gemma-zds-client # oyaml - # vng-api-common qrcode==6.1 # via maykin-django-two-factor-auth redis==3.5.3 # via django-redis requests==2.25.1 # via + # commonground-api-common # coreapi # django-rosetta # gemma-zds-client # mozilla-django-oidc # requests-mock - # vng-api-common # zgw-consumers requests-mock==1.8.0 # via zgw-consumers @@ -255,12 +291,17 @@ urllib3==1.26.6 # sentry-sdk uwsgi==2.0.21 # via -r requirements/base.in -vng-api-common[markdown-docs]==1.8.0 +vine==5.1.0 # via - # -r requirements/base.in - # vng-api-common + # amqp + # celery + # kombu +wcwidth==0.2.13 + # via prompt-toolkit zgw-consumers==0.27.0 - # via -r requirements/base.in + # via + # -r requirements/base.in + # notifications-api-common # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/requirements/ci.txt b/requirements/ci.txt index 8de3075a..f57fd03f 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -4,6 +4,10 @@ # # pip-compile --no-emit-index-url --output-file=requirements/ci.txt requirements/base.txt requirements/test-tools.in # +amqp==5.2.0 + # via + # -r requirements/base.txt + # kombu asgiref==3.7.2 # via # -r requirements/base.txt @@ -15,11 +19,19 @@ attrs==20.3.0 # jsonschema beautifulsoup4==4.9.3 # via webtest +billiard==3.6.4.0 + # via + # -r requirements/base.txt + # celery boltons==21.0.0 # via # -r requirements/base.txt # face # glom +celery==5.2.2 + # via + # -r requirements/base.txt + # notifications-api-common certifi==2020.12.5 # via # -r requirements/base.txt @@ -35,9 +47,33 @@ chardet==4.0.0 # via # -r requirements/base.txt # requests +click==8.1.7 + # via + # -r requirements/base.txt + # celery + # click-didyoumean + # click-plugins + # click-repl +click-didyoumean==0.3.0 + # via + # -r requirements/base.txt + # celery +click-plugins==1.1.1 + # via + # -r requirements/base.txt + # celery +click-repl==0.3.0 + # via + # -r requirements/base.txt + # celery +commonground-api-common[markdown-docs]==1.12.1 + # via + # -r requirements/base.txt + # commonground-api-common coreapi==2.3.3 # via # -r requirements/base.txt + # commonground-api-common # drf-yasg coreschema==0.0.4 # via @@ -58,9 +94,9 @@ cssselect==1.1.0 django==3.2.23 # via # -r requirements/base.txt + # commonground-api-common # django-admin-index # django-axes - # django-choices # django-filter # django-formtools # django-jsonform @@ -82,20 +118,16 @@ django==3.2.23 # maykin-django-two-factor-auth # mozilla-django-oidc # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers django-admin-index==3.1.0 # via -r requirements/base.txt django-axes==6.3.0 # via -r requirements/base.txt -django-choices==2.0.0 - # via - # -r requirements/base.txt - # vng-api-common django-filter==23.5 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common django-formtools==2.3 # via # -r requirements/base.txt @@ -105,7 +137,9 @@ django-jsonform==2.21.4 # -r requirements/base.txt # mozilla-django-oidc-db django-markup==1.3 - # via -r requirements/base.txt + # via + # -r requirements/base.txt + # commonground-api-common django-ordered-model==3.7.4 # via # -r requirements/base.txt @@ -131,7 +165,7 @@ django-relativedelta==2.0.0 django-rest-framework-condition==0.1.1 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common django-rosetta==0.9.8 # via -r requirements/base.txt django-sendfile2==0.7.0 @@ -145,35 +179,38 @@ django-simple-certmanager==1.4.1 django-solo==2.2.0 # via # -r requirements/base.txt + # commonground-api-common # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers django-webtest==1.9.7 # via -r requirements/test-tools.in djangorestframework==3.12.4 # via # -r requirements/base.txt + # commonground-api-common # djangorestframework-gis # drf-nested-routers # drf-spectacular # drf-yasg - # vng-api-common + # notifications-api-common djangorestframework-camel-case==1.2.0 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common + # notifications-api-common djangorestframework-gis==1.0 # via -r requirements/base.txt drf-nested-routers==0.93.3 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common drf-spectacular==0.16.0 # via -r requirements/base.txt drf-yasg==1.20.0 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common elastic-apm==6.1.1 # via -r requirements/base.txt face==20.1.1 @@ -192,7 +229,8 @@ freezegun==1.1.0 gemma-zds-client==1.0.1 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common + # notifications-api-common # zgw-consumers glom==23.5.0 # via @@ -210,11 +248,11 @@ inflection==0.5.1 iso-639==0.4.5 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common isodate==0.6.0 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common itypes==1.2.0 # via # -r requirements/base.txt @@ -231,10 +269,16 @@ jsonschema==4.17.3 # via # -r requirements/base.txt # drf-spectacular +kombu==5.3.5 + # via + # -r requirements/base.txt + # celery lxml==4.7.1 # via pyquery markdown==3.3.4 - # via -r requirements/base.txt + # via + # -r requirements/base.txt + # commonground-api-common markupsafe==2.1.3 # via # -r requirements/base.txt @@ -249,10 +293,14 @@ mozilla-django-oidc==4.0.0 # mozilla-django-oidc-db mozilla-django-oidc-db==0.14.1 # via -r requirements/base.txt +notifications-api-common==0.2.2 + # via + # -r requirements/base.txt + # commonground-api-common oyaml==1.0 # via # -r requirements/base.txt - # vng-api-common + # commonground-api-common packaging==23.2 # via # -r requirements/base.txt @@ -267,6 +315,10 @@ polib==1.1.1 # via # -r requirements/base.txt # django-rosetta +prompt-toolkit==3.0.43 + # via + # -r requirements/base.txt + # click-repl psycopg2==2.8.6 # via -r requirements/base.txt pycparser==2.20 @@ -276,8 +328,8 @@ pycparser==2.20 pyjwt==2.4.0 # via # -r requirements/base.txt + # commonground-api-common # gemma-zds-client - # vng-api-common pyopenssl==23.3.0 # via # -r requirements/base.txt @@ -303,14 +355,15 @@ python-dotenv==1.0.0 pytz==2021.1 # via # -r requirements/base.txt + # celery # django pyyaml==6.0.1 # via # -r requirements/base.txt + # commonground-api-common # drf-spectacular # gemma-zds-client # oyaml - # vng-api-common qrcode==6.1 # via # -r requirements/base.txt @@ -322,12 +375,12 @@ redis==3.5.3 requests==2.25.1 # via # -r requirements/base.txt + # commonground-api-common # coreapi # django-rosetta # gemma-zds-client # mozilla-django-oidc # requests-mock - # vng-api-common # zgw-consumers requests-mock==1.8.0 # via @@ -379,18 +432,26 @@ urllib3==1.26.6 # sentry-sdk uwsgi==2.0.21 # via -r requirements/base.txt -vng-api-common[markdown-docs]==1.8.0 +vine==5.1.0 # via # -r requirements/base.txt - # vng-api-common + # amqp + # celery + # kombu waitress==2.1.1 # via webtest +wcwidth==0.2.13 + # via + # -r requirements/base.txt + # prompt-toolkit webob==1.8.7 # via webtest webtest==2.0.35 # via django-webtest zgw-consumers==0.27.0 - # via -r requirements/base.txt + # via + # -r requirements/base.txt + # notifications-api-common # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/requirements/dev.txt b/requirements/dev.txt index 907bbc4c..e919ecca 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -6,6 +6,10 @@ # alabaster==0.7.12 # via sphinx +amqp==5.2.0 + # via + # -r requirements/ci.txt + # kombu asgiref==3.7.2 # via # -r requirements/ci.txt @@ -21,6 +25,10 @@ beautifulsoup4==4.9.3 # via # -r requirements/ci.txt # webtest +billiard==3.6.4.0 + # via + # -r requirements/ci.txt + # celery black==23.12.1 # via -r requirements/dev.in boltons==21.0.0 @@ -34,6 +42,10 @@ bump2version==1.0.1 # via bumpversion bumpversion==0.6.0 # via -r requirements/dev.in +celery==5.2.2 + # via + # -r requirements/ci.txt + # notifications-api-common certifi==2020.12.5 # via # -r requirements/ci.txt @@ -51,13 +63,35 @@ chardet==4.0.0 # requests click==8.1.7 # via + # -r requirements/ci.txt # black + # celery + # click-didyoumean + # click-plugins + # click-repl # pip-tools +click-didyoumean==0.3.0 + # via + # -r requirements/ci.txt + # celery +click-plugins==1.1.1 + # via + # -r requirements/ci.txt + # celery +click-repl==0.3.0 + # via + # -r requirements/ci.txt + # celery +commonground-api-common[markdown-docs]==1.12.1 + # via + # -r requirements/ci.txt + # commonground-api-common commonmark==0.9.1 # via recommonmark coreapi==2.3.3 # via # -r requirements/ci.txt + # commonground-api-common # drf-yasg coreschema==0.0.4 # via @@ -80,9 +114,9 @@ cssselect==1.1.0 django==3.2.23 # via # -r requirements/ci.txt + # commonground-api-common # django-admin-index # django-axes - # django-choices # django-debug-toolbar # django-extensions # django-filter @@ -106,16 +140,12 @@ django==3.2.23 # maykin-django-two-factor-auth # mozilla-django-oidc # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers django-admin-index==3.1.0 # via -r requirements/ci.txt django-axes==6.3.0 # via -r requirements/ci.txt -django-choices==2.0.0 - # via - # -r requirements/ci.txt - # vng-api-common django-debug-toolbar==4.2.0 # via -r requirements/dev.in django-extensions==3.2.3 @@ -123,7 +153,7 @@ django-extensions==3.2.3 django-filter==23.5 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common django-formtools==2.3 # via # -r requirements/ci.txt @@ -159,7 +189,7 @@ django-relativedelta==2.0.0 django-rest-framework-condition==0.1.1 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common django-rosetta==0.9.8 # via -r requirements/ci.txt django-sendfile2==0.7.0 @@ -173,23 +203,26 @@ django-simple-certmanager==1.4.1 django-solo==2.2.0 # via # -r requirements/ci.txt + # commonground-api-common # mozilla-django-oidc-db - # vng-api-common + # notifications-api-common # zgw-consumers django-webtest==1.9.7 # via -r requirements/ci.txt djangorestframework==3.12.4 # via # -r requirements/ci.txt + # commonground-api-common # djangorestframework-gis # drf-nested-routers # drf-spectacular # drf-yasg - # vng-api-common + # notifications-api-common djangorestframework-camel-case==1.2.0 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common + # notifications-api-common djangorestframework-gis==1.0 # via -r requirements/ci.txt docutils==0.18.1 @@ -201,13 +234,13 @@ docutils==0.18.1 drf-nested-routers==0.93.3 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common drf-spectacular==0.16.0 # via -r requirements/ci.txt drf-yasg==1.20.0 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common elastic-apm==6.1.1 # via -r requirements/ci.txt face==20.1.1 @@ -228,7 +261,8 @@ freezegun==1.1.0 gemma-zds-client==1.0.1 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common + # notifications-api-common # zgw-consumers glom==23.5.0 # via @@ -248,11 +282,11 @@ inflection==0.5.1 iso-639==0.4.5 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common isodate==0.6.0 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common isort==5.13.2 # via -r requirements/dev.in itypes==1.2.0 @@ -272,6 +306,10 @@ jsonschema==4.17.3 # via # -r requirements/ci.txt # drf-spectacular +kombu==5.3.5 + # via + # -r requirements/ci.txt + # celery lxml==4.7.1 # via # -r requirements/ci.txt @@ -296,10 +334,14 @@ mozilla-django-oidc-db==0.14.1 # via -r requirements/ci.txt mypy-extensions==0.4.3 # via black +notifications-api-common==0.2.2 + # via + # -r requirements/ci.txt + # commonground-api-common oyaml==1.0 # via # -r requirements/ci.txt - # vng-api-common + # commonground-api-common packaging==23.2 # via # -r requirements/ci.txt @@ -323,6 +365,10 @@ polib==1.1.1 # via # -r requirements/ci.txt # django-rosetta +prompt-toolkit==3.0.43 + # via + # -r requirements/ci.txt + # click-repl psycopg2==2.8.6 # via -r requirements/ci.txt pycodestyle==2.11.1 @@ -340,8 +386,8 @@ pygments==2.17.2 pyjwt==2.4.0 # via # -r requirements/ci.txt + # commonground-api-common # gemma-zds-client - # vng-api-common pyopenssl==23.3.0 # via # -r requirements/ci.txt @@ -370,14 +416,15 @@ pytz==2021.1 # via # -r requirements/ci.txt # babel + # celery # django pyyaml==6.0.1 # via # -r requirements/ci.txt + # commonground-api-common # drf-spectacular # gemma-zds-client # oyaml - # vng-api-common qrcode==6.1 # via # -r requirements/ci.txt @@ -391,13 +438,13 @@ redis==3.5.3 requests==2.25.1 # via # -r requirements/ci.txt + # commonground-api-common # coreapi # django-rosetta # gemma-zds-client # mozilla-django-oidc # requests-mock # sphinx - # vng-api-common # zgw-consumers requests-mock==1.8.0 # via @@ -485,14 +532,20 @@ urllib3==1.26.6 # sentry-sdk uwsgi==2.0.21 # via -r requirements/ci.txt -vng-api-common[markdown-docs]==1.8.0 +vine==5.1.0 # via # -r requirements/ci.txt - # vng-api-common + # amqp + # celery + # kombu waitress==2.1.1 # via # -r requirements/ci.txt # webtest +wcwidth==0.2.13 + # via + # -r requirements/ci.txt + # prompt-toolkit webob==1.8.7 # via # -r requirements/ci.txt @@ -504,7 +557,9 @@ webtest==2.0.35 wheel==0.42.0 # via pip-tools zgw-consumers==0.27.0 - # via -r requirements/ci.txt + # via + # -r requirements/ci.txt + # notifications-api-common # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/src/objects/__init__.py b/src/objects/__init__.py index 85d26250..77df6d6b 100644 --- a/src/objects/__init__.py +++ b/src/objects/__init__.py @@ -1,3 +1,6 @@ +from .celery import app as celery_app + +__all__ = ("celery_app",) __version__ = "2.2.1" __author__ = "Maykin Media" __homepage__ = "https://github.com/maykinmedia/objects-api" diff --git a/src/objects/api/kanalen.py b/src/objects/api/kanalen.py index 2ac56ccf..d2b29bb1 100644 --- a/src/objects/api/kanalen.py +++ b/src/objects/api/kanalen.py @@ -3,7 +3,7 @@ from django.conf import settings from django.db import models -from vng_api_common.notifications.kanalen import Kanaal +from notifications_api_common.kanalen import Kanaal from objects.core.models import ObjectRecord diff --git a/src/objects/api/mixins.py b/src/objects/api/mixins.py index dad3010d..1731b3bf 100644 --- a/src/objects/api/mixins.py +++ b/src/objects/api/mixins.py @@ -1,5 +1,10 @@ from django.db import models +from notifications_api_common.viewsets import ( + NotificationCreateMixin, + NotificationDestroyMixin, + conditional_atomic, +) from rest_framework.exceptions import NotAcceptable from rest_framework.renderers import BrowsableAPIRenderer from vng_api_common.exceptions import PreconditionFailed @@ -10,11 +15,6 @@ GeoMixin as _GeoMixin, extract_header, ) -from vng_api_common.notifications.viewsets import ( - NotificationCreateMixin, - NotificationDestroyMixin, - conditional_atomic, -) class GeoMixin(_GeoMixin): diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index 6a60a6b4..1972340e 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -79,7 +79,7 @@ "solo", "django_markup", "vng_api_common", - "vng_api_common.notifications", + "notifications_api_common", "simple_certmanager", "zgw_consumers", # 2fa apps diff --git a/src/objects/fixtures/default_admin_index.json b/src/objects/fixtures/default_admin_index.json index b11e8500..48aa5b94 100644 --- a/src/objects/fixtures/default_admin_index.json +++ b/src/objects/fixtures/default_admin_index.json @@ -29,7 +29,7 @@ "appgroup" ], [ - "notifications", + "notifications_api_common", "notificationsconfig" ], [ diff --git a/src/objects/tests/v1/test_notifications_send.py b/src/objects/tests/v1/test_notifications_send.py index 75d4884f..43621815 100644 --- a/src/objects/tests/v1/test_notifications_send.py +++ b/src/objects/tests/v1/test_notifications_send.py @@ -4,9 +4,9 @@ import requests_mock from freezegun import freeze_time +from notifications_api_common.models import NotificationsConfig from rest_framework import status from rest_framework.test import APITestCase -from vng_api_common.notifications.models import NotificationsConfig from zgw_consumers.constants import APITypes from zgw_consumers.models import Service @@ -20,12 +20,7 @@ from objects.utils.test import TokenAuthMixin from ..constants import GEO_WRITE_KWARGS -from ..utils import ( - mock_objecttype, - mock_objecttype_version, - mock_service_oas_get, - notifications_client_mock, -) +from ..utils import mock_objecttype, mock_objecttype_version, mock_service_oas_get from .utils import reverse OBJECT_TYPES_API = "https://example.com/objecttypes/v1/" @@ -49,9 +44,8 @@ def setUpTestData(cls): def setUp(self): super().setUp() - config = NotificationsConfig.get_solo() - Service.objects.update_or_create( - api_root=config.api_root, + service, _ = Service.objects.update_or_create( + api_root="https://notificaties-api.vng.cloud/api/v1/", defaults=dict( api_type=APITypes.nrc, client_id="test", @@ -60,13 +54,15 @@ def setUp(self): user_representation="Test", ), ) + config = NotificationsConfig.get_solo() + config.notifications_api_service = service + config.save() - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_create_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_create_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -95,8 +91,7 @@ def test_send_notif_create_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -110,12 +105,11 @@ def test_send_notif_create_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_update_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_update_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -147,8 +141,7 @@ def test_send_notif_update_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -162,12 +155,11 @@ def test_send_notif_update_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_partial_update_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_partial_update_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -199,8 +191,7 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -214,12 +205,11 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_delete_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_delete_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -238,8 +228,7 @@ def test_send_notif_delete_object(self, mocker, mock_client): response.status_code, status.HTTP_204_NO_CONTENT, response.data ) - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": full_url, diff --git a/src/objects/tests/v2/test_notifications_kanaal.py b/src/objects/tests/v2/test_notifications_kanaal.py index 87764024..4b5937f4 100644 --- a/src/objects/tests/v2/test_notifications_kanaal.py +++ b/src/objects/tests/v2/test_notifications_kanaal.py @@ -5,8 +5,11 @@ from django.core.management import call_command from django.test import override_settings +from notifications_api_common.kanalen import KANAAL_REGISTRY, Kanaal +from notifications_api_common.models import NotificationsConfig from rest_framework.test import APITestCase -from vng_api_common.notifications.kanalen import Kanaal +from zgw_consumers.constants import APITypes +from zgw_consumers.models import Service from objects.core.models import Object @@ -20,23 +23,35 @@ def setUpTestData(cls): site.domain = "example.com" site.save() - @patch( - "vng_api_common.notifications.management.commands.register_kanaal.get_client" - ) + kanaal = Kanaal(label="kanaal_test", main_resource=Object) + cls.addClassCleanup(lambda: KANAAL_REGISTRY.remove(kanaal)) + + service, _ = Service.objects.update_or_create( + api_root="https://notificaties-api.vng.cloud/api/v1/", + defaults=dict( + api_type=APITypes.nrc, + client_id="test", + secret="test", + user_id="test", + user_representation="Test", + ), + ) + config = NotificationsConfig.get_solo() + config.notifications_api_service = service + config.save() + + @patch("notifications_api_common.models.NotificationsConfig.get_client") def test_kanaal_create_with_name(self, mock_get_client): """ Test is request to create kanaal is send with specified kanaal name """ client = mock_get_client.return_value client.list.return_value = [] - # ensure this is added to the registry - Kanaal(label="kanaal_test", main_resource=Object) stdout = StringIO() call_command( - "register_kanaal", - "kanaal_test", - notificaties_api_root="https://example.com/api/v1", + "register_kanalen", + kanalen=["kanaal_test"], stdout=stdout, ) @@ -49,9 +64,7 @@ def test_kanaal_create_with_name(self, mock_get_client): }, ) - @patch( - "vng_api_common.notifications.management.commands.register_kanaal.get_client" - ) + @patch("notifications_api_common.models.NotificationsConfig.get_client") @override_settings(NOTIFICATIONS_KANAAL="dummy-kanaal") def test_kanaal_create_without_name(self, mock_get_client): """ @@ -59,21 +72,18 @@ def test_kanaal_create_without_name(self, mock_get_client): """ client = mock_get_client.return_value client.list.return_value = [] - # ensure this is added to the registry - Kanaal(label="dummy-kanaal", main_resource=Object) stdout = StringIO() call_command( - "register_kanaal", - notificaties_api_root="https://example.com/api/v1", + "register_kanalen", stdout=stdout, ) client.create.assert_called_once_with( "kanaal", { - "naam": "dummy-kanaal", - "documentatieLink": "https://example.com/ref/kanalen/#dummy-kanaal", + "naam": "kanaal_test", + "documentatieLink": "https://example.com/ref/kanalen/#kanaal_test", "filters": [], }, ) diff --git a/src/objects/tests/v2/test_notifications_send.py b/src/objects/tests/v2/test_notifications_send.py index 75d4884f..43621815 100644 --- a/src/objects/tests/v2/test_notifications_send.py +++ b/src/objects/tests/v2/test_notifications_send.py @@ -4,9 +4,9 @@ import requests_mock from freezegun import freeze_time +from notifications_api_common.models import NotificationsConfig from rest_framework import status from rest_framework.test import APITestCase -from vng_api_common.notifications.models import NotificationsConfig from zgw_consumers.constants import APITypes from zgw_consumers.models import Service @@ -20,12 +20,7 @@ from objects.utils.test import TokenAuthMixin from ..constants import GEO_WRITE_KWARGS -from ..utils import ( - mock_objecttype, - mock_objecttype_version, - mock_service_oas_get, - notifications_client_mock, -) +from ..utils import mock_objecttype, mock_objecttype_version, mock_service_oas_get from .utils import reverse OBJECT_TYPES_API = "https://example.com/objecttypes/v1/" @@ -49,9 +44,8 @@ def setUpTestData(cls): def setUp(self): super().setUp() - config = NotificationsConfig.get_solo() - Service.objects.update_or_create( - api_root=config.api_root, + service, _ = Service.objects.update_or_create( + api_root="https://notificaties-api.vng.cloud/api/v1/", defaults=dict( api_type=APITypes.nrc, client_id="test", @@ -60,13 +54,15 @@ def setUp(self): user_representation="Test", ), ) + config = NotificationsConfig.get_solo() + config.notifications_api_service = service + config.save() - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_create_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_create_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -95,8 +91,7 @@ def test_send_notif_create_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -110,12 +105,11 @@ def test_send_notif_create_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_update_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_update_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -147,8 +141,7 @@ def test_send_notif_update_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -162,12 +155,11 @@ def test_send_notif_update_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_partial_update_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_partial_update_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -199,8 +191,7 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): data = response.json() - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": data["url"], @@ -214,12 +205,11 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): }, ) - @patch("zds_client.Client.from_url", side_effect=notifications_client_mock) - def test_send_notif_delete_object(self, mocker, mock_client): + @patch("notifications_api_common.viewsets.send_notification.delay") + def test_send_notif_delete_object(self, mocker, mock_task): """ Check if notifications will be send when Object is created """ - client = mock_client.return_value mock_service_oas_get(mocker, OBJECT_TYPES_API, "objecttypes") mocker.get( f"{self.object_type.url}/versions/1", @@ -238,8 +228,7 @@ def test_send_notif_delete_object(self, mocker, mock_client): response.status_code, status.HTTP_204_NO_CONTENT, response.data ) - client.create.assert_called_once_with( - "notificaties", + mock_task.assert_called_once_with( { "kanaal": "objecten", "hoofdObject": full_url, diff --git a/src/objects/urls.py b/src/objects/urls.py index 4413dcb8..42833103 100644 --- a/src/objects/urls.py +++ b/src/objects/urls.py @@ -45,7 +45,7 @@ ), ), path("ref/", include("vng_api_common.urls")), - path("ref/", include("vng_api_common.notifications.urls")), + path("ref/", include("notifications_api_common.urls")), path("oidc/", include("mozilla_django_oidc.urls")), path("api/", include("objects.api.urls")), ] From db7d52303fbb7ece1121d56a5ca3c8f7695241d4 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:53:20 +0100 Subject: [PATCH 05/15] Upgrade to `maykin-2fa` Upgrade is pretty straightforward. Followed documentation --- docs/installation/config.rst | 6 --- requirements/base.in | 3 +- requirements/base.txt | 27 +++++++++---- requirements/ci.txt | 40 ++++++++++++++----- requirements/dev.txt | 40 ++++++++++++++----- src/objects/conf/base.py | 19 ++++++--- src/objects/conf/ci.py | 6 --- src/objects/conf/dev.py | 6 ++- src/objects/templates/admin/base_site.html | 6 +-- src/objects/templates/admin/login.html | 16 -------- src/objects/templates/maykin_2fa/base.html | 9 +++++ src/objects/templates/maykin_2fa/login.html | 23 +++++++++++ .../templates/two_factor/admin/login.html | 1 - .../tests/admin/test_token_permissions.py | 2 + src/objects/urls.py | 5 +++ 15 files changed, 138 insertions(+), 71 deletions(-) delete mode 100644 src/objects/templates/admin/login.html create mode 100644 src/objects/templates/maykin_2fa/base.html create mode 100644 src/objects/templates/maykin_2fa/login.html delete mode 100644 src/objects/templates/two_factor/admin/login.html diff --git a/docs/installation/config.rst b/docs/installation/config.rst index 211826a9..7bee5790 100644 --- a/docs/installation/config.rst +++ b/docs/installation/config.rst @@ -84,12 +84,6 @@ Other settings sent to the Notificaties API for operations on the Object endpoint. Defaults to ``True`` for the ``dev`` environment, otherwise defaults to ``False``. -* ``TWO_FACTOR_FORCE_OTP_ADMIN``: Enforce 2 Factor Authentication in the admin or not. - Default ``True``. You'll probably want to disable this when using OIDC. - -* ``TWO_FACTOR_PATCH_ADMIN``: Whether to use the 2 Factor Authentication login flow for - the admin or not. Default ``True``. You'll probably want to disable this when using OIDC. - * ``USE_X_FORWARDED_HOST``: whether to grab the domain/host from the ``X-Forwarded-Host`` header or not. This header is typically set by reverse proxies (such as nginx, traefik, Apache...). Default ``False`` - this is a header that can be spoofed and you diff --git a/requirements/base.in b/requirements/base.in index 14052f5d..f9f5d4d3 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -14,8 +14,7 @@ django-admin-index django-axes django-redis django-rosetta -maykin-django-two-factor-auth -maykin-django-two-factor-auth[phonenumbers] +maykin-2fa mozilla-django-oidc-db # API libraries diff --git a/requirements/base.txt b/requirements/base.txt index f7245d2e..58fa4f7f 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -8,6 +8,8 @@ amqp==5.2.0 # via kombu asgiref==3.7.2 # via django +asn1crypto==1.5.1 + # via webauthn attrs==20.3.0 # via # glom @@ -18,6 +20,8 @@ boltons==21.0.0 # via # face # glom +cbor2==5.6.1 + # via webauthn celery==5.2.2 # via # -r requirements/base.in @@ -60,6 +64,7 @@ cryptography==41.0.7 # josepy # mozilla-django-oidc # pyopenssl + # webauthn django==3.2.23 # via # -r requirements/base.in @@ -80,11 +85,12 @@ django==3.2.23 # django-sendfile2 # django-simple-certmanager # django-solo + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular # drf-yasg - # maykin-django-two-factor-auth + # maykin-2fa # mozilla-django-oidc # mozilla-django-oidc-db # notifications-api-common @@ -98,7 +104,7 @@ django-filter==23.5 # -r requirements/base.in # commonground-api-common django-formtools==2.3 - # via maykin-django-two-factor-auth + # via django-two-factor-auth django-jsonform==2.21.4 # via mozilla-django-oidc-db django-markup==1.3 @@ -106,9 +112,9 @@ django-markup==1.3 django-ordered-model==3.7.4 # via django-admin-index django-otp==1.0.6 - # via maykin-django-two-factor-auth + # via django-two-factor-auth django-phonenumber-field==5.2.0 - # via maykin-django-two-factor-auth + # via django-two-factor-auth django-privates==2.0.0.post0 # via django-simple-certmanager django-redis==5.4.0 @@ -129,6 +135,8 @@ django-solo==2.2.0 # mozilla-django-oidc-db # notifications-api-common # zgw-consumers +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via maykin-2fa djangorestframework==3.12.4 # via # -r requirements/base.in @@ -191,7 +199,7 @@ markdown==3.3.4 # via commonground-api-common markupsafe==2.1.3 # via jinja2 -maykin-django-two-factor-auth[phonenumbers]==2.0.3 +maykin-2fa==1.0.0 # via -r requirements/base.in mozilla-django-oidc==4.0.0 # via mozilla-django-oidc-db @@ -205,8 +213,8 @@ oyaml==1.0 # via commonground-api-common packaging==23.2 # via drf-yasg -phonenumbers==8.12.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 polib==1.1.1 @@ -225,6 +233,7 @@ pyopenssl==23.3.0 # via # django-simple-certmanager # josepy + # webauthn # zgw-consumers pyrsistent==0.17.3 # via jsonschema @@ -248,7 +257,7 @@ pyyaml==6.0.1 # gemma-zds-client # oyaml qrcode==6.1 - # via maykin-django-two-factor-auth + # via django-two-factor-auth redis==3.5.3 # via django-redis requests==2.25.1 @@ -298,6 +307,8 @@ vine==5.1.0 # kombu wcwidth==0.2.13 # via prompt-toolkit +webauthn==2.0.0 + # via django-two-factor-auth zgw-consumers==0.27.0 # via # -r requirements/base.in diff --git a/requirements/ci.txt b/requirements/ci.txt index f57fd03f..36b497cc 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -12,6 +12,10 @@ asgiref==3.7.2 # via # -r requirements/base.txt # django +asn1crypto==1.5.1 + # via + # -r requirements/base.txt + # webauthn attrs==20.3.0 # via # -r requirements/base.txt @@ -28,6 +32,10 @@ boltons==21.0.0 # -r requirements/base.txt # face # glom +cbor2==5.6.1 + # via + # -r requirements/base.txt + # webauthn celery==5.2.2 # via # -r requirements/base.txt @@ -89,6 +97,7 @@ cryptography==41.0.7 # josepy # mozilla-django-oidc # pyopenssl + # webauthn cssselect==1.1.0 # via pyquery django==3.2.23 @@ -111,11 +120,12 @@ django==3.2.23 # django-sendfile2 # django-simple-certmanager # django-solo + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular # drf-yasg - # maykin-django-two-factor-auth + # maykin-2fa # mozilla-django-oidc # mozilla-django-oidc-db # notifications-api-common @@ -131,7 +141,7 @@ django-filter==23.5 django-formtools==2.3 # via # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-jsonform==2.21.4 # via # -r requirements/base.txt @@ -147,11 +157,11 @@ django-ordered-model==3.7.4 django-otp==1.0.6 # via # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-phonenumber-field==5.2.0 # via # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-privates==2.0.0.post0 # via # -r requirements/base.txt @@ -183,6 +193,11 @@ django-solo==2.2.0 # mozilla-django-oidc-db # notifications-api-common # zgw-consumers +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via + # -r requirements/base.txt + # django-two-factor-auth + # maykin-2fa django-webtest==1.9.7 # via -r requirements/test-tools.in djangorestframework==3.12.4 @@ -283,10 +298,8 @@ markupsafe==2.1.3 # via # -r requirements/base.txt # jinja2 -maykin-django-two-factor-auth[phonenumbers]==2.0.3 - # via - # -r requirements/base.txt - # maykin-django-two-factor-auth +maykin-2fa==1.0.0 + # via -r requirements/base.txt mozilla-django-oidc==4.0.0 # via # -r requirements/base.txt @@ -305,10 +318,10 @@ packaging==23.2 # via # -r requirements/base.txt # drf-yasg -phonenumbers==8.12.29 +phonenumberslite==8.13.30 # via # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth pillow==10.2.0 # via -r requirements/base.txt polib==1.1.1 @@ -335,6 +348,7 @@ pyopenssl==23.3.0 # -r requirements/base.txt # django-simple-certmanager # josepy + # webauthn # zgw-consumers pyquery==1.4.3 # via -r requirements/test-tools.in @@ -367,7 +381,7 @@ pyyaml==6.0.1 qrcode==6.1 # via # -r requirements/base.txt - # maykin-django-two-factor-auth + # django-two-factor-auth redis==3.5.3 # via # -r requirements/base.txt @@ -444,6 +458,10 @@ wcwidth==0.2.13 # via # -r requirements/base.txt # prompt-toolkit +webauthn==2.0.0 + # via + # -r requirements/base.txt + # django-two-factor-auth webob==1.8.7 # via webtest webtest==2.0.35 diff --git a/requirements/dev.txt b/requirements/dev.txt index e919ecca..f4315223 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -14,6 +14,10 @@ asgiref==3.7.2 # via # -r requirements/ci.txt # django +asn1crypto==1.5.1 + # via + # -r requirements/ci.txt + # webauthn attrs==20.3.0 # via # -r requirements/ci.txt @@ -42,6 +46,10 @@ bump2version==1.0.1 # via bumpversion bumpversion==0.6.0 # via -r requirements/dev.in +cbor2==5.6.1 + # via + # -r requirements/ci.txt + # webauthn celery==5.2.2 # via # -r requirements/ci.txt @@ -107,6 +115,7 @@ cryptography==41.0.7 # josepy # mozilla-django-oidc # pyopenssl + # webauthn cssselect==1.1.0 # via # -r requirements/ci.txt @@ -133,11 +142,12 @@ django==3.2.23 # django-sendfile2 # django-simple-certmanager # django-solo + # django-two-factor-auth # djangorestframework # drf-nested-routers # drf-spectacular # drf-yasg - # maykin-django-two-factor-auth + # maykin-2fa # mozilla-django-oidc # mozilla-django-oidc-db # notifications-api-common @@ -157,7 +167,7 @@ django-filter==23.5 django-formtools==2.3 # via # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-jsonform==2.21.4 # via # -r requirements/ci.txt @@ -171,11 +181,11 @@ django-ordered-model==3.7.4 django-otp==1.0.6 # via # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-phonenumber-field==5.2.0 # via # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth django-privates==2.0.0.post0 # via # -r requirements/ci.txt @@ -207,6 +217,11 @@ django-solo==2.2.0 # mozilla-django-oidc-db # notifications-api-common # zgw-consumers +django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 + # via + # -r requirements/ci.txt + # django-two-factor-auth + # maykin-2fa django-webtest==1.9.7 # via -r requirements/ci.txt djangorestframework==3.12.4 @@ -320,10 +335,8 @@ markupsafe==2.1.3 # via # -r requirements/ci.txt # jinja2 -maykin-django-two-factor-auth[phonenumbers]==2.0.3 - # via - # -r requirements/ci.txt - # maykin-django-two-factor-auth +maykin-2fa==1.0.0 + # via -r requirements/ci.txt mccabe==0.7.0 # via flake8 mozilla-django-oidc==4.0.0 @@ -351,10 +364,10 @@ packaging==23.2 # sphinx pathspec==0.11.2 # via black -phonenumbers==8.12.29 +phonenumberslite==8.13.30 # via # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth pillow==10.2.0 # via -r requirements/ci.txt pip-tools==7.3.0 @@ -393,6 +406,7 @@ pyopenssl==23.3.0 # -r requirements/ci.txt # django-simple-certmanager # josepy + # webauthn # zgw-consumers pyproject-hooks==1.0.0 # via build @@ -428,7 +442,7 @@ pyyaml==6.0.1 qrcode==6.1 # via # -r requirements/ci.txt - # maykin-django-two-factor-auth + # django-two-factor-auth recommonmark==0.7.1 # via -r requirements/dev.in redis==3.5.3 @@ -546,6 +560,10 @@ wcwidth==0.2.13 # via # -r requirements/ci.txt # prompt-toolkit +webauthn==2.0.0 + # via + # -r requirements/ci.txt + # django-two-factor-auth webob==1.8.7 # via # -r requirements/ci.txt diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index 1972340e..2f0662a7 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -82,11 +82,12 @@ "notifications_api_common", "simple_certmanager", "zgw_consumers", - # 2fa apps + # Two-factor authentication in the Django admin, enforced. "django_otp", "django_otp.plugins.otp_static", "django_otp.plugins.otp_totp", "two_factor", + "maykin_2fa", # Project applications. "objects.accounts", "objects.api", @@ -102,11 +103,11 @@ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "maykin_2fa.middleware.OTPMiddleware", "mozilla_django_oidc_db.middleware.SessionRefresh", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "axes.middleware.AxesMiddleware", - "django_otp.middleware.OTPMiddleware", ] ROOT_URLCONF = "objects.urls" @@ -422,10 +423,18 @@ NOTIFICATIONS_DISABLED = config("NOTIFICATIONS_DISABLED", False) # -# Maykin fork of DJANGO-TWO-FACTOR-AUTH +# MAYKIN-2FA +# Uses django-two-factor-auth under the hood, so relevant upstream package settings +# apply too. # -TWO_FACTOR_FORCE_OTP_ADMIN = config("TWO_FACTOR_FORCE_OTP_ADMIN", not DEBUG) -TWO_FACTOR_PATCH_ADMIN = config("TWO_FACTOR_PATCH_ADMIN", True) + +# we run the admin site monkeypatch instead. +TWO_FACTOR_PATCH_ADMIN = False +# 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 = [ + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend", +] # # Mozilla Django OIDC DB settings diff --git a/src/objects/conf/ci.py b/src/objects/conf/ci.py index 23946766..def866cd 100644 --- a/src/objects/conf/ci.py +++ b/src/objects/conf/ci.py @@ -29,9 +29,3 @@ AXES_BEHIND_REVERSE_PROXY = False NOTIFICATIONS_DISABLED = True - - -# -# Maykin fork of django-two-factor-auth -# -TWO_FACTOR_FORCE_OTP_ADMIN = False diff --git a/src/objects/conf/dev.py b/src/objects/conf/dev.py index f06e07ec..07843250 100644 --- a/src/objects/conf/dev.py +++ b/src/objects/conf/dev.py @@ -102,8 +102,10 @@ if "test" in sys.argv: NOTIFICATIONS_DISABLED = True - TWO_FACTOR_PATCH_ADMIN = False - TWO_FACTOR_FORCE_OTP_ADMIN = False + +# None of the authentication backends require two-factor authentication. +if config("DISABLE_2FA", default=False): # pragma: no cover + MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS # Override settings with local settings. try: diff --git a/src/objects/templates/admin/base_site.html b/src/objects/templates/admin/base_site.html index 6e1a9e1f..1352afce 100644 --- a/src/objects/templates/admin/base_site.html +++ b/src/objects/templates/admin/base_site.html @@ -23,9 +23,9 @@

{{ settings.PROJECT_NAME }} {% if site_url %} {{ settings.SITE_TITLE }} / {% endif %} - {% url 'admin:two_factor:profile' as 2fa_profile_url %} - {% if 2fa_profile_url %} - {% trans "View 2fa profile" %} / + {% url 'maykin_2fa:account_security' as 2fa_account_security_url %} + {% if 2fa_account_security_url %} + {% trans "Account security" %} / {% endif %} {% if user.has_usable_password %} {% trans 'Change password' %} / diff --git a/src/objects/templates/admin/login.html b/src/objects/templates/admin/login.html deleted file mode 100644 index d408af04..00000000 --- a/src/objects/templates/admin/login.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "two_factor/admin/login.html" %} -{% load solo_tags i18n %} - - -{% block content %} -{{ block.super }} - -{% get_solo 'mozilla_django_oidc_db.OpenIDConnectConfig' as oidc_config %} -{% if oidc_config.enabled %} -
{% trans "or" %}
-
- {% trans "Login with organization account" %} -
-{% endif %} - -{% endblock %} diff --git a/src/objects/templates/maykin_2fa/base.html b/src/objects/templates/maykin_2fa/base.html new file mode 100644 index 00000000..68fa4301 --- /dev/null +++ b/src/objects/templates/maykin_2fa/base.html @@ -0,0 +1,9 @@ +{% extends "maykin_2fa/base.html" %} + +{# Django 3.2 #} +{% block breadcrumbs %}{% endblock %} + +{# Do not show any version information #} +{% block footer %} + +{% endblock %} diff --git a/src/objects/templates/maykin_2fa/login.html b/src/objects/templates/maykin_2fa/login.html new file mode 100644 index 00000000..51987a80 --- /dev/null +++ b/src/objects/templates/maykin_2fa/login.html @@ -0,0 +1,23 @@ +{% extends "maykin_2fa/login.html" %} +{% load solo_tags i18n %} + +{% block extra_login_options %} + {% get_solo 'mozilla_django_oidc_db.OpenIDConnectConfig' as oidc_config %} + {% if oidc_config.enabled %} +
{% trans "or" %}
+
+ {% trans "Login with organization account" %} +
+ {% endif %} +{% endblock %} + +{% block extra_recovery_options %} +
  • + {% trans 'Contact support to start the account recovery process' %} +
  • +{% endblock extra_recovery_options %} + +{# Do not show any version information #} +{% block footer %} + +{% endblock %} diff --git a/src/objects/templates/two_factor/admin/login.html b/src/objects/templates/two_factor/admin/login.html deleted file mode 100644 index afdff9b3..00000000 --- a/src/objects/templates/two_factor/admin/login.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "admin/login.html" %} diff --git a/src/objects/tests/admin/test_token_permissions.py b/src/objects/tests/admin/test_token_permissions.py index a9195275..40eb9d59 100644 --- a/src/objects/tests/admin/test_token_permissions.py +++ b/src/objects/tests/admin/test_token_permissions.py @@ -1,6 +1,7 @@ from django.urls import reverse_lazy from django_webtest import WebTest +from maykin_2fa.test import disable_admin_mfa from requests_mock import Mocker from objects.accounts.tests.factories import UserFactory @@ -11,6 +12,7 @@ OBJECT_TYPES_API = "https://example.com/objecttypes/v1/" +@disable_admin_mfa() class AddPermissionTests(WebTest): url = reverse_lazy("admin:token_permission_add") diff --git a/src/objects/urls.py b/src/objects/urls.py index 42833103..93bd2f34 100644 --- a/src/objects/urls.py +++ b/src/objects/urls.py @@ -7,6 +7,8 @@ from django.urls import include, path from django.views.generic.base import TemplateView +from maykin_2fa import monkeypatch_admin +from maykin_2fa.urls import urlpatterns as maykin_2fa_urlpatterns from rest_framework.settings import api_settings handler500 = "objects.utils.views.server_error" @@ -14,6 +16,8 @@ admin.site.site_title = "objects admin" admin.site.index_title = "Welcome to the objects admin" +monkeypatch_admin() + urlpatterns = [ path( "admin/password_reset/", @@ -25,6 +29,7 @@ auth_views.PasswordResetDoneView.as_view(), name="password_reset_done", ), + path("admin/", include((maykin_2fa_urlpatterns, "maykin_2fa"))), path("admin/", admin.site.urls), path( "reset///", From 93763ce65c93d717cfbc9c17e43455697a86c00d Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:05:36 +0100 Subject: [PATCH 06/15] Remove `generate_random_password` method, will be deprecated --- .../accounts/management/commands/createinitialsuperuser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/objects/accounts/management/commands/createinitialsuperuser.py b/src/objects/accounts/management/commands/createinitialsuperuser.py index 436dadc0..42680e00 100644 --- a/src/objects/accounts/management/commands/createinitialsuperuser.py +++ b/src/objects/accounts/management/commands/createinitialsuperuser.py @@ -1,4 +1,6 @@ import os +import secrets +import string from django.conf import settings from django.contrib.auth.management.commands.createsuperuser import ( @@ -50,7 +52,8 @@ def handle(self, **options): user = qs.get() if not password and options["generate_password"]: - password = self.UserModel.objects.make_random_password(length=20) + alphabet = string.ascii_letters + string.digits + password = "".join(secrets.choice(alphabet) for _ in range(20)) if password: self.stdout.write("Setting user password...") From 334632cdd6fa85247b5197f389c14b7f4625bc10 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:58:14 +0100 Subject: [PATCH 07/15] Replace deprecated `smart_text` --- src/objects/api/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/objects/api/fields.py b/src/objects/api/fields.py index 3cd1b28f..1e4a2da1 100644 --- a/src/objects/api/fields.py +++ b/src/objects/api/fields.py @@ -1,5 +1,5 @@ from django.core.exceptions import ObjectDoesNotExist -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from django.utils.translation import gettext_lazy as _ from rest_framework import serializers @@ -42,7 +42,7 @@ def to_internal_value(self, data): try: return self.get_queryset().get_by_url(data) except ObjectDoesNotExist: - self.fail("does_not_exist", value=smart_text(data)) + self.fail("does_not_exist", value=smart_str(data)) except (TypeError, ValueError): self.fail("invalid") From 5567694878bdd5ac527f58cd36bde5fba05ebeae Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:20:34 +0100 Subject: [PATCH 08/15] Update dependencies required for next `commonground-api-common` update --- requirements/base.txt | 16 ++++++---------- requirements/ci.txt | 12 ++++-------- requirements/dev.txt | 12 ++++-------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 58fa4f7f..e614a4bb 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -51,13 +51,9 @@ click-repl==0.3.0 commonground-api-common[markdown-docs,markdown_docs]==1.12.1 # via -r requirements/base.in coreapi==2.3.3 - # via - # commonground-api-common - # drf-yasg + # via commonground-api-common coreschema==0.0.4 - # via - # coreapi - # drf-yasg + # via coreapi cryptography==41.0.7 # via # django-simple-certmanager @@ -146,7 +142,7 @@ djangorestframework==3.12.4 # drf-spectacular # drf-yasg # notifications-api-common -djangorestframework-camel-case==1.2.0 +djangorestframework-camel-case==1.4.2 # via # commonground-api-common # notifications-api-common @@ -156,7 +152,7 @@ drf-nested-routers==0.93.3 # via commonground-api-common drf-spectacular==0.16.0 # via -r requirements/base.in -drf-yasg==1.20.0 +drf-yasg==1.21.7 # via commonground-api-common elastic-apm==6.1.1 # via -r requirements/base.in @@ -250,10 +246,12 @@ pytz==2021.1 # -r requirements/base.in # celery # django + # drf-yasg pyyaml==6.0.1 # via # commonground-api-common # drf-spectacular + # drf-yasg # gemma-zds-client # oyaml qrcode==6.1 @@ -271,8 +269,6 @@ requests==2.25.1 # zgw-consumers requests-mock==1.8.0 # via zgw-consumers -ruamel-yaml==0.17.4 - # via drf-yasg sentry-sdk==1.0.0 # via -r requirements/base.in six==1.15.0 diff --git a/requirements/ci.txt b/requirements/ci.txt index 36b497cc..1be4cbe2 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -82,12 +82,10 @@ coreapi==2.3.3 # via # -r requirements/base.txt # commonground-api-common - # drf-yasg coreschema==0.0.4 # via # -r requirements/base.txt # coreapi - # drf-yasg coverage==4.5.4 # via -r requirements/test-tools.in cryptography==41.0.7 @@ -209,7 +207,7 @@ djangorestframework==3.12.4 # drf-spectacular # drf-yasg # notifications-api-common -djangorestframework-camel-case==1.2.0 +djangorestframework-camel-case==1.4.2 # via # -r requirements/base.txt # commonground-api-common @@ -222,7 +220,7 @@ drf-nested-routers==0.93.3 # commonground-api-common drf-spectacular==0.16.0 # via -r requirements/base.txt -drf-yasg==1.20.0 +drf-yasg==1.21.7 # via # -r requirements/base.txt # commonground-api-common @@ -371,11 +369,13 @@ pytz==2021.1 # -r requirements/base.txt # celery # django + # drf-yasg pyyaml==6.0.1 # via # -r requirements/base.txt # commonground-api-common # drf-spectacular + # drf-yasg # gemma-zds-client # oyaml qrcode==6.1 @@ -401,10 +401,6 @@ requests-mock==1.8.0 # -r requirements/base.txt # -r requirements/test-tools.in # zgw-consumers -ruamel-yaml==0.17.4 - # via - # -r requirements/base.txt - # drf-yasg sentry-sdk==1.0.0 # via -r requirements/base.txt six==1.15.0 diff --git a/requirements/dev.txt b/requirements/dev.txt index f4315223..46380de3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -100,12 +100,10 @@ coreapi==2.3.3 # via # -r requirements/ci.txt # commonground-api-common - # drf-yasg coreschema==0.0.4 # via # -r requirements/ci.txt # coreapi - # drf-yasg coverage==4.5.4 # via -r requirements/ci.txt cryptography==41.0.7 @@ -233,7 +231,7 @@ djangorestframework==3.12.4 # drf-spectacular # drf-yasg # notifications-api-common -djangorestframework-camel-case==1.2.0 +djangorestframework-camel-case==1.4.2 # via # -r requirements/ci.txt # commonground-api-common @@ -252,7 +250,7 @@ drf-nested-routers==0.93.3 # commonground-api-common drf-spectacular==0.16.0 # via -r requirements/ci.txt -drf-yasg==1.20.0 +drf-yasg==1.21.7 # via # -r requirements/ci.txt # commonground-api-common @@ -432,11 +430,13 @@ pytz==2021.1 # babel # celery # django + # drf-yasg pyyaml==6.0.1 # via # -r requirements/ci.txt # commonground-api-common # drf-spectacular + # drf-yasg # gemma-zds-client # oyaml qrcode==6.1 @@ -464,10 +464,6 @@ requests-mock==1.8.0 # via # -r requirements/ci.txt # zgw-consumers -ruamel-yaml==0.17.4 - # via - # -r requirements/ci.txt - # drf-yasg sentry-sdk==1.0.0 # via -r requirements/ci.txt six==1.15.0 From 0a7d996ffd85995e5f93fad3c82b984ea037b14a Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:41:47 +0100 Subject: [PATCH 09/15] Fix more deprecation warnings By updating a couple libraries and removing a deprecated setting --- requirements/base.txt | 4 ++-- requirements/ci.txt | 4 ++-- requirements/dev.txt | 4 ++-- src/objects/conf/base.py | 2 -- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index e614a4bb..6bc01293 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -191,7 +191,7 @@ jsonschema==4.17.3 # drf-spectacular kombu==5.3.5 # via celery -markdown==3.3.4 +markdown==3.5.2 # via commonground-api-common markupsafe==2.1.3 # via jinja2 @@ -271,7 +271,7 @@ requests-mock==1.8.0 # via zgw-consumers sentry-sdk==1.0.0 # via -r requirements/base.in -six==1.15.0 +six==1.16.0 # via # django-markup # isodate diff --git a/requirements/ci.txt b/requirements/ci.txt index 1be4cbe2..d3794feb 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -288,7 +288,7 @@ kombu==5.3.5 # celery lxml==4.7.1 # via pyquery -markdown==3.3.4 +markdown==3.5.2 # via # -r requirements/base.txt # commonground-api-common @@ -403,7 +403,7 @@ requests-mock==1.8.0 # zgw-consumers sentry-sdk==1.0.0 # via -r requirements/base.txt -six==1.15.0 +six==1.16.0 # via # -r requirements/base.txt # django-markup diff --git a/requirements/dev.txt b/requirements/dev.txt index 46380de3..92dbde74 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -327,7 +327,7 @@ lxml==4.7.1 # via # -r requirements/ci.txt # pyquery -markdown==3.3.4 +markdown==3.5.2 # via -r requirements/ci.txt markupsafe==2.1.3 # via @@ -466,7 +466,7 @@ requests-mock==1.8.0 # zgw-consumers sentry-sdk==1.0.0 # via -r requirements/ci.txt -six==1.15.0 +six==1.16.0 # via # -r requirements/ci.txt # django-markup diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index 2f0662a7..f1775a8b 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -172,8 +172,6 @@ USE_I18N = True -USE_L10N = True - USE_TZ = True USE_THOUSAND_SEPARATOR = True From 1416068f05cca37bc7a5b27933496a5a8adf5368 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:00:34 +0100 Subject: [PATCH 10/15] Disable 2FA by default in dev --- src/objects/conf/dev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/conf/dev.py b/src/objects/conf/dev.py index 07843250..0c77b1b7 100644 --- a/src/objects/conf/dev.py +++ b/src/objects/conf/dev.py @@ -104,7 +104,7 @@ NOTIFICATIONS_DISABLED = True # None of the authentication backends require two-factor authentication. -if config("DISABLE_2FA", default=False): # pragma: no cover +if config("DISABLE_2FA", default=True): # pragma: no cover MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS # Override settings with local settings. From 1177dfea4943418b4cad9d8600f27d78db30207d Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:50:07 +0100 Subject: [PATCH 11/15] Update to Django 4.2 --- requirements/base.in | 2 +- requirements/base.txt | 9 ++++----- requirements/ci.txt | 9 ++++----- requirements/dev.txt | 9 ++++----- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index f9f5d4d3..90f6e98e 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -9,7 +9,7 @@ python-decouple # processing of envvar configs jsonschema # Framework libraries -django~=3.2 +django~=4.2 django-admin-index django-axes django-redis diff --git a/requirements/base.txt b/requirements/base.txt index 6bc01293..8a34f312 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -48,7 +48,7 @@ click-plugins==1.1.1 # via celery click-repl==0.3.0 # via celery -commonground-api-common[markdown-docs,markdown_docs]==1.12.1 +commonground-api-common[markdown-docs,markdown_docs]==1.13.0 # via -r requirements/base.in coreapi==2.3.3 # via commonground-api-common @@ -61,7 +61,7 @@ cryptography==41.0.7 # mozilla-django-oidc # pyopenssl # webauthn -django==3.2.23 +django==4.2.11 # via # -r requirements/base.in # commonground-api-common @@ -133,7 +133,7 @@ django-solo==2.2.0 # zgw-consumers django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 # via maykin-2fa -djangorestframework==3.12.4 +djangorestframework==3.14.0 # via # -r requirements/base.in # commonground-api-common @@ -245,11 +245,10 @@ pytz==2021.1 # via # -r requirements/base.in # celery - # django + # djangorestframework # drf-yasg pyyaml==6.0.1 # via - # commonground-api-common # drf-spectacular # drf-yasg # gemma-zds-client diff --git a/requirements/ci.txt b/requirements/ci.txt index d3794feb..6b6fdf9e 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -74,7 +74,7 @@ click-repl==0.3.0 # via # -r requirements/base.txt # celery -commonground-api-common[markdown-docs]==1.12.1 +commonground-api-common[markdown-docs]==1.13.0 # via # -r requirements/base.txt # commonground-api-common @@ -98,7 +98,7 @@ cryptography==41.0.7 # webauthn cssselect==1.1.0 # via pyquery -django==3.2.23 +django==4.2.11 # via # -r requirements/base.txt # commonground-api-common @@ -198,7 +198,7 @@ django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 # maykin-2fa django-webtest==1.9.7 # via -r requirements/test-tools.in -djangorestframework==3.12.4 +djangorestframework==3.14.0 # via # -r requirements/base.txt # commonground-api-common @@ -368,12 +368,11 @@ pytz==2021.1 # via # -r requirements/base.txt # celery - # django + # djangorestframework # drf-yasg pyyaml==6.0.1 # via # -r requirements/base.txt - # commonground-api-common # drf-spectacular # drf-yasg # gemma-zds-client diff --git a/requirements/dev.txt b/requirements/dev.txt index 92dbde74..a90bf325 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -90,7 +90,7 @@ click-repl==0.3.0 # via # -r requirements/ci.txt # celery -commonground-api-common[markdown-docs]==1.12.1 +commonground-api-common[markdown-docs]==1.13.0 # via # -r requirements/ci.txt # commonground-api-common @@ -118,7 +118,7 @@ cssselect==1.1.0 # via # -r requirements/ci.txt # pyquery -django==3.2.23 +django==4.2.11 # via # -r requirements/ci.txt # commonground-api-common @@ -222,7 +222,7 @@ django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 # maykin-2fa django-webtest==1.9.7 # via -r requirements/ci.txt -djangorestframework==3.12.4 +djangorestframework==3.14.0 # via # -r requirements/ci.txt # commonground-api-common @@ -429,12 +429,11 @@ pytz==2021.1 # -r requirements/ci.txt # babel # celery - # django + # djangorestframework # drf-yasg pyyaml==6.0.1 # via # -r requirements/ci.txt - # commonground-api-common # drf-spectacular # drf-yasg # gemma-zds-client From e1c217f05e0d733b760de65f14c908f020c9e648 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:29:12 +0100 Subject: [PATCH 12/15] Update to `drf-spectacular==0.26.5` Latest version without too much breaking changes Fix `AutoSchema` subclass Regenerate schemas Add regression test --- requirements/base.txt | 2 +- requirements/ci.txt | 2 +- requirements/dev.txt | 2 +- src/objects/api/v1/filters.py | 6 +++-- src/objects/api/v1/openapi.yaml | 28 +++++++++++++++------- src/objects/api/v2/openapi.yaml | 37 +++++++++++++++++++++-------- src/objects/tests/v1/test_schema.py | 10 ++++++++ src/objects/tests/v2/test_schema.py | 10 ++++++++ src/objects/utils/autoschema.py | 12 ++++++---- 9 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 src/objects/tests/v1/test_schema.py create mode 100644 src/objects/tests/v2/test_schema.py diff --git a/requirements/base.txt b/requirements/base.txt index 8a34f312..34996741 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -150,7 +150,7 @@ djangorestframework-gis==1.0 # via -r requirements/base.in drf-nested-routers==0.93.3 # via commonground-api-common -drf-spectacular==0.16.0 +drf-spectacular==0.26.5 # via -r requirements/base.in drf-yasg==1.21.7 # via commonground-api-common diff --git a/requirements/ci.txt b/requirements/ci.txt index 6b6fdf9e..4bdc9878 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -218,7 +218,7 @@ drf-nested-routers==0.93.3 # via # -r requirements/base.txt # commonground-api-common -drf-spectacular==0.16.0 +drf-spectacular==0.26.5 # via -r requirements/base.txt drf-yasg==1.21.7 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index a90bf325..4fcc442b 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -248,7 +248,7 @@ drf-nested-routers==0.93.3 # via # -r requirements/ci.txt # commonground-api-common -drf-spectacular==0.16.0 +drf-spectacular==0.26.5 # via -r requirements/ci.txt drf-yasg==1.21.7 # via diff --git a/src/objects/api/v1/filters.py b/src/objects/api/v1/filters.py index 49b9e70d..bb36d9f6 100644 --- a/src/objects/api/v1/filters.py +++ b/src/objects/api/v1/filters.py @@ -1,3 +1,5 @@ +from datetime import date as date_ + from django import forms from django.utils.translation import gettext_lazy as _ @@ -108,8 +110,8 @@ def filter_data_attrs(self, queryset, name, value: str): return queryset - def filter_date(self, queryset, name, value: date): + def filter_date(self, queryset, name, value: date_): return queryset.filter_for_date(value) - def filter_registration_date(self, queryset, name, value: date): + def filter_registration_date(self, queryset, name, value: date_): return queryset.filter_for_registration_date(value) diff --git a/src/objects/api/v1/openapi.yaml b/src/objects/api/v1/openapi.yaml index 832f404a..2fdecd42 100644 --- a/src/objects/api/v1/openapi.yaml +++ b/src/objects/api/v1/openapi.yaml @@ -639,8 +639,8 @@ components: typeVersion: type: integer maximum: 32767 - description: Version of the OBJECTTYPE for data in the object record minimum: 0 + description: Version of the OBJECTTYPE for data in the object record data: type: object additionalProperties: {} @@ -661,6 +661,7 @@ components: type: string format: date readOnly: true + nullable: true description: Legal end date of the object record registrationAt: type: string @@ -668,13 +669,17 @@ components: readOnly: true description: The date when the record was registered in the system correctionFor: - type: string - readOnly: true + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record corrected by the current record - correctedBy: - type: string readOnly: true + correctedBy: + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record, which corrects the current record + readOnly: true required: - startAt - typeVersion @@ -786,8 +791,8 @@ components: typeVersion: type: integer maximum: 32767 - description: Version of the OBJECTTYPE for data in the object record minimum: 0 + description: Version of the OBJECTTYPE for data in the object record data: type: object additionalProperties: {} @@ -808,6 +813,7 @@ components: type: string format: date readOnly: true + nullable: true description: Legal end date of the object record registrationAt: type: string @@ -815,12 +821,16 @@ components: readOnly: true description: The date when the record was registered in the system correctionFor: - type: string + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record corrected by the current record correctedBy: - type: string - readOnly: true + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record, which corrects the current record + readOnly: true required: - startAt - typeVersion diff --git a/src/objects/api/v2/openapi.yaml b/src/objects/api/v2/openapi.yaml index d47d0b7f..4e3a2f7f 100644 --- a/src/objects/api/v2/openapi.yaml +++ b/src/objects/api/v2/openapi.yaml @@ -727,8 +727,8 @@ components: typeVersion: type: integer maximum: 32767 - description: Version of the OBJECTTYPE for data in the object record minimum: 0 + description: Version of the OBJECTTYPE for data in the object record data: type: object additionalProperties: {} @@ -749,6 +749,7 @@ components: type: string format: date readOnly: true + nullable: true description: Legal end date of the object record registrationAt: type: string @@ -756,13 +757,17 @@ components: readOnly: true description: The date when the record was registered in the system correctionFor: - type: string - readOnly: true + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record corrected by the current record - correctedBy: - type: string readOnly: true + correctedBy: + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record, which corrects the current record + readOnly: true required: - startAt - typeVersion @@ -787,6 +792,9 @@ components: - read_only - read_and_write type: string + description: |- + * `read_only` - Read-only + * `read_and_write` - Read and write MultiLineString: type: object description: GeoJSON multi-line-string geometry @@ -879,8 +887,8 @@ components: typeVersion: type: integer maximum: 32767 - description: Version of the OBJECTTYPE for data in the object record minimum: 0 + description: Version of the OBJECTTYPE for data in the object record data: type: object additionalProperties: {} @@ -901,6 +909,7 @@ components: type: string format: date readOnly: true + nullable: true description: Legal end date of the object record registrationAt: type: string @@ -908,12 +917,16 @@ components: readOnly: true description: The date when the record was registered in the system correctionFor: - type: string + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record corrected by the current record correctedBy: - type: string - readOnly: true + type: integer + maximum: 2147483647 + minimum: 0 description: Index of the record, which corrects the current record + readOnly: true required: - startAt - typeVersion @@ -1022,7 +1035,11 @@ components: mode: allOf: - $ref: '#/components/schemas/ModeEnum' - description: Permission mode + description: |- + Permission mode + + * `read_only` - Read-only + * `read_and_write` - Read and write use_fields: type: boolean description: Use field-based authorization diff --git a/src/objects/tests/v1/test_schema.py b/src/objects/tests/v1/test_schema.py new file mode 100644 index 00000000..e0b07335 --- /dev/null +++ b/src/objects/tests/v1/test_schema.py @@ -0,0 +1,10 @@ +from rest_framework import status +from rest_framework.test import APITestCase + +from .utils import reverse + + +class APISchemaTest(APITestCase): + def test_schema_endoint(self): + response = self.client.get(reverse("schema-redoc")) + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/src/objects/tests/v2/test_schema.py b/src/objects/tests/v2/test_schema.py new file mode 100644 index 00000000..e0b07335 --- /dev/null +++ b/src/objects/tests/v2/test_schema.py @@ -0,0 +1,10 @@ +from rest_framework import status +from rest_framework.test import APITestCase + +from .utils import reverse + + +class APISchemaTest(APITestCase): + def test_schema_endoint(self): + response = self.client.get(reverse("schema-redoc")) + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/src/objects/utils/autoschema.py b/src/objects/utils/autoschema.py index bd12b493..a20d29ec 100644 --- a/src/objects/utils/autoschema.py +++ b/src/objects/utils/autoschema.py @@ -40,9 +40,13 @@ def _get_filter_parameters(self): return [] return super()._get_filter_parameters() - def _get_response_for_code(self, serializer, status_code, media_types=None): + def _get_response_for_code( + self, serializer, status_code, media_types=None, direction="response" + ): """add default description to the response""" - response = super()._get_response_for_code(serializer, status_code, media_types) + response = super()._get_response_for_code( + serializer, status_code, media_types, direction + ) if not response.get("description"): response["description"] = HTTP_STATUS_CODE_TITLES.get(int(status_code)) @@ -149,9 +153,9 @@ def get_fields_params(self) -> list[OpenApiParameter]: return [] - def _get_request_body(self): + def _get_request_body(self, direction="request"): """update search request body with filter parameters""" - request_body = super()._get_request_body() + request_body = super()._get_request_body(direction) if self.view.action == "search": filter_params = self.get_filter_params_for_search() From 8fc92251d5177f1a9bdf3030092afd3f2d0f863f Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:47:27 +0100 Subject: [PATCH 13/15] Remove duplicate setting --- src/objects/conf/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index f1775a8b..d4c67e7d 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -342,7 +342,6 @@ # If set, specifies a template to render when a user is locked out. Template # receives cooloff_time and failure_limit as context variables. Default: None AXES_LOCKOUT_TEMPLATE = "account_blocked.html" -AXES_LOCKOUT_TEMPLATE = "account_blocked.html" AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent", "username"]] # The default meta precedence order From b68b437f2a94f3039ae387c048ea2918c6a6b59d Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:47:40 +0100 Subject: [PATCH 14/15] Document Celery variables --- docs/installation/config.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/installation/config.rst b/docs/installation/config.rst index 7bee5790..ce2321d5 100644 --- a/docs/installation/config.rst +++ b/docs/installation/config.rst @@ -29,8 +29,8 @@ Required settings Defaults to ``*`` for the ``docker`` environment and defaults to ``127.0.0.1,localhost`` for the ``dev`` environment. -Database settings ------------------ +Common settings +--------------- * ``DB_HOST``: Hostname of the PostgreSQL database. Defaults to ``db`` for the ``docker`` environment, otherwise defaults to ``localhost``. @@ -43,6 +43,12 @@ Database settings * ``DB_PORT``: Port number of the database. Defaults to ``5432``. +* ``CELERY_BROKER_URL``: URL for the Redis task broker for Celery. Defaults + to ``redis://127.0.0.1:6379/1``. + +* ``CELERY_RESULT_BACKEND``: URL for the Redis result broker for Celery. + Defaults to ``redis://127.0.0.1:6379/1``. + Elastic APM settings -------------------- From e3f28ca4b598f1da027783bc4fb09f809eda91b0 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:35:08 +0100 Subject: [PATCH 15/15] Allow disabling 2FA --- CHANGELOG.rst | 8 ++++++++ docs/installation/config.rst | 3 +++ src/objects/conf/base.py | 3 +++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eecdb53e..d5e16383 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,14 @@ Change history ============== +2.3.0 (TBD) +----------- + +.. warning:: + + Two-factor authentication is enabled by default. The ``DISABLE_2FA`` environment variable + can be used to disable it if needed. + 2.2.1 (2024-03-02) ------------------ diff --git a/docs/installation/config.rst b/docs/installation/config.rst index ce2321d5..e0f82432 100644 --- a/docs/installation/config.rst +++ b/docs/installation/config.rst @@ -95,6 +95,9 @@ Other settings traefik, Apache...). Default ``False`` - this is a header that can be spoofed and you need to ensure you control it before enabling this. +* ``DISABLE_2FA``: whether to disable two-factor authentication. Defaults to ``False``. + If set to ``False``, 2FA will be required if not using OIDC. + Initial superuser creation -------------------------- diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index d4c67e7d..db8e498c 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -433,6 +433,9 @@ "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend", ] +if config("DISABLE_2FA", default=False): # pragma: no cover + MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS + # # Mozilla Django OIDC DB settings #