From 63440e40d790776e856c340c7de307de44eeee48 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 3 Dec 2024 16:29:31 +0100 Subject: [PATCH 01/63] [maykinmedia/objects-api#481] Fix requirements --- requirements/base.txt | 27 +++++++++++++++++++++++---- requirements/ci.txt | 35 ++++++++++++++++++++++++++++++++--- requirements/dev.txt | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 004a725..3b3805c 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,11 +1,13 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --no-emit-index-url requirements/base.in # amqp==5.1.1 # via kombu +annotated-types==0.7.0 + # via pydantic ape-pie==0.1.0 # via # commonground-api-common @@ -18,6 +20,8 @@ asgiref==3.6.0 # django-cors-headers asn1crypto==1.5.1 # via webauthn +async-timeout==5.0.1 + # via redis attrs==23.1.0 # via # glom @@ -154,7 +158,7 @@ django-sendfile2==0.7.0 # via django-privates django-sessionprofile==3.0.0 # via open-api-framework -django-setup-configuration==0.1.0 +django-setup-configuration==0.4.0 # via # -r requirements/base.in # open-api-framework @@ -266,6 +270,16 @@ psycopg2==2.9.9 # via open-api-framework pycparser==2.21 # via cffi +pydantic==2.9.2 + # via + # django-setup-configuration + # pydantic-settings +pydantic-core==2.23.4 + # via pydantic +pydantic-settings[yaml]==2.6.1 + # via + # django-setup-configuration + # pydantic-settings pyjwt==2.7.0 # via # commonground-api-common @@ -283,16 +297,19 @@ python-dateutil==2.8.2 python-decouple==3.8 # via open-api-framework python-dotenv==1.0.0 - # via open-api-framework + # via + # open-api-framework + # pydantic-settings pytz==2023.3 # via # drf-yasg # flower -pyyaml==6.0 +pyyaml==6.0.2 # via # drf-spectacular # drf-yasg # oyaml + # pydantic-settings qrcode==6.1 # via django-two-factor-auth redis==4.5.5 @@ -330,6 +347,8 @@ tornado==6.4.1 typing-extensions==4.11.0 # via # mozilla-django-oidc-db + # pydantic + # pydantic-core # zgw-consumers tzdata==2024.1 # via celery diff --git a/requirements/ci.txt b/requirements/ci.txt index fdf3ab0..1639641 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --no-emit-index-url --output-file=requirements/ci.txt requirements/base.txt requirements/ci.in requirements/test-tools.in @@ -8,6 +8,10 @@ amqp==5.1.1 # via # -r requirements/base.txt # kombu +annotated-types==0.7.0 + # via + # -r requirements/base.txt + # pydantic ape-pie==0.1.0 # via # -r requirements/base.txt @@ -24,6 +28,10 @@ asn1crypto==1.5.1 # via # -r requirements/base.txt # webauthn +async-timeout==5.0.1 + # via + # -r requirements/base.txt + # redis attrs==23.1.0 # via # -r requirements/base.txt @@ -238,7 +246,7 @@ django-sessionprofile==3.0.0 # via # -r requirements/base.txt # open-api-framework -django-setup-configuration==0.1.0 +django-setup-configuration==0.4.0 # via # -r requirements/base.txt # open-api-framework @@ -448,6 +456,20 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pydantic==2.9.2 + # via + # -r requirements/base.txt + # django-setup-configuration + # pydantic-settings +pydantic-core==2.23.4 + # via + # -r requirements/base.txt + # pydantic +pydantic-settings[yaml]==2.6.1 + # via + # -r requirements/base.txt + # django-setup-configuration + # pydantic-settings pyflakes==3.2.0 # via flake8 pyjwt==2.7.0 @@ -481,17 +503,19 @@ python-dotenv==1.0.0 # via # -r requirements/base.txt # open-api-framework + # pydantic-settings pytz==2023.3 # via # -r requirements/base.txt # drf-yasg # flower -pyyaml==6.0 +pyyaml==6.0.2 # via # -r requirements/base.txt # drf-spectacular # drf-yasg # oyaml + # pydantic-settings # vcrpy qrcode==6.1 # via @@ -543,6 +567,8 @@ sqlparse==0.5.0 # django tblib==1.7.0 # via -r requirements/test-tools.in +tomli==2.2.1 + # via black tornado==6.4.1 # via # -r requirements/base.txt @@ -550,7 +576,10 @@ tornado==6.4.1 typing-extensions==4.11.0 # via # -r requirements/base.txt + # black # mozilla-django-oidc-db + # pydantic + # pydantic-core # zgw-consumers tzdata==2024.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index a31c3bd..05ce35c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --no-emit-index-url --output-file=requirements/dev.txt requirements/base.txt requirements/dev.in requirements/test-tools.in @@ -10,6 +10,10 @@ amqp==5.1.1 # via # -r requirements/base.txt # kombu +annotated-types==0.7.0 + # via + # -r requirements/base.txt + # pydantic ape-pie==0.1.0 # via # -r requirements/base.txt @@ -26,6 +30,10 @@ asn1crypto==1.5.1 # via # -r requirements/base.txt # webauthn +async-timeout==5.0.1 + # via + # -r requirements/base.txt + # redis attrs==23.1.0 # via # -r requirements/base.txt @@ -249,7 +257,7 @@ django-sessionprofile==3.0.0 # via # -r requirements/base.txt # open-api-framework -django-setup-configuration==0.1.0 +django-setup-configuration==0.4.0 # via # -r requirements/base.txt # open-api-framework @@ -470,6 +478,20 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pydantic==2.9.2 + # via + # -r requirements/base.txt + # django-setup-configuration + # pydantic-settings +pydantic-core==2.23.4 + # via + # -r requirements/base.txt + # pydantic +pydantic-settings[yaml]==2.6.1 + # via + # -r requirements/base.txt + # django-setup-configuration + # pydantic-settings pyflakes==3.0.1 # via flake8 pygments==2.15.1 @@ -507,17 +529,19 @@ python-dotenv==1.0.0 # via # -r requirements/base.txt # open-api-framework + # pydantic-settings pytz==2023.3 # via # -r requirements/base.txt # drf-yasg # flower -pyyaml==6.0 +pyyaml==6.0.2 # via # -r requirements/base.txt # drf-spectacular # drf-yasg # oyaml + # pydantic-settings # vcrpy qrcode==6.1 # via @@ -593,6 +617,12 @@ sqlparse==0.5.0 # django-debug-toolbar tblib==1.7.0 # via -r requirements/test-tools.in +tomli==2.2.1 + # via + # black + # build + # pip-tools + # pyproject-hooks tornado==6.4.1 # via # -r requirements/base.txt @@ -600,7 +630,10 @@ tornado==6.4.1 typing-extensions==4.11.0 # via # -r requirements/base.txt + # black # mozilla-django-oidc-db + # pydantic + # pydantic-core # zgw-consumers tzdata==2024.1 # via From 00ec96b329b5820835f25bd4446b83476c735db1 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 5 Dec 2024 15:22:21 +0100 Subject: [PATCH 02/63] [maykinmedia/objects-api#481] Move config folder in setup_configuration --- src/objecttypes/{config => setup_configuration}/__init__.py | 0 src/objecttypes/{config => setup_configuration}/demo.py | 0 src/objecttypes/{config => setup_configuration}/objects.py | 0 src/objecttypes/{config => setup_configuration}/site.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/objecttypes/{config => setup_configuration}/__init__.py (100%) rename src/objecttypes/{config => setup_configuration}/demo.py (100%) rename src/objecttypes/{config => setup_configuration}/objects.py (100%) rename src/objecttypes/{config => setup_configuration}/site.py (100%) diff --git a/src/objecttypes/config/__init__.py b/src/objecttypes/setup_configuration/__init__.py similarity index 100% rename from src/objecttypes/config/__init__.py rename to src/objecttypes/setup_configuration/__init__.py diff --git a/src/objecttypes/config/demo.py b/src/objecttypes/setup_configuration/demo.py similarity index 100% rename from src/objecttypes/config/demo.py rename to src/objecttypes/setup_configuration/demo.py diff --git a/src/objecttypes/config/objects.py b/src/objecttypes/setup_configuration/objects.py similarity index 100% rename from src/objecttypes/config/objects.py rename to src/objecttypes/setup_configuration/objects.py diff --git a/src/objecttypes/config/site.py b/src/objecttypes/setup_configuration/site.py similarity index 100% rename from src/objecttypes/config/site.py rename to src/objecttypes/setup_configuration/site.py From 5834a296daed704dfddacff2c7caabd3d5f16a4d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 5 Dec 2024 16:35:02 +0100 Subject: [PATCH 03/63] [maykinmedia/objects-api#481] Config in settings --- src/objecttypes/conf/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index c398951..e548867 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -18,7 +18,6 @@ # Project applications. "objecttypes.accounts", "objecttypes.api", - "objecttypes.config", "objecttypes.core", "objecttypes.token", "objecttypes.utils", @@ -53,9 +52,10 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - "objecttypes.config.site.SiteConfigurationStep", - "objecttypes.config.objects.ObjectsAuthStep", - "objecttypes.config.demo.DemoUserStep", + #"objecttypes.config.site.SiteConfigurationStep", + #"objecttypes.config.objects.ObjectsAuthStep", + #"objecttypes.config.demo.DemoUserStep", + "objecttypes.setup_configuration.steps.TokenAuthConfigurationStep", ] From 1e76cc2c53c66f65a3b1bf341126ec8baefb6689 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 5 Dec 2024 16:35:39 +0100 Subject: [PATCH 04/63] [maykinmedia/objects-api#481] Update model with migrations --- src/objecttypes/token/admin.py | 4 +- ...enauth_identifier_alter_tokenauth_token.py | 108 ++++++++++++++++++ src/objecttypes/token/models.py | 28 +++-- src/objecttypes/token/utils.py | 17 +++ src/objecttypes/token/validators.py | 18 +++ 5 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py create mode 100644 src/objecttypes/token/utils.py create mode 100644 src/objecttypes/token/validators.py diff --git a/src/objecttypes/token/admin.py b/src/objecttypes/token/admin.py index 968c947..c4223fd 100644 --- a/src/objecttypes/token/admin.py +++ b/src/objecttypes/token/admin.py @@ -5,11 +5,11 @@ @admin.register(TokenAuth) class TokenAuthAdmin(admin.ModelAdmin): - readonly_fields = ("token",) list_display = ( - "token", + "identifier", "contact_person", "organization", "administration", "application", ) + readonly_fields = ("token",) \ No newline at end of file diff --git a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py new file mode 100644 index 0000000..6d3b76d --- /dev/null +++ b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py @@ -0,0 +1,108 @@ +# Generated by Django 4.2.15 on 2024-12-05 15:26 + +import logging + +from django.db import migrations, models +from django.db.migrations.state import StateApps +from django.db.models.aggregates import Count + +import objecttypes.token.validators + +from objecttypes.token.utils import get_token + +logger = logging.getLogger(__name__) + + +def _generate_unique_identifiers(apps: StateApps, schema_editor) -> None: + TokenAuth = apps.get_model("token", "TokenAuth") + + count = 1 + + for token in TokenAuth.objects.filter(identifier__isnull=True): + while TokenAuth.objects.filter(identifier=f"token-{count}").exists(): + count += 1 + + identifier = f"token-{count}" + logger.debug(f"Generated {identifier} for token {token.pk}") + + token.identifier = identifier + token.save(update_fields=("identifier",)) + + +def _generate_unique_tokens(apps: StateApps, schema_editor) -> None: + TokenAuth = apps.get_model("token", "TokenAuth") + + duplicate_token_values = ( + TokenAuth.objects.values("token") + .annotate(Count("id")) + .order_by() + .filter(id__count__gt=1) + ) + + duplicate_tokens = TokenAuth.objects.filter( + token__in=[item["token"] for item in duplicate_token_values] + ) + + existing_tokens = set( + TokenAuth.objects.exclude( + id__in=[token.id for token in duplicate_tokens] + ).values_list("token", flat=True) + ) + + for token in duplicate_tokens: + _token: str = get_token(existing_tokens=existing_tokens) + + logger.debug(f"Generated a new token for {token.pk}") + + token.token = _token + token.save(update_fields=("token",)) + + existing_tokens.add(token) + +class Migration(migrations.Migration): + dependencies = [ + ("token", "0008_alter_tokenauth_token"), + ] + + operations = [ + migrations.AddField( + model_name="tokenauth", + name="identifier", + field=models.CharField(blank=True, null=True), + ), + migrations.RunPython( + code=_generate_unique_identifiers, + reverse_code=migrations.RunPython.noop, + ), + migrations.RunPython( + code=_generate_unique_tokens, + reverse_code=migrations.RunPython.noop, + ), + migrations.AlterField( + model_name="tokenauth", + name="token", + field=models.CharField(max_length=40, unique=True, verbose_name="token"), + ), + migrations.AlterField( + model_name="tokenauth", + name="identifier", + field=models.SlugField(unique=True), + ), + migrations.AlterField( + model_name="tokenauth", + name="identifier", + field=models.SlugField( + help_text="A human-friendly label to refer to this token", unique=True + ), + ), + migrations.AlterField( + model_name="tokenauth", + name="token", + field=models.CharField( + max_length=40, + unique=True, + validators=[objecttypes.token.validators.validate_whitespace], + verbose_name="token", + ), + ), + ] diff --git a/src/objecttypes/token/models.py b/src/objecttypes/token/models.py index 6b2bcea..c738478 100644 --- a/src/objecttypes/token/models.py +++ b/src/objecttypes/token/models.py @@ -1,19 +1,25 @@ -import binascii -import os - from django.db import models from django.utils.translation import gettext_lazy as _ +from objecttypes.token.utils import get_token +from objecttypes.token.validators import validate_whitespace class TokenAuth(models.Model): - token = models.CharField(_("token"), max_length=40, unique=True) + identifier = models.SlugField( + unique=True, help_text=_("A human-friendly label to refer to this token"), + ) + + token = models.CharField( + _("token"), max_length=40, unique=True, validators=[validate_whitespace], + ) + contact_person = models.CharField( _("contact person"), max_length=200, help_text=_("Name of the person in the organization who can access the API"), ) email = models.EmailField( - _("email"), help_text=_("Email of the person, who can access the API") + _("email"), help_text=_("Email of the person, who can access the API"), ) organization = models.CharField( _("organization"), @@ -27,7 +33,7 @@ class TokenAuth(models.Model): help_text=_("Last date when the token was modified"), ) created = models.DateTimeField( - _("created"), auto_now_add=True, help_text=_("Date when the token was created") + _("created"), auto_now_add=True, help_text=_("Date when the token was created"), ) application = models.CharField( _("application"), @@ -46,10 +52,12 @@ class Meta: verbose_name = _("token authorization") verbose_name_plural = _("token authorizations") + def __str__(self): + return self.contact_person + def save(self, *args, **kwargs): if not self.token: - self.token = self.generate_token() - return super().save(*args, **kwargs) + existing_tokens = TokenAuth.objects.values_list("token", flat=True) + self.token = get_token(existing_tokens=existing_tokens) - def generate_token(self): - return binascii.hexlify(os.urandom(20)).decode() + return super().save(*args, **kwargs) \ No newline at end of file diff --git a/src/objecttypes/token/utils.py b/src/objecttypes/token/utils.py new file mode 100644 index 0000000..0815423 --- /dev/null +++ b/src/objecttypes/token/utils.py @@ -0,0 +1,17 @@ +import secrets +from typing import Iterable + + +def _generate_token() -> str: + return secrets.token_hex(20) + + +def get_token(existing_tokens: Iterable[str]) -> str: + token = _generate_token() + + while token in existing_tokens: + token = _generate_token() + + return token + + diff --git a/src/objecttypes/token/validators.py b/src/objecttypes/token/validators.py new file mode 100644 index 0000000..a8fc228 --- /dev/null +++ b/src/objecttypes/token/validators.py @@ -0,0 +1,18 @@ +import re + +from django.core.exceptions import ValidationError +from django.utils.translation import gettext as _ + +# includes tabs, carriage returns, newlines, form-feeds and vertical whitespace characters +WHITESPACE_PATTERN = re.compile(r".*\s.*") + + +def validate_whitespace(value: str) -> None: + if not value: + raise ValidationError(code="invalid", message=_("Blank values are not allowed")) + + if WHITESPACE_PATTERN.match(value): + raise ValidationError( + code="all-whitespace", + message=_("Tokens cannot contain whitespace-like characters"), + ) From d159b34ba076fcc8015e95f9ea6d7643da8ee25c Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 5 Dec 2024 16:36:45 +0100 Subject: [PATCH 05/63] [maykinmedia/objects-api#481] Black --- src/objecttypes/conf/base.py | 6 +++--- src/objecttypes/token/admin.py | 2 +- ...okenauth_identifier_alter_tokenauth_token.py | 1 + src/objecttypes/token/models.py | 17 ++++++++++++----- src/objecttypes/token/utils.py | 2 -- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index e548867..95c4237 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -52,9 +52,9 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - #"objecttypes.config.site.SiteConfigurationStep", - #"objecttypes.config.objects.ObjectsAuthStep", - #"objecttypes.config.demo.DemoUserStep", + # "objecttypes.config.site.SiteConfigurationStep", + # "objecttypes.config.objects.ObjectsAuthStep", + # "objecttypes.config.demo.DemoUserStep", "objecttypes.setup_configuration.steps.TokenAuthConfigurationStep", ] diff --git a/src/objecttypes/token/admin.py b/src/objecttypes/token/admin.py index c4223fd..7ca9e99 100644 --- a/src/objecttypes/token/admin.py +++ b/src/objecttypes/token/admin.py @@ -12,4 +12,4 @@ class TokenAuthAdmin(admin.ModelAdmin): "administration", "application", ) - readonly_fields = ("token",) \ No newline at end of file + readonly_fields = ("token",) diff --git a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py index 6d3b76d..9bb20fa 100644 --- a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py +++ b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py @@ -59,6 +59,7 @@ def _generate_unique_tokens(apps: StateApps, schema_editor) -> None: existing_tokens.add(token) + class Migration(migrations.Migration): dependencies = [ ("token", "0008_alter_tokenauth_token"), diff --git a/src/objecttypes/token/models.py b/src/objecttypes/token/models.py index c738478..4234c3e 100644 --- a/src/objecttypes/token/models.py +++ b/src/objecttypes/token/models.py @@ -6,11 +6,15 @@ class TokenAuth(models.Model): identifier = models.SlugField( - unique=True, help_text=_("A human-friendly label to refer to this token"), + unique=True, + help_text=_("A human-friendly label to refer to this token"), ) token = models.CharField( - _("token"), max_length=40, unique=True, validators=[validate_whitespace], + _("token"), + max_length=40, + unique=True, + validators=[validate_whitespace], ) contact_person = models.CharField( @@ -19,7 +23,8 @@ class TokenAuth(models.Model): help_text=_("Name of the person in the organization who can access the API"), ) email = models.EmailField( - _("email"), help_text=_("Email of the person, who can access the API"), + _("email"), + help_text=_("Email of the person, who can access the API"), ) organization = models.CharField( _("organization"), @@ -33,7 +38,9 @@ class TokenAuth(models.Model): help_text=_("Last date when the token was modified"), ) created = models.DateTimeField( - _("created"), auto_now_add=True, help_text=_("Date when the token was created"), + _("created"), + auto_now_add=True, + help_text=_("Date when the token was created"), ) application = models.CharField( _("application"), @@ -60,4 +67,4 @@ def save(self, *args, **kwargs): existing_tokens = TokenAuth.objects.values_list("token", flat=True) self.token = get_token(existing_tokens=existing_tokens) - return super().save(*args, **kwargs) \ No newline at end of file + return super().save(*args, **kwargs) diff --git a/src/objecttypes/token/utils.py b/src/objecttypes/token/utils.py index 0815423..6e89987 100644 --- a/src/objecttypes/token/utils.py +++ b/src/objecttypes/token/utils.py @@ -13,5 +13,3 @@ def get_token(existing_tokens: Iterable[str]) -> str: token = _generate_token() return token - - From ce9141b3bf1f66706d7080af67b5805b9db72ee3 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 10:33:44 +0100 Subject: [PATCH 06/63] [maykinmedia/objects-api#481] Create test_authenticaton tests --- src/objecttypes/token/tests/__init__.py | 0 .../token/tests/test_authenticaton.py | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/objecttypes/token/tests/__init__.py create mode 100644 src/objecttypes/token/tests/test_authenticaton.py diff --git a/src/objecttypes/token/tests/__init__.py b/src/objecttypes/token/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objecttypes/token/tests/test_authenticaton.py b/src/objecttypes/token/tests/test_authenticaton.py new file mode 100644 index 0000000..c8120a4 --- /dev/null +++ b/src/objecttypes/token/tests/test_authenticaton.py @@ -0,0 +1,37 @@ +from django.urls import reverse + +from rest_framework import status +from rest_framework.test import APITestCase + +from objecttypes.token.models import TokenAuth + + +class TestTokenAuthAuthorization(APITestCase): + def test_valid_token(self): + token_auth = TokenAuth.objects.create( + contact_person="test_person", + email="test_person@gmail.nl", + identifier="token-1", + ) + response = self.client.get( + reverse("v2:objecttype-list"), + HTTP_AUTHORIZATION=f"Token {token_auth.token}", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_invalid_token(self): + response = self.client.get( + reverse("v2:objecttype-list"), + HTTP_AUTHORIZATION=f"Token 1234-Token-5678", + ) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_empty_token(self): + response = self.client.get( + reverse("v2:objecttype-list"), HTTP_AUTHORIZATION=f"Token" + ) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_non_token(self): + response = self.client.get(reverse("v2:objecttype-list")) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) From 9d9b1c9a3fd83e22b7d8acbad9f391c7fcbc363e Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 10:36:17 +0100 Subject: [PATCH 07/63] [maykinmedia/objects-api#481] Create test_validators --- .../token/tests/test_validators.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/objecttypes/token/tests/test_validators.py diff --git a/src/objecttypes/token/tests/test_validators.py b/src/objecttypes/token/tests/test_validators.py new file mode 100644 index 0000000..a1fe023 --- /dev/null +++ b/src/objecttypes/token/tests/test_validators.py @@ -0,0 +1,45 @@ +from django.core.exceptions import ValidationError +from django.test import SimpleTestCase + +from objecttypes.token.validators import validate_whitespace + + +class WhiteSpaceValidatorTestCase(SimpleTestCase): + def test_characters_only(self): + self.assertIsNone(validate_whitespace("test123")) + + def test_trailing_whitespace(self): + with self.assertRaises(ValidationError): + validate_whitespace("test123 ") + + def test_leading_whitespace(self): + with self.assertRaises(ValidationError): + validate_whitespace(" test123") + + def test_whitespace_in_between(self): + with self.assertRaises(ValidationError): + validate_whitespace("test 123") + + def test_whitespace_only(self): + with self.assertRaises(ValidationError): + validate_whitespace(" ") + + def test_trailing_tab_character(self): + with self.assertRaises(ValidationError): + validate_whitespace("test123\t") + + def test_leading_tab_character(self): + with self.assertRaises(ValidationError): + validate_whitespace("\ttest123") + + def test_tab_character_in_between(self): + with self.assertRaises(ValidationError): + validate_whitespace("test\t123") + + def test_tab_characters_only(self): + with self.assertRaises(ValidationError): + validate_whitespace("\t\t") + + def test_blank_value(self): + with self.assertRaises(ValidationError): + validate_whitespace("") From 8fab4bc4d664b973d630129e3c336a05c53accf1 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 11:24:51 +0100 Subject: [PATCH 08/63] [maykinmedia/objects-api#481] Fix models and migrations --- ...enauth_identifier_alter_tokenauth_token.py | 43 ------------------- src/objecttypes/token/models.py | 14 +++--- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py index 9bb20fa..caa6c34 100644 --- a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py +++ b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py @@ -4,12 +4,9 @@ from django.db import migrations, models from django.db.migrations.state import StateApps -from django.db.models.aggregates import Count import objecttypes.token.validators -from objecttypes.token.utils import get_token - logger = logging.getLogger(__name__) @@ -29,37 +26,6 @@ def _generate_unique_identifiers(apps: StateApps, schema_editor) -> None: token.save(update_fields=("identifier",)) -def _generate_unique_tokens(apps: StateApps, schema_editor) -> None: - TokenAuth = apps.get_model("token", "TokenAuth") - - duplicate_token_values = ( - TokenAuth.objects.values("token") - .annotate(Count("id")) - .order_by() - .filter(id__count__gt=1) - ) - - duplicate_tokens = TokenAuth.objects.filter( - token__in=[item["token"] for item in duplicate_token_values] - ) - - existing_tokens = set( - TokenAuth.objects.exclude( - id__in=[token.id for token in duplicate_tokens] - ).values_list("token", flat=True) - ) - - for token in duplicate_tokens: - _token: str = get_token(existing_tokens=existing_tokens) - - logger.debug(f"Generated a new token for {token.pk}") - - token.token = _token - token.save(update_fields=("token",)) - - existing_tokens.add(token) - - class Migration(migrations.Migration): dependencies = [ ("token", "0008_alter_tokenauth_token"), @@ -75,15 +41,6 @@ class Migration(migrations.Migration): code=_generate_unique_identifiers, reverse_code=migrations.RunPython.noop, ), - migrations.RunPython( - code=_generate_unique_tokens, - reverse_code=migrations.RunPython.noop, - ), - migrations.AlterField( - model_name="tokenauth", - name="token", - field=models.CharField(max_length=40, unique=True, verbose_name="token"), - ), migrations.AlterField( model_name="tokenauth", name="identifier", diff --git a/src/objecttypes/token/models.py b/src/objecttypes/token/models.py index 4234c3e..cf011b8 100644 --- a/src/objecttypes/token/models.py +++ b/src/objecttypes/token/models.py @@ -1,6 +1,8 @@ +import secrets + from django.db import models from django.utils.translation import gettext_lazy as _ -from objecttypes.token.utils import get_token + from objecttypes.token.validators import validate_whitespace @@ -59,12 +61,10 @@ class Meta: verbose_name = _("token authorization") verbose_name_plural = _("token authorizations") - def __str__(self): - return self.contact_person - def save(self, *args, **kwargs): if not self.token: - existing_tokens = TokenAuth.objects.values_list("token", flat=True) - self.token = get_token(existing_tokens=existing_tokens) - + self.token = self.generate_token() return super().save(*args, **kwargs) + + def generate_token(self): + return secrets.token_hex(20) From 694c41d574dc2a92f1cfe3dd959a7984a3adbabd Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 11:26:39 +0100 Subject: [PATCH 09/63] [maykinmedia/objects-api#481] Fix tests --- src/objecttypes/token/tests/test_authenticaton.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/objecttypes/token/tests/test_authenticaton.py b/src/objecttypes/token/tests/test_authenticaton.py index c8120a4..c05ddca 100644 --- a/src/objecttypes/token/tests/test_authenticaton.py +++ b/src/objecttypes/token/tests/test_authenticaton.py @@ -9,8 +9,8 @@ class TestTokenAuthAuthorization(APITestCase): def test_valid_token(self): token_auth = TokenAuth.objects.create( - contact_person="test_person", - email="test_person@gmail.nl", + contact_person="contact_person", + email="contact_person@gmail.nl", identifier="token-1", ) response = self.client.get( From 0fc8ef91f1b6e71f76a379f849dd3b5b90f9b0f4 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 11:36:56 +0100 Subject: [PATCH 10/63] [maykinmedia/objects-api#481] Delete utils --- src/objecttypes/token/utils.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/objecttypes/token/utils.py diff --git a/src/objecttypes/token/utils.py b/src/objecttypes/token/utils.py deleted file mode 100644 index 6e89987..0000000 --- a/src/objecttypes/token/utils.py +++ /dev/null @@ -1,15 +0,0 @@ -import secrets -from typing import Iterable - - -def _generate_token() -> str: - return secrets.token_hex(20) - - -def get_token(existing_tokens: Iterable[str]) -> str: - token = _generate_token() - - while token in existing_tokens: - token = _generate_token() - - return token From b596a135528fe2a609902f1444f85e9a2113b57a Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 11:37:12 +0100 Subject: [PATCH 11/63] [maykinmedia/objects-api#481] Add factory --- src/objecttypes/token/tests/factories/__init__.py | 0 src/objecttypes/token/tests/factories/token.py | 11 +++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/objecttypes/token/tests/factories/__init__.py create mode 100644 src/objecttypes/token/tests/factories/token.py diff --git a/src/objecttypes/token/tests/factories/__init__.py b/src/objecttypes/token/tests/factories/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objecttypes/token/tests/factories/token.py b/src/objecttypes/token/tests/factories/token.py new file mode 100644 index 0000000..88ee0b9 --- /dev/null +++ b/src/objecttypes/token/tests/factories/token.py @@ -0,0 +1,11 @@ +import factory +from objecttypes.token.models import TokenAuth + + +class TokenAuthFactory(factory.django.DjangoModelFactory): + identifier = factory.Sequence(lambda sequence: f"token-{sequence}") + contact_person = factory.Faker("name") + email = factory.Faker("email") + + class Meta: + model = TokenAuth From 6b6ef918c8665e87aa59ce98d67d89ea5f709969 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 15:34:06 +0100 Subject: [PATCH 12/63] [maykinmedia/objects-api#481] Create step and model --- src/objecttypes/conf/base.py | 4 +- src/objecttypes/setup_configuration/models.py | 23 ++++ src/objecttypes/setup_configuration/steps.py | 100 ++++++++++++++++++ .../token/tests/factories/token.py | 1 + 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/objecttypes/setup_configuration/models.py create mode 100644 src/objecttypes/setup_configuration/steps.py diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index 95c4237..b133a7d 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -52,9 +52,7 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - # "objecttypes.config.site.SiteConfigurationStep", - # "objecttypes.config.objects.ObjectsAuthStep", - # "objecttypes.config.demo.DemoUserStep", + "objecttypes.setup_configuration.steps.SiteConfigurationStep", "objecttypes.setup_configuration.steps.TokenAuthConfigurationStep", ] diff --git a/src/objecttypes/setup_configuration/models.py b/src/objecttypes/setup_configuration/models.py new file mode 100644 index 0000000..3457bb5 --- /dev/null +++ b/src/objecttypes/setup_configuration/models.py @@ -0,0 +1,23 @@ +from django_setup_configuration.models import ConfigurationModel +from pydantic import Field + +from objecttypes.token.models import TokenAuth + + +class TokenAuthConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + TokenAuth: ( + "identifier", + "token", + "contact_person", + "email", + "organization", + "application", + "administration", + ) + } + + +class TokenAuthGroupConfigurationModel(ConfigurationModel): + items: list[TokenAuthConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py new file mode 100644 index 0000000..5173292 --- /dev/null +++ b/src/objecttypes/setup_configuration/steps.py @@ -0,0 +1,100 @@ +import logging + +from django.core.exceptions import ValidationError +from django.db import IntegrityError + +from django_setup_configuration.configuration import BaseConfigurationStep +from django_setup_configuration.exceptions import ConfigurationRunFailed + +from objecttypes.setup_configuration.models import TokenAuthGroupConfigurationModel +from objecttypes.token.models import TokenAuth + +logger = logging.getLogger(__name__) + + +class TokenAuthConfigurationStep( + BaseConfigurationStep[TokenAuthGroupConfigurationModel] +): + """ + Configure configuration groups for the Objects API backend + """ + + namespace = "objecttypes_tokens" + enable_setting = "objecttypes_tokens_config_enable" + + verbose_name = "Configuration to set up authentication tokens for ObjectTypes" + config_model = TokenAuthGroupConfigurationModel + + def execute(self, model: TokenAuthGroupConfigurationModel) -> None: + for item in model.items: + logger.info(f"Configuring {item.identifier}") + + model_kwargs = { + "identifier": item.identifier, + "token": item.token, + "contact_person": item.contact_person, + "email": item.email, + "organization": item.organization, + "application": item.application, + "administration": item.administration, + } + + token_instance = TokenAuth(**model_kwargs) + + try: + token_instance.full_clean(exclude=("id",), validate_unique=False) + except ValidationError as exception: + exception_message = ( + f"Validation error(s) occured for {item.identifier}." + ) + raise ConfigurationRunFailed(exception_message) from exception + + logger.debug(f"No validation errors found for {item.identifier}") + + try: + logger.debug(f"Saving {item.identifier}") + + TokenAuth.objects.update_or_create( + identifier=item.identifier, + defaults={ + key: value + for key, value in model_kwargs.items() + if key != "identifier" + }, + ) + except IntegrityError as exception: + exception_message = f"Failed configuring token {item.identifier}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.info(f"Configured {item.identifier}") + + +class SiteConfigurationStep(BaseConfigurationStep[TokenAuthGroupConfigurationModel]): + """ + Configure configuration groups for the Objects API backend + + Configure the application site/domain. + + verbose_name = "Site Configuration" + required_settings = ["OBJECTTYPES_DOMAIN", "OBJECTTYPES_ORGANIZATION"] + enable_setting = "SITES_CONFIG_ENABLE" + + def is_configured(self) -> bool: + site = Site.objects.get_current() + return site.domain == settings.OBJECTTYPES_DOMAIN + + def configure(self): + site = Site.objects.get_current() + site.domain = settings.OBJECTTYPES_DOMAIN + site.name = f"Objecttypes {settings.OBJECTTYPES_ORGANIZATION}".strip() + site.save() + + def test_configuration(self): + full_url = build_absolute_url(reverse("home")) + try: + response = requests.get(full_url) + response.raise_for_status() + except requests.RequestException as exc: + raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc + + """ diff --git a/src/objecttypes/token/tests/factories/token.py b/src/objecttypes/token/tests/factories/token.py index 88ee0b9..7e150ab 100644 --- a/src/objecttypes/token/tests/factories/token.py +++ b/src/objecttypes/token/tests/factories/token.py @@ -1,4 +1,5 @@ import factory + from objecttypes.token.models import TokenAuth From bae8e8262a0034a7c3af78656078b613f1670af3 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 6 Dec 2024 15:35:10 +0100 Subject: [PATCH 13/63] [maykinmedia/objects-api#481] Create tests --- .../setup_configuration/tests/__init__.py | 0 .../tests/files/invalid_setup_empty.yaml | 3 + .../tests/files/valid_setup_complete.yaml | 18 + .../tests/files/valid_setup_default.yaml | 12 + .../tests/test_token_auth_config.py | 349 ++++++++++++++++++ 5 files changed, 382 insertions(+) create mode 100644 src/objecttypes/setup_configuration/tests/__init__.py create mode 100644 src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml create mode 100644 src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml create mode 100644 src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml create mode 100644 src/objecttypes/setup_configuration/tests/test_token_auth_config.py diff --git a/src/objecttypes/setup_configuration/tests/__init__.py b/src/objecttypes/setup_configuration/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml b/src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml new file mode 100644 index 0000000..cb982c0 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml @@ -0,0 +1,3 @@ +objecttypes_tokens_config_enable: true +objecttypes_tokens: + items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml new file mode 100644 index 0000000..e3ce216 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml @@ -0,0 +1,18 @@ +objecttypes_tokens_config_enable: true +objecttypes_tokens: + items: + - identifier: token-1 + token: 18b2b74ef994314b84021d47b9422e82b685d82f + contact_person: Person 1 + email: person-1@example.com + organization: Organization 1 + application: Application 1 + administration: Administration 1 + + - identifier: token-2 + token: e882642bd0ec2482adcdc97258c2e6f98cb06d85 + contact_person: Person 2 + email: person-2@example.com + organization: Organization 2 + application: Application 2 + administration: Administration 2 diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml new file mode 100644 index 0000000..7c23ff0 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml @@ -0,0 +1,12 @@ +objecttypes_tokens_config_enable: true +objecttypes_tokens: + items: + - identifier: token-1 + token: 18b2b74ef994314b84021d47b9422e82b685d82f + contact_person: Person 1 + email: person-1@example.com + + - identifier: token-2 + token: e882642bd0ec2482adcdc97258c2e6f98cb06d85 + contact_person: Person 2 + email: person-2@example.com diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py new file mode 100644 index 0000000..e197595 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -0,0 +1,349 @@ +from pathlib import Path + +from django.test import TestCase + +from django_setup_configuration.exceptions import ( + ConfigurationRunFailed, + PrerequisiteFailed, +) +from django_setup_configuration.test_utils import build_step_config_from_sources + +from objecttypes.setup_configuration.steps import TokenAuthConfigurationStep +from objecttypes.token.models import TokenAuth +from objecttypes.token.tests.factories.token import TokenAuthFactory + +DIR_FILES = (Path(__file__).parent / "files").resolve() + + +class TokenAuthConfigurationStepTests(TestCase): + def test_valid_setup_default(self): + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "valid_setup_default.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + tokens = TokenAuth.objects.order_by("created") + self.assertEqual(tokens.count(), 2) + + token = tokens[0] + self.assertEqual(token.identifier, "token-1") + self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") + self.assertEqual(token.contact_person, "Person 1") + self.assertEqual(token.email, "person-1@example.com") + self.assertEqual(token.organization, "") + self.assertEqual(token.application, "") + self.assertEqual(token.administration, "") + + token = tokens[1] + self.assertEqual(token.identifier, "token-2") + self.assertEqual(token.contact_person, "Person 2") + self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") + self.assertEqual(token.email, "person-2@example.com") + self.assertEqual(token.organization, "") + self.assertEqual(token.application, "") + self.assertEqual(token.administration, "") + + def test_valid_setup_complete(self): + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "valid_setup_complete.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + tokens = TokenAuth.objects.order_by("created") + self.assertEqual(tokens.count(), 2) + + # Same as configuration + token = tokens[0] + self.assertEqual(token.identifier, "token-1") + self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") + self.assertEqual(token.contact_person, "Person 1") + self.assertEqual(token.email, "person-1@example.com") + self.assertEqual(token.organization, "Organization 1") + self.assertEqual(token.application, "Application 1") + self.assertEqual(token.administration, "Administration 1") + + # Token data updated + token = tokens[1] + self.assertEqual(token.identifier, "token-2") + self.assertEqual(token.contact_person, "Person 2") + self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") + self.assertEqual(token.email, "person-2@example.com") + self.assertEqual(token.organization, "Organization 2") + self.assertEqual(token.application, "Application 2") + self.assertEqual(token.administration, "Administration 2") + + self.assertNotEqual(token.token, "1cad42916dfa439af8c69000bf7b6af6a66782af") + self.assertNotEqual(token.contact_person, "Person 3") + self.assertNotEqual(token.email, "person-3@example.com") + + def test_valid_update_existing_tokens(self): + TokenAuthFactory( + identifier="token-1", + token="18b2b74ef994314b84021d47b9422e82b685d82f", + contact_person="Person 1", + email="person-1@example.com", + organization="Organization XYZ", + application="Application XYZ", + administration="Administration XYZ", + ) + + TokenAuthFactory( + identifier="token-2", + token="1cad42916dfa439af8c69000bf7b6af6a66782af", + contact_person="Person 3", + email="person-3@example.com", + ) + + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "valid_setup_complete.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + tokens = TokenAuth.objects.order_by("created") + self.assertEqual(tokens.count(), 2) + + # Same as configuration + token = tokens[0] + self.assertEqual(token.identifier, "token-1") + self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") + self.assertEqual(token.contact_person, "Person 1") + self.assertEqual(token.email, "person-1@example.com") + self.assertEqual(token.organization, "Organization 1") + self.assertEqual(token.application, "Application 1") + self.assertEqual(token.administration, "Administration 1") + + # Token data updated + token = tokens[1] + self.assertEqual(token.identifier, "token-2") + self.assertEqual(token.contact_person, "Person 2") + self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") + self.assertEqual(token.email, "person-2@example.com") + self.assertEqual(token.organization, "Organization 2") + self.assertEqual(token.application, "Application 2") + self.assertEqual(token.administration, "Administration 2") + + self.assertNotEqual(token.token, "1cad42916dfa439af8c69000bf7b6af6a66782af") + self.assertNotEqual(token.contact_person, "Person 3") + self.assertNotEqual(token.email, "person-3@example.com") + + def test_valid_idempotent_step(self): + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "valid_setup_complete.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + tokens = TokenAuth.objects.order_by("created") + self.assertEqual(tokens.count(), 2) + + old_token_a = tokens[0] + self.assertEqual(old_token_a.identifier, "token-1") + self.assertEqual(old_token_a.token, "18b2b74ef994314b84021d47b9422e82b685d82f") + self.assertEqual(old_token_a.contact_person, "Person 1") + self.assertEqual(old_token_a.email, "person-1@example.com") + self.assertEqual(old_token_a.organization, "Organization 1") + self.assertEqual(old_token_a.application, "Application 1") + self.assertEqual(old_token_a.administration, "Administration 1") + + old_token_b = tokens[1] + self.assertEqual(old_token_b.identifier, "token-2") + self.assertEqual(old_token_b.contact_person, "Person 2") + self.assertEqual(old_token_b.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") + self.assertEqual(old_token_b.email, "person-2@example.com") + self.assertEqual(old_token_b.organization, "Organization 2") + self.assertEqual(old_token_b.application, "Application 2") + self.assertEqual(old_token_b.administration, "Administration 2") + + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "valid_setup_complete.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertEqual(tokens.count(), 2) + tokens = TokenAuth.objects.order_by("created") + + new_token_a = tokens[0] + self.assertEqual(new_token_a.identifier, old_token_a.identifier) + self.assertEqual(new_token_a.token, old_token_a.token) + self.assertEqual(new_token_a.contact_person, old_token_a.contact_person) + self.assertEqual(new_token_a.email, old_token_a.email) + self.assertEqual(new_token_a.organization, old_token_a.organization) + self.assertEqual(new_token_a.application, old_token_a.application) + self.assertEqual(new_token_a.administration, old_token_a.administration) + + new_token_b = tokens[1] + self.assertEqual(new_token_b.identifier, old_token_b.identifier) + self.assertEqual(new_token_b.contact_person, old_token_b.contact_person) + self.assertEqual(new_token_b.token, old_token_b.token) + self.assertEqual(new_token_b.email, old_token_b.email) + self.assertEqual(new_token_b.organization, old_token_b.organization) + self.assertEqual(new_token_b.application, old_token_b.application) + self.assertEqual(new_token_b.administration, old_token_b.administration) + + def test_invalid_setup_empty(self): + with self.assertRaises(PrerequisiteFailed) as command_error: + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + str(DIR_FILES / "invalid_setup_empty.yaml"), + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue("Input should be a valid list" in str(command_error.exception)) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_setup_email(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "contact_person": "Person 1", + "email": "invalid", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + with self.assertRaises(ConfigurationRunFailed) as command_error: + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue( + "Validation error(s) occured for token-1" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_setup_token(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "token": "invalid token", + "contact_person": "Person 1", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + with self.assertRaises(ConfigurationRunFailed) as command_error: + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue( + "Validation error(s) occured for token-1" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_setup_token_missing(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "contact_person": "Person 1", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + with self.assertRaises(PrerequisiteFailed) as command_error: + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue("Field required" in str(command_error.exception)) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_setup_contact_person(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "contact_person": "", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + with self.assertRaises(ConfigurationRunFailed) as command_error: + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue( + "Validation error(s) occured for token-1" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_setup_identifier(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "invalid identifier", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "contact_person": "Person 1", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + with self.assertRaises(ConfigurationRunFailed) as command_error: + step = TokenAuthConfigurationStep() + step.execute(setup_config) + self.assertTrue( + "Validation error(s) occured for invalid identifier" + in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) From 952b2b2fe9a4887cca2059384b1e2be888aae5ab Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 09:35:43 +0100 Subject: [PATCH 14/63] [maykinmedia/objects-api#481] Fix old tests --- src/objecttypes/setup_configuration/demo.py | 53 ----------- src/objecttypes/setup_configuration/models.py | 16 ++++ .../setup_configuration/objects.py | 55 ----------- src/objecttypes/setup_configuration/site.py | 37 -------- src/objecttypes/setup_configuration/steps.py | 67 ++++++++----- .../tests/files/sites/invalid_setup.yaml | 3 + .../tests/files/sites/valid_setup.yaml | 8 ++ .../{ => token_auth}/invalid_setup_empty.yaml | 0 .../valid_setup_complete.yaml | 0 .../{ => token_auth}/valid_setup_default.yaml | 0 .../tests/test_site_config.py | 93 +++++++++++++++++++ .../tests/test_token_auth_config.py | 41 +++++++- 12 files changed, 204 insertions(+), 169 deletions(-) delete mode 100644 src/objecttypes/setup_configuration/demo.py delete mode 100644 src/objecttypes/setup_configuration/objects.py delete mode 100644 src/objecttypes/setup_configuration/site.py create mode 100644 src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml create mode 100644 src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml rename src/objecttypes/setup_configuration/tests/files/{ => token_auth}/invalid_setup_empty.yaml (100%) rename src/objecttypes/setup_configuration/tests/files/{ => token_auth}/valid_setup_complete.yaml (100%) rename src/objecttypes/setup_configuration/tests/files/{ => token_auth}/valid_setup_default.yaml (100%) create mode 100644 src/objecttypes/setup_configuration/tests/test_site_config.py diff --git a/src/objecttypes/setup_configuration/demo.py b/src/objecttypes/setup_configuration/demo.py deleted file mode 100644 index 215d4c7..0000000 --- a/src/objecttypes/setup_configuration/demo.py +++ /dev/null @@ -1,53 +0,0 @@ -from django.conf import settings -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.token.models import TokenAuth -from objecttypes.utils import build_absolute_url - - -class DemoUserStep(BaseConfigurationStep): - """ - Create demo user to request Objectypes API - """ - - verbose_name = "Demo User Configuration" - required_settings = [ - "DEMO_TOKEN", - "DEMO_PERSON", - "DEMO_EMAIL", - ] - enable_setting = "DEMO_CONFIG_ENABLE" - - def is_configured(self) -> bool: - return TokenAuth.objects.filter(token=settings.DEMO_TOKEN).exists() - - def configure(self): - TokenAuth.objects.update_or_create( - token=settings.DEMO_TOKEN, - defaults={ - "contact_person": settings.DEMO_PERSON, - "email": settings.DEMO_EMAIL, - }, - ) - - def test_configuration(self): - endpoint = reverse("v2:objecttype-list") - full_url = build_absolute_url(endpoint, request=None) - - try: - response = requests.get( - full_url, - headers={ - "Authorization": f"Token {settings.DEMO_TOKEN}", - "Accept": "application/json", - }, - ) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed( - "Could not list objecttypes for the configured token" - ) from exc diff --git a/src/objecttypes/setup_configuration/models.py b/src/objecttypes/setup_configuration/models.py index 3457bb5..522f0d5 100644 --- a/src/objecttypes/setup_configuration/models.py +++ b/src/objecttypes/setup_configuration/models.py @@ -1,3 +1,5 @@ +from django.contrib.sites.models import Site + from django_setup_configuration.models import ConfigurationModel from pydantic import Field @@ -21,3 +23,17 @@ class Meta: class TokenAuthGroupConfigurationModel(ConfigurationModel): items: list[TokenAuthConfigurationModel] = Field() + + +class SiteConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + Site: ( + "domain", + "name", + ) + } + + +class SiteGroupConfigurationModel(ConfigurationModel): + items: list[SiteConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/objects.py b/src/objecttypes/setup_configuration/objects.py deleted file mode 100644 index a24d5cf..0000000 --- a/src/objecttypes/setup_configuration/objects.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.conf import settings -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.token.models import TokenAuth -from objecttypes.utils import build_absolute_url - - -class ObjectsAuthStep(BaseConfigurationStep): - """ - Configure credentials for Objects API to request Objecttypes API - """ - - verbose_name = "Objects API Authentication Configuration" - required_settings = [ - "OBJECTS_OBJECTTYPES_TOKEN", - "OBJECTS_OBJECTTYPES_PERSON", - "OBJECTS_OBJECTTYPES_EMAIL", - ] - enable_setting = "OBJECTS_OBJECTTYPES_CONFIG_ENABLE" - - def is_configured(self) -> bool: - return TokenAuth.objects.filter( - token=settings.OBJECTS_OBJECTTYPES_TOKEN - ).exists() - - def configure(self): - TokenAuth.objects.update_or_create( - token=settings.OBJECTS_OBJECTTYPES_TOKEN, - defaults={ - "contact_person": settings.OBJECTS_OBJECTTYPES_PERSON, - "email": settings.OBJECTS_OBJECTTYPES_EMAIL, - }, - ) - - def test_configuration(self): - endpoint = reverse("v2:objecttype-list") - full_url = build_absolute_url(endpoint, request=None) - - try: - response = requests.get( - full_url, - headers={ - "Authorization": f"Token {settings.OBJECTS_OBJECTTYPES_TOKEN}", - "Accept": "application/json", - }, - ) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed( - "Could not list objecttypes for the configured token" - ) from exc diff --git a/src/objecttypes/setup_configuration/site.py b/src/objecttypes/setup_configuration/site.py deleted file mode 100644 index 3d00b87..0000000 --- a/src/objecttypes/setup_configuration/site.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.conf import settings -from django.contrib.sites.models import Site -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.utils import build_absolute_url - - -class SiteConfigurationStep(BaseConfigurationStep): - """ - Configure the application site/domain. - """ - - verbose_name = "Site Configuration" - required_settings = ["OBJECTTYPES_DOMAIN", "OBJECTTYPES_ORGANIZATION"] - enable_setting = "SITES_CONFIG_ENABLE" - - def is_configured(self) -> bool: - site = Site.objects.get_current() - return site.domain == settings.OBJECTTYPES_DOMAIN - - def configure(self): - site = Site.objects.get_current() - site.domain = settings.OBJECTTYPES_DOMAIN - site.name = f"Objecttypes {settings.OBJECTTYPES_ORGANIZATION}".strip() - site.save() - - def test_configuration(self): - full_url = build_absolute_url(reverse("home")) - try: - response = requests.get(full_url) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py index 5173292..6cbe2ef 100644 --- a/src/objecttypes/setup_configuration/steps.py +++ b/src/objecttypes/setup_configuration/steps.py @@ -1,12 +1,16 @@ import logging +from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.db import IntegrityError from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import ConfigurationRunFailed -from objecttypes.setup_configuration.models import TokenAuthGroupConfigurationModel +from objecttypes.setup_configuration.models import ( + SiteGroupConfigurationModel, + TokenAuthGroupConfigurationModel, +) from objecttypes.token.models import TokenAuth logger = logging.getLogger(__name__) @@ -69,32 +73,49 @@ def execute(self, model: TokenAuthGroupConfigurationModel) -> None: logger.info(f"Configured {item.identifier}") -class SiteConfigurationStep(BaseConfigurationStep[TokenAuthGroupConfigurationModel]): +class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): """ - Configure configuration groups for the Objects API backend - Configure the application site/domain. + """ - verbose_name = "Site Configuration" - required_settings = ["OBJECTTYPES_DOMAIN", "OBJECTTYPES_ORGANIZATION"] - enable_setting = "SITES_CONFIG_ENABLE" + namespace = "objecttypes_sites" + enable_setting = "objecttypes_site_config_enable" + + verbose_name = "Configuration to set up Sites for ObjectTypes" + config_model = SiteGroupConfigurationModel + + def execute(self, model: SiteGroupConfigurationModel) -> None: + for item in model.items: + logger.info(f"Configuring {item.domain}") - def is_configured(self) -> bool: - site = Site.objects.get_current() - return site.domain == settings.OBJECTTYPES_DOMAIN + model_kwargs = { + "domain": item.domain, + "name": item.name, + } - def configure(self): - site = Site.objects.get_current() - site.domain = settings.OBJECTTYPES_DOMAIN - site.name = f"Objecttypes {settings.OBJECTTYPES_ORGANIZATION}".strip() - site.save() + instance = Site(**model_kwargs) - def test_configuration(self): - full_url = build_absolute_url(reverse("home")) - try: - response = requests.get(full_url) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc + try: + instance.full_clean(exclude=("id",), validate_unique=False) + except ValidationError as exception: + exception_message = f"Validation error(s) occured for {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception - """ + logger.debug(f"No validation errors found for {item.domain}") + + try: + logger.debug(f"Saving {item.domain}") + Site.objects.update_or_create( + domain=item.domain, + defaults={ + key: value + for key, value in model_kwargs.items() + if key != "domain" + }, + ) + + except IntegrityError as exception: + exception_message = f"Failed configuring token {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.info(f"Configured {item.domain}") diff --git a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml new file mode 100644 index 0000000..1c377cf --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml @@ -0,0 +1,3 @@ +objecttypes_site_config_enable: true +objecttypes_sites: + items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml new file mode 100644 index 0000000..9034cb6 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml @@ -0,0 +1,8 @@ +objecttypes_site_config_enable: true +objecttypes_sites: + items: + - domain: example-1.com + name: example-1 + + - domain: example-2.com + name: example-2 diff --git a/src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py new file mode 100644 index 0000000..e957efd --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -0,0 +1,93 @@ +from pathlib import Path + +from django.contrib.sites.models import Site +from django.test import TestCase + +from django_setup_configuration.exceptions import ( + ConfigurationRunFailed, + PrerequisiteFailed, +) +from django_setup_configuration.test_utils import build_step_config_from_sources + +from objecttypes.setup_configuration.steps import SitesConfigurationStep + +DIR_FILES = (Path(__file__).parent / "files/sites").resolve() + + +class SitesConfigurationStepTests(TestCase): + def test_valid_setup_default(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "valid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 3) + + site = sites[1] + self.assertEqual(site.domain, "example-1.com") + self.assertEqual(site.name, "example-1") + + site = sites[2] + self.assertEqual(site.domain, "example-2.com") + self.assertEqual(site.name, "example-2") + + def test_valid_update_existing_sites(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + Site.objects.create(domain="example-2.com", name="example-3") + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 2) + + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "valid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 3) + + site = sites[1] + self.assertEqual(site.domain, "example-2.com") + self.assertEqual(site.name, "example-2") + + site = sites[2] + self.assertEqual(site.domain, "example-1.com") + self.assertEqual(site.name, "example-1") + + def test_invalid_setup_empty(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + with self.assertRaises(PrerequisiteFailed) as command_error: + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "invalid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + self.assertTrue("Input should be a valid list" in str(command_error.exception)) + + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index e197595..da0b9e0 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -12,7 +12,7 @@ from objecttypes.token.models import TokenAuth from objecttypes.token.tests.factories.token import TokenAuthFactory -DIR_FILES = (Path(__file__).parent / "files").resolve() +DIR_FILES = (Path(__file__).parent / "files/token_auth").resolve() class TokenAuthConfigurationStepTests(TestCase): @@ -288,6 +288,45 @@ def test_invalid_setup_token_missing(self): self.assertTrue("Field required" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_setup_token_unique(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "contact_person": "Person 1", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + { + "identifier": "token-2", + "contact_person": "Person 2", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "email": "person-2@example.com", + "organization": "Organization 2", + "application": "Application 2", + "administration": "Administration 2", + }, + ], + }, + } + with self.assertRaises(ConfigurationRunFailed) as command_error: + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue( + "Failed configuring token token-2" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_setup_contact_person(self): object_source = { "objecttypes_tokens_config_enable": True, From b94ee295fcad34835f97e11c4139ce597a47782f Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 09:44:19 +0100 Subject: [PATCH 15/63] [maykinmedia/objects-api#481] Update command line --- bin/setup_configuration.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bin/setup_configuration.sh b/bin/setup_configuration.sh index 216f869..844ddd1 100755 --- a/bin/setup_configuration.sh +++ b/bin/setup_configuration.sh @@ -3,9 +3,12 @@ # setup initial configuration using environment variables # Run this script from the root of the repository -#set -e -${SCRIPTPATH}/wait_for_db.sh +set -e -src/manage.py migrate +if [[ "${RUN_SETUP_CONFIG,,}" =~ ^(true|1|yes)$ ]]; then + # wait for required services + /wait_for_db.sh -src/manage.py setup_configuration --no-selftest + src/manage.py migrate + src/manage.py setup_configuration --yaml-file setup_configuration/data.yaml +fi \ No newline at end of file From 55939bca958ed61be52b1d3b4a6af545798daecd Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 09:44:39 +0100 Subject: [PATCH 16/63] [maykinmedia/objects-api#481] Remove old settings --- src/objecttypes/conf/base.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index b133a7d..63156e0 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -60,21 +60,3 @@ # # Objecttypes settings # - -# setup_configuration command -# sites config -SITES_CONFIG_ENABLE = config("SITES_CONFIG_ENABLE", default=False) -OBJECTTYPES_DOMAIN = config("OBJECTTYPES_DOMAIN", "") -OBJECTTYPES_ORGANIZATION = config("OBJECTTYPES_ORGANIZATION", "") -# objects auth config -OBJECTS_OBJECTTYPES_CONFIG_ENABLE = config( - "OBJECTS_OBJECTTYPES_CONFIG_ENABLE", default=False -) -OBJECTS_OBJECTTYPES_TOKEN = config("OBJECTS_OBJECTTYPES_TOKEN", "") -OBJECTS_OBJECTTYPES_PERSON = config("OBJECTS_OBJECTTYPES_PERSON", "") -OBJECTS_OBJECTTYPES_EMAIL = config("OBJECTS_OBJECTTYPES_EMAIL", "") -# Demo User Configuration -DEMO_CONFIG_ENABLE = config("DEMO_CONFIG_ENABLE", default=False) -DEMO_TOKEN = config("DEMO_TOKEN", "") -DEMO_PERSON = config("DEMO_PERSON", "") -DEMO_EMAIL = config("DEMO_EMAIL", "") From 53fadd8006d8e784d5e238076fb16cd87489fa5b Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 09:54:54 +0100 Subject: [PATCH 17/63] [maykinmedia/objects-api#481] Add configuration file for docker --- docker/setup_configuration/data.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docker/setup_configuration/data.yaml diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml new file mode 100644 index 0000000..3803b0c --- /dev/null +++ b/docker/setup_configuration/data.yaml @@ -0,0 +1,10 @@ +objecttypes_tokens_config_enable: true +objecttypes_tokens: + items: + - identifier: token-1 + token: 18b2b74ef994314b84021d47b9422e82b685d82f + contact_person: Person 1 + email: person-1@example.com + organization: Organization 1 + application: Application 1 + administration: Administration 1 \ No newline at end of file From 0ec85d818d88efb3de40be8907cbafaffac2fda5 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 12:47:25 +0100 Subject: [PATCH 18/63] [maykinmedia/objects-api#481] Fix docker-compose --- docker-compose.yml | 12 ++++-------- src/objecttypes/conf/base.py | 7 +------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ad41c7e..26cb9c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,14 +22,9 @@ services: - CACHE_AXES=redis:6379/0 - DISABLE_2FA=yes - SUBPATH=${SUBPATH:-/} - # setup_configuration env vars - - SITES_CONFIG_ENABLE=yes - - OBJECTTYPES_DOMAIN=web:8000 - - OBJECTTYPES_ORGANIZATION=ObjectTypes - - OBJECTS_OBJECTTYPES_CONFIG_ENABLE=yes - - OBJECTS_OBJECTTYPES_TOKEN=some-random-string - - OBJECTS_OBJECTTYPES_PERSON=Some Person - - OBJECTS_OBJECTTYPES_EMAIL=objects@objects.local + - RUN_SETUP_CONFIG=${RUN_SETUP_CONFIG:-true} + volumes: &web-volumes + - ./docker/setup_configuration:/app/setup_configuration ports: - 8000:8000 depends_on: @@ -41,5 +36,6 @@ services: build: . environment: *app-env command: /setup_configuration.sh + volumes: *web-volumes depends_on: - db \ No newline at end of file diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index 63156e0..0a15d6f 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -52,11 +52,6 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - "objecttypes.setup_configuration.steps.SiteConfigurationStep", + "objecttypes.setup_configuration.steps.SitesConfigurationStep", "objecttypes.setup_configuration.steps.TokenAuthConfigurationStep", ] - - -# -# Objecttypes settings -# From 350b5ff3d1a479283d5f39e1d50cc5d43edfba72 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 12:49:42 +0100 Subject: [PATCH 19/63] [maykinmedia/objects-api#481] Flake8 --- src/objecttypes/conf/base.py | 1 - .../setup_configuration/tests/test_site_config.py | 5 +---- src/objecttypes/token/tests/test_authenticaton.py | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index 0a15d6f..85e4336 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -1,5 +1,4 @@ from open_api_framework.conf.base import * # noqa -from open_api_framework.conf.utils import config from .api import * # noqa diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index e957efd..7a10bd2 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -3,10 +3,7 @@ from django.contrib.sites.models import Site from django.test import TestCase -from django_setup_configuration.exceptions import ( - ConfigurationRunFailed, - PrerequisiteFailed, -) +from django_setup_configuration.exceptions import PrerequisiteFailed from django_setup_configuration.test_utils import build_step_config_from_sources from objecttypes.setup_configuration.steps import SitesConfigurationStep diff --git a/src/objecttypes/token/tests/test_authenticaton.py b/src/objecttypes/token/tests/test_authenticaton.py index c05ddca..fb1748d 100644 --- a/src/objecttypes/token/tests/test_authenticaton.py +++ b/src/objecttypes/token/tests/test_authenticaton.py @@ -22,13 +22,13 @@ def test_valid_token(self): def test_invalid_token(self): response = self.client.get( reverse("v2:objecttype-list"), - HTTP_AUTHORIZATION=f"Token 1234-Token-5678", + HTTP_AUTHORIZATION="Token 1234-Token-5678", ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_empty_token(self): response = self.client.get( - reverse("v2:objecttype-list"), HTTP_AUTHORIZATION=f"Token" + reverse("v2:objecttype-list"), HTTP_AUTHORIZATION="Token" ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) From 0c840970579089a2c8a8a1a919c97e9ff7c76d64 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 14:02:35 +0100 Subject: [PATCH 20/63] [maykinmedia/objects-api#481] Create command tests and delete old tests --- .../tests/test_token_auth_config.py | 55 +++++++++- src/objecttypes/tests/commands/__init__.py | 0 .../commands/test_setup_configuration.py | 102 ------------------ src/objecttypes/tests/config/__init__.py | 0 .../tests/config/test_demo_configuration.py | 72 ------------- .../config/test_objects_configuration.py | 72 ------------- .../tests/config/test_site_configuration.py | 66 ------------ 7 files changed, 54 insertions(+), 313 deletions(-) delete mode 100644 src/objecttypes/tests/commands/__init__.py delete mode 100644 src/objecttypes/tests/commands/test_setup_configuration.py delete mode 100644 src/objecttypes/tests/config/__init__.py delete mode 100644 src/objecttypes/tests/config/test_demo_configuration.py delete mode 100644 src/objecttypes/tests/config/test_objects_configuration.py delete mode 100644 src/objecttypes/tests/config/test_site_configuration.py diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index da0b9e0..f6e87d2 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -1,5 +1,7 @@ +from io import StringIO from pathlib import Path +from django.core.management import CommandError, call_command from django.test import TestCase from django_setup_configuration.exceptions import ( @@ -325,7 +327,7 @@ def test_invalid_setup_token_unique(self): self.assertTrue( "Failed configuring token token-2" in str(command_error.exception) ) - self.assertEqual(TokenAuth.objects.count(), 0) + self.assertEqual(TokenAuth.objects.count(), 1) def test_invalid_setup_contact_person(self): object_source = { @@ -386,3 +388,54 @@ def test_invalid_setup_identifier(self): in str(command_error.exception) ) self.assertEqual(TokenAuth.objects.count(), 0) + + def test_valid_call_command(self): + stdout = StringIO() + self.assertEqual(TokenAuth.objects.count(), 0) + call_command( + "setup_configuration", + "--yaml-file", + str(DIR_FILES / "valid_setup_default.yaml"), + stdout=stdout, + ) + self.assertTrue( + "Successfully executed step: Configuration to set up authentication tokens for ObjectTypes" + in stdout.getvalue() + ) + self.assertEqual(TokenAuth.objects.count(), 2) + + tokens = TokenAuth.objects.order_by("created") + self.assertEqual(tokens.count(), 2) + + token = tokens[0] + self.assertEqual(token.identifier, "token-1") + self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") + self.assertEqual(token.contact_person, "Person 1") + self.assertEqual(token.email, "person-1@example.com") + self.assertEqual(token.organization, "") + self.assertEqual(token.application, "") + self.assertEqual(token.administration, "") + + token = tokens[1] + self.assertEqual(token.identifier, "token-2") + self.assertEqual(token.contact_person, "Person 2") + self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") + self.assertEqual(token.email, "person-2@example.com") + self.assertEqual(token.organization, "") + self.assertEqual(token.application, "") + self.assertEqual(token.administration, "") + + def test_invalid_call_command(self): + self.assertEqual(TokenAuth.objects.count(), 0) + with self.assertRaises(CommandError) as command_error: + call_command( + "setup_configuration", + "--yaml-file", + str(DIR_FILES / "invalid_setup_empty.yaml"), + ) + + self.assertTrue( + "Failed to load config model for Configuration to set up authentication tokens for ObjectTypes" + in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) diff --git a/src/objecttypes/tests/commands/__init__.py b/src/objecttypes/tests/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/objecttypes/tests/commands/test_setup_configuration.py b/src/objecttypes/tests/commands/test_setup_configuration.py deleted file mode 100644 index ff05ede..0000000 --- a/src/objecttypes/tests/commands/test_setup_configuration.py +++ /dev/null @@ -1,102 +0,0 @@ -from io import StringIO - -from django.contrib.sites.models import Site -from django.core.management import CommandError, call_command -from django.test import TestCase, override_settings -from django.urls import reverse - -import requests_mock -from rest_framework import status - -from objecttypes.config.demo import DemoUserStep -from objecttypes.config.objects import ObjectsAuthStep -from objecttypes.config.site import SiteConfigurationStep - - -@override_settings( - SITES_CONFIG_ENABLE=True, - OBJECTTYPES_DOMAIN="objecttypes.example.com", - OBJECTTYPES_ORGANIZATION="ACME", - OBJECTS_OBJECTTYPES_CONFIG_ENABLE=True, - OBJECTS_OBJECTTYPES_TOKEN="some-random-string", - OBJECTS_OBJECTTYPES_PERSON="Some Person", - OBJECTS_OBJECTTYPES_EMAIL="objects@objects.local", - DEMO_CONFIG_ENABLE=True, - DEMO_TOKEN="demo-random-string", - DEMO_PERSON="Demo", - DEMO_EMAIL="demo@demo.local", -) -class SetupConfigurationTests(TestCase): - def setUp(self): - super().setUp() - - self.addCleanup(Site.objects.clear_cache) - - @requests_mock.Mocker() - def test_setup_configuration(self, m): - stdout = StringIO() - # mocks - m.get("http://objecttypes.example.com/", status_code=200) - m.get("http://objecttypes.example.com/api/v2/objecttypes", json=[]) - - call_command("setup_configuration", stdout=stdout) - - with self.subTest("Command output"): - command_output = stdout.getvalue().splitlines() - expected_output = [ - f"Configuration will be set up with following steps: [{SiteConfigurationStep()}, " - f"{ObjectsAuthStep()}, {DemoUserStep()}]", - f"Configuring {SiteConfigurationStep()}...", - f"{SiteConfigurationStep()} is successfully configured", - f"Configuring {ObjectsAuthStep()}...", - f"{ObjectsAuthStep()} is successfully configured", - f"Configuring {DemoUserStep()}...", - f"{DemoUserStep()} is successfully configured", - "Instance configuration completed.", - ] - - self.assertEqual(command_output, expected_output) - - with self.subTest("Site configured correctly"): - site = Site.objects.get_current() - self.assertEqual(site.domain, "objecttypes.example.com") - self.assertEqual(site.name, "Objecttypes ACME") - - with self.subTest("Objects can query Objecttypes API"): - response = self.client.get( - reverse("v2:objecttype-list"), - HTTP_AUTHORIZATION="Token some-random-string", - ) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - - with self.subTest("Demo user configured correctly"): - response = self.client.get( - reverse("v2:objecttype-list"), - HTTP_AUTHORIZATION="Token demo-random-string", - ) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - - @requests_mock.Mocker() - def test_setup_configuration_selftest_fails(self, m): - m.get("http://objecttypes.example.com/", status_code=200) - m.get("http://objecttypes.example.com/api/v2/objecttypes", status_code=500) - - with self.assertRaisesMessage( - CommandError, - "Configuration test failed with errors: " - "Objects API Authentication Configuration: " - "Could not list objecttypes for the configured token", - ): - call_command("setup_configuration") - - @requests_mock.Mocker() - def test_setup_configuration_without_selftest(self, m): - stdout = StringIO() - - call_command("setup_configuration", no_selftest=True, stdout=stdout) - command_output = stdout.getvalue() - - self.assertEqual(len(m.request_history), 0) - self.assertTrue("Selftest is skipped" in command_output) diff --git a/src/objecttypes/tests/config/__init__.py b/src/objecttypes/tests/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/objecttypes/tests/config/test_demo_configuration.py b/src/objecttypes/tests/config/test_demo_configuration.py deleted file mode 100644 index 952cfdb..0000000 --- a/src/objecttypes/tests/config/test_demo_configuration.py +++ /dev/null @@ -1,72 +0,0 @@ -from unittest.mock import patch - -from django.test import TestCase, override_settings - -import requests -import requests_mock -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.config.demo import DemoUserStep -from objecttypes.token.models import TokenAuth - - -@override_settings( - DEMO_TOKEN="demo-random-string", DEMO_PERSON="Demo", DEMO_EMAIL="demo@demo.local" -) -class DemoConfigurationTests(TestCase): - def test_configure(self): - configuration = DemoUserStep() - - configuration.configure() - - token_auth = TokenAuth.objects.get() - self.assertEqual(token_auth.token, "demo-random-string") - self.assertEqual(token_auth.contact_person, "Demo") - self.assertEqual(token_auth.email, "demo@demo.local") - - @requests_mock.Mocker() - @patch( - "objecttypes.config.demo.build_absolute_url", - return_value="http://testserver/objecttypes", - ) - def test_configuration_check_ok(self, m, *mocks): - configuration = DemoUserStep() - configuration.configure() - m.get("http://testserver/objecttypes", json=[]) - - configuration.test_configuration() - - self.assertEqual(m.last_request.url, "http://testserver/objecttypes") - self.assertEqual(m.last_request.method, "GET") - - @requests_mock.Mocker() - @patch( - "objecttypes.config.demo.build_absolute_url", - return_value="http://testserver/objecttypes", - ) - def test_configuration_check_failures(self, m, *mocks): - configuration = DemoUserStep() - configuration.configure() - - mock_kwargs = ( - {"exc": requests.ConnectTimeout}, - {"exc": requests.ConnectionError}, - {"status_code": 404}, - {"status_code": 403}, - {"status_code": 500}, - ) - for mock_config in mock_kwargs: - with self.subTest(mock=mock_config): - m.get("http://testserver/objecttypes", **mock_config) - - with self.assertRaises(SelfTestFailed): - configuration.test_configuration() - - def test_is_configured(self): - configuration = DemoUserStep() - - self.assertFalse(configuration.is_configured()) - - configuration.configure() - - self.assertTrue(configuration.is_configured()) diff --git a/src/objecttypes/tests/config/test_objects_configuration.py b/src/objecttypes/tests/config/test_objects_configuration.py deleted file mode 100644 index 044680c..0000000 --- a/src/objecttypes/tests/config/test_objects_configuration.py +++ /dev/null @@ -1,72 +0,0 @@ -from unittest.mock import patch - -from django.test import TestCase, override_settings - -import requests -import requests_mock -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.config.objects import ObjectsAuthStep -from objecttypes.token.models import TokenAuth - - -@override_settings( - OBJECTS_OBJECTTYPES_TOKEN="some-random-string", - OBJECTS_OBJECTTYPES_PERSON="Some Person", - OBJECTS_OBJECTTYPES_EMAIL="objects@objects.local", -) -class ObjectsConfigurationTests(TestCase): - def test_configure(self): - configuration = ObjectsAuthStep() - - configuration.configure() - - token_auth = TokenAuth.objects.get(token="some-random-string") - self.assertEqual(token_auth.contact_person, "Some Person") - self.assertEqual(token_auth.email, "objects@objects.local") - - @requests_mock.Mocker() - @patch( - "objecttypes.config.objects.build_absolute_url", - return_value="http://testserver/objecttypes", - ) - def test_selftest_ok(self, m, *mocks): - configuration = ObjectsAuthStep() - configuration.configure() - m.get("http://testserver/objecttypes", json=[]) - - configuration.test_configuration() - - self.assertEqual(m.last_request.url, "http://testserver/objecttypes") - self.assertEqual(m.last_request.method, "GET") - - @requests_mock.Mocker() - @patch( - "objecttypes.config.objects.build_absolute_url", - return_value="http://testserver/objecttypes", - ) - def test_selftest_fail(self, m, *mocks): - configuration = ObjectsAuthStep() - configuration.configure() - - mock_kwargs = ( - {"exc": requests.ConnectTimeout}, - {"exc": requests.ConnectionError}, - {"status_code": 404}, - {"status_code": 403}, - {"status_code": 500}, - ) - for mock_config in mock_kwargs: - with self.subTest(mock=mock_config): - m.get("http://testserver/objecttypes", **mock_config) - - with self.assertRaises(SelfTestFailed): - configuration.test_configuration() - - def test_is_configured(self): - configuration = ObjectsAuthStep() - self.assertFalse(configuration.is_configured()) - - configuration.configure() - - self.assertTrue(configuration.is_configured()) diff --git a/src/objecttypes/tests/config/test_site_configuration.py b/src/objecttypes/tests/config/test_site_configuration.py deleted file mode 100644 index 4dccee1..0000000 --- a/src/objecttypes/tests/config/test_site_configuration.py +++ /dev/null @@ -1,66 +0,0 @@ -from django.contrib.sites.models import Site -from django.test import TestCase, override_settings - -import requests -import requests_mock -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.config.site import SiteConfigurationStep - - -@override_settings( - OBJECTTYPES_DOMAIN="localhost:8000", - OBJECTTYPES_ORGANIZATION="ACME", -) -class SiteConfigurationTests(TestCase): - def setUp(self): - super().setUp() - - self.addCleanup(Site.objects.clear_cache) - - def test_set_domain(self): - configuration = SiteConfigurationStep() - configuration.configure() - - site = Site.objects.get_current() - self.assertEqual(site.domain, "localhost:8000") - self.assertEqual(site.name, "Objecttypes ACME") - - @requests_mock.Mocker() - def test_configuration_check_ok(self, m): - m.get("http://localhost:8000/", status_code=200) - configuration = SiteConfigurationStep() - configuration.configure() - - configuration.test_configuration() - - self.assertEqual(m.last_request.url, "http://localhost:8000/") - self.assertEqual(m.last_request.method, "GET") - - @requests_mock.Mocker() - def test_configuration_check_failures(self, m): - configuration = SiteConfigurationStep() - configuration.configure() - - mock_kwargs = ( - {"exc": requests.ConnectTimeout}, - {"exc": requests.ConnectionError}, - {"status_code": 404}, - {"status_code": 403}, - {"status_code": 500}, - ) - for mock_config in mock_kwargs: - with self.subTest(mock=mock_config): - m.get("http://localhost:8000/", **mock_config) - - with self.assertRaises(SelfTestFailed): - configuration.test_configuration() - - def test_is_configured(self): - configuration = SiteConfigurationStep() - - self.assertFalse(configuration.is_configured()) - - configuration.configure() - - self.assertTrue(configuration.is_configured()) From dd87a6324c097efeb7c425d561cf81df01b00a84 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 14:23:49 +0100 Subject: [PATCH 21/63] [maykinmedia/objects-api#481] Fix setup_configuration --- bin/setup_configuration.sh | 2 +- docker/setup_configuration/data.yaml | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/setup_configuration.sh b/bin/setup_configuration.sh index 844ddd1..844ba43 100755 --- a/bin/setup_configuration.sh +++ b/bin/setup_configuration.sh @@ -7,7 +7,7 @@ set -e if [[ "${RUN_SETUP_CONFIG,,}" =~ ^(true|1|yes)$ ]]; then # wait for required services - /wait_for_db.sh + ${SCRIPTPATH}/wait_for_db.sh src/manage.py migrate src/manage.py setup_configuration --yaml-file setup_configuration/data.yaml diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 3803b0c..59952e3 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -7,4 +7,10 @@ objecttypes_tokens: email: person-1@example.com organization: Organization 1 application: Application 1 - administration: Administration 1 \ No newline at end of file + administration: Administration 1 + +objecttypes_site_config_enable: true +objecttypes_sites: + items: + - domain: example-1.com + name: example-1 \ No newline at end of file From 050db039d08362fb781935ff0d381f378ae7a31e Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Mon, 9 Dec 2024 14:24:59 +0100 Subject: [PATCH 22/63] [maykinmedia/objects-api#481] Fix requirements --- requirements/base.txt | 2 +- requirements/dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 3b3805c..cc6e1be 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --no-emit-index-url requirements/base.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 05ce35c..682aecc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --no-emit-index-url --output-file=requirements/dev.txt requirements/base.txt requirements/dev.in requirements/test-tools.in From 3bd76ac3a0878aeaffac68ae7e3a4b21095a033e Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 11:48:07 +0100 Subject: [PATCH 23/63] [maykinmedia/objects-api#481] Fix requirements --- requirements/base.txt | 10 ++-------- requirements/ci.txt | 12 +----------- requirements/dev.txt | 14 -------------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index cc6e1be..8c4d6a2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -20,8 +20,6 @@ asgiref==3.6.0 # django-cors-headers asn1crypto==1.5.1 # via webauthn -async-timeout==5.0.1 - # via redis attrs==23.1.0 # via # glom @@ -197,9 +195,7 @@ drf-nested-routers==0.94.1 # -r requirements/base.in # commonground-api-common drf-spectacular[sidecar]==0.27.2 - # via - # drf-spectacular - # open-api-framework + # via open-api-framework drf-spectacular-sidecar==2024.7.1 # via drf-spectacular drf-yasg==1.21.7 @@ -277,9 +273,7 @@ pydantic==2.9.2 pydantic-core==2.23.4 # via pydantic pydantic-settings[yaml]==2.6.1 - # via - # django-setup-configuration - # pydantic-settings + # via django-setup-configuration pyjwt==2.7.0 # via # commonground-api-common diff --git a/requirements/ci.txt b/requirements/ci.txt index 1639641..53e47df 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --no-emit-index-url --output-file=requirements/ci.txt requirements/base.txt requirements/ci.in requirements/test-tools.in @@ -28,10 +28,6 @@ asn1crypto==1.5.1 # via # -r requirements/base.txt # webauthn -async-timeout==5.0.1 - # via - # -r requirements/base.txt - # redis attrs==23.1.0 # via # -r requirements/base.txt @@ -266,7 +262,6 @@ django-solo==2.0.0 django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 # via # -r requirements/base.txt - # django-two-factor-auth # maykin-2fa django-webtest==1.9.10 # via -r requirements/test-tools.in @@ -301,7 +296,6 @@ drf-nested-routers==0.94.1 drf-spectacular[sidecar]==0.27.2 # via # -r requirements/base.txt - # drf-spectacular # open-api-framework drf-spectacular-sidecar==2024.7.1 # via @@ -469,7 +463,6 @@ pydantic-settings[yaml]==2.6.1 # via # -r requirements/base.txt # django-setup-configuration - # pydantic-settings pyflakes==3.2.0 # via flake8 pyjwt==2.7.0 @@ -567,8 +560,6 @@ sqlparse==0.5.0 # django tblib==1.7.0 # via -r requirements/test-tools.in -tomli==2.2.1 - # via black tornado==6.4.1 # via # -r requirements/base.txt @@ -576,7 +567,6 @@ tornado==6.4.1 typing-extensions==4.11.0 # via # -r requirements/base.txt - # black # mozilla-django-oidc-db # pydantic # pydantic-core diff --git a/requirements/dev.txt b/requirements/dev.txt index 682aecc..d98a302 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -30,10 +30,6 @@ asn1crypto==1.5.1 # via # -r requirements/base.txt # webauthn -async-timeout==5.0.1 - # via - # -r requirements/base.txt - # redis attrs==23.1.0 # via # -r requirements/base.txt @@ -277,7 +273,6 @@ django-solo==2.0.0 django-two-factor-auth[phonenumberslite,webauthn]==1.16.0 # via # -r requirements/base.txt - # django-two-factor-auth # maykin-2fa django-webtest==1.9.10 # via -r requirements/test-tools.in @@ -316,7 +311,6 @@ drf-nested-routers==0.94.1 drf-spectacular[sidecar]==0.27.2 # via # -r requirements/base.txt - # drf-spectacular # open-api-framework drf-spectacular-sidecar==2024.7.1 # via @@ -491,7 +485,6 @@ pydantic-settings[yaml]==2.6.1 # via # -r requirements/base.txt # django-setup-configuration - # pydantic-settings pyflakes==3.0.1 # via flake8 pygments==2.15.1 @@ -617,12 +610,6 @@ sqlparse==0.5.0 # django-debug-toolbar tblib==1.7.0 # via -r requirements/test-tools.in -tomli==2.2.1 - # via - # black - # build - # pip-tools - # pyproject-hooks tornado==6.4.1 # via # -r requirements/base.txt @@ -630,7 +617,6 @@ tornado==6.4.1 typing-extensions==4.11.0 # via # -r requirements/base.txt - # black # mozilla-django-oidc-db # pydantic # pydantic-core From d1fdcaa10e20a18d84900382ce8e5bc293dc92f4 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 12:07:34 +0100 Subject: [PATCH 24/63] [maykinmedia/objects-api#481] Fix data.yaml --- docker/setup_configuration/data.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 59952e3..456d23b 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -12,5 +12,5 @@ objecttypes_tokens: objecttypes_site_config_enable: true objecttypes_sites: items: - - domain: example-1.com - name: example-1 \ No newline at end of file + - domain: example.com + name: example \ No newline at end of file From 8858b43ebc13f0fbbd7985f1b7d62cc29b89ba9d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 12:49:41 +0100 Subject: [PATCH 25/63] [maykinmedia/objects-api#481] Fix base settings --- src/objecttypes/conf/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index 85e4336..1abeba9 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -17,6 +17,7 @@ # Project applications. "objecttypes.accounts", "objecttypes.api", + "objecttypes.setup_configuration", "objecttypes.core", "objecttypes.token", "objecttypes.utils", From c1f13637f362084047b2cbae8ad46763c276170b Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 15:33:25 +0100 Subject: [PATCH 26/63] [maykinmedia/objects-api#481] Fix docker-compose file --- docker-compose.yml | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 26cb9c1..4aab988 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,18 +13,19 @@ services: image: maykinmedia/objecttypes-api:latest build: . environment: &app-env - - DB_USER=objecttypes - - DB_PASSWORD=objecttypes - - DJANGO_SETTINGS_MODULE=objecttypes.conf.docker - - SECRET_KEY=${SECRET_KEY:-fgv=c0hz&tl*8*3m3893@m+1pstrvidc9e^5@fpspmg%cyf15d} - - ALLOWED_HOSTS=* - - CACHE_DEFAULT=redis:6379/0 - - CACHE_AXES=redis:6379/0 - - DISABLE_2FA=yes - - SUBPATH=${SUBPATH:-/} - - RUN_SETUP_CONFIG=${RUN_SETUP_CONFIG:-true} - volumes: &web-volumes - - ./docker/setup_configuration:/app/setup_configuration + DB_USER: objecttypes + DB_PASSWORD: objecttypes + DJANGO_SETTINGS_MODULE: objecttypes.conf.docker + SECRET_KEY: ${SECRET_KEY:-fgv=c0hz&tl*8*3m3893@m+1pstrvidc9e^5@fpspmg%cyf15d} + ALLOWED_HOSTS: '*' + CACHE_DEFAULT: redis:6379/0 + CACHE_AXES: redis:6379/0 + DISABLE_2FA: yes + SUBPATH: ${SUBPATH:-/} + volumes: + - media:/app/media + - private_media:/app/private_media + - log:/app/log ports: - 8000:8000 depends_on: @@ -32,10 +33,21 @@ services: condition: service_completed_successfully web-init: - image: maykinmedia/objecttypes-api:latest build: . - environment: *app-env + environment: + <<: *app-env + # + # Django-setup-configuration + RUN_SETUP_CONFIG: ${RUN_SETUP_CONFIG:-true} command: /setup_configuration.sh - volumes: *web-volumes + volumes: + - log:/app/log + - ./docker/setup_configuration:/app/setup_configuration depends_on: - - db \ No newline at end of file + - db + +volumes: + db: + log: + media: + private_media: \ No newline at end of file From 2384ffa49d2f5cb4f6c3a2e7ad8db8e4a1ca8d5d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 15:42:21 +0100 Subject: [PATCH 27/63] [maykinmedia/objects-api#481] Replace build_step_config_from_sources with execute_single_step --- .../tests/test_site_config.py | 22 ++--- .../tests/test_token_auth_config.py | 82 +++++-------------- 2 files changed, 27 insertions(+), 77 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index 7a10bd2..7579c6d 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -4,7 +4,7 @@ from django.test import TestCase from django_setup_configuration.exceptions import PrerequisiteFailed -from django_setup_configuration.test_utils import build_step_config_from_sources +from django_setup_configuration.test_utils import execute_single_step from objecttypes.setup_configuration.steps import SitesConfigurationStep @@ -19,12 +19,9 @@ def test_valid_setup_default(self): self.assertEqual(site.domain, "example.com") self.assertEqual(site.name, "example.com") - setup_config = build_step_config_from_sources( - SitesConfigurationStep, - str(DIR_FILES / "valid_setup.yaml"), + execute_single_step( + SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - step = SitesConfigurationStep() - step.execute(setup_config) sites = Site.objects.order_by("pk") self.assertEqual(sites.count(), 3) @@ -48,12 +45,9 @@ def test_valid_update_existing_sites(self): sites = Site.objects.order_by("pk") self.assertEqual(sites.count(), 2) - setup_config = build_step_config_from_sources( - SitesConfigurationStep, - str(DIR_FILES / "valid_setup.yaml"), + execute_single_step( + SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - step = SitesConfigurationStep() - step.execute(setup_config) sites = Site.objects.order_by("pk") self.assertEqual(sites.count(), 3) @@ -74,12 +68,10 @@ def test_invalid_setup_empty(self): self.assertEqual(site.name, "example.com") with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( + execute_single_step( SitesConfigurationStep, - str(DIR_FILES / "invalid_setup.yaml"), + yaml_source=str(DIR_FILES / "invalid_setup.yaml"), ) - step = SitesConfigurationStep() - step.execute(setup_config) self.assertTrue("Input should be a valid list" in str(command_error.exception)) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index f6e87d2..7a28d2e 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -8,7 +8,7 @@ ConfigurationRunFailed, PrerequisiteFailed, ) -from django_setup_configuration.test_utils import build_step_config_from_sources +from django_setup_configuration.test_utils import execute_single_step from objecttypes.setup_configuration.steps import TokenAuthConfigurationStep from objecttypes.token.models import TokenAuth @@ -19,12 +19,10 @@ class TokenAuthConfigurationStepTests(TestCase): def test_valid_setup_default(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_default.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_default.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) tokens = TokenAuth.objects.order_by("created") self.assertEqual(tokens.count(), 2) @@ -48,12 +46,10 @@ def test_valid_setup_default(self): self.assertEqual(token.administration, "") def test_valid_setup_complete(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) tokens = TokenAuth.objects.order_by("created") self.assertEqual(tokens.count(), 2) @@ -99,13 +95,10 @@ def test_valid_update_existing_tokens(self): contact_person="Person 3", email="person-3@example.com", ) - - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) tokens = TokenAuth.objects.order_by("created") self.assertEqual(tokens.count(), 2) @@ -135,12 +128,10 @@ def test_valid_update_existing_tokens(self): self.assertNotEqual(token.email, "person-3@example.com") def test_valid_idempotent_step(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) tokens = TokenAuth.objects.order_by("created") self.assertEqual(tokens.count(), 2) @@ -163,12 +154,10 @@ def test_valid_idempotent_step(self): self.assertEqual(old_token_b.application, "Application 2") self.assertEqual(old_token_b.administration, "Administration 2") - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) self.assertEqual(tokens.count(), 2) tokens = TokenAuth.objects.order_by("created") @@ -193,12 +182,10 @@ def test_valid_idempotent_step(self): def test_invalid_setup_empty(self): with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "invalid_setup_empty.yaml"), + yaml_source=str(DIR_FILES / "invalid_setup_empty.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) self.assertTrue("Input should be a valid list" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) @@ -220,13 +207,8 @@ def test_invalid_setup_email(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -250,13 +232,8 @@ def test_invalid_setup_token(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -280,12 +257,7 @@ def test_invalid_setup_token_missing(self): }, } with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue("Field required" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) @@ -317,12 +289,7 @@ def test_invalid_setup_token_unique(self): }, } with self.assertRaises(ConfigurationRunFailed) as command_error: - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Failed configuring token token-2" in str(command_error.exception) @@ -346,13 +313,8 @@ def test_invalid_setup_contact_person(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -376,13 +338,9 @@ def test_invalid_setup_identifier(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) + self.assertTrue( "Validation error(s) occured for invalid identifier" in str(command_error.exception) From 25f9962e2907aed00de35ebf00e7c163f0d2025e Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 15:50:29 +0100 Subject: [PATCH 28/63] [maykinmedia/objects-api#481] Fix Sites tests --- .../tests/test_site_config.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index 7579c6d..436be1f 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -13,9 +13,9 @@ class SitesConfigurationStepTests(TestCase): def test_valid_setup_default(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) + self.assertEqual(Site.objects.count(), 1) + + site = Site.objects.get(name="example.com") self.assertEqual(site.domain, "example.com") self.assertEqual(site.name, "example.com") @@ -26,18 +26,18 @@ def test_valid_setup_default(self): sites = Site.objects.order_by("pk") self.assertEqual(sites.count(), 3) - site = sites[1] + site = sites.get(name="example-1") self.assertEqual(site.domain, "example-1.com") self.assertEqual(site.name, "example-1") - site = sites[2] + site = sites.get(name="example-2") self.assertEqual(site.domain, "example-2.com") self.assertEqual(site.name, "example-2") def test_valid_update_existing_sites(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) + self.assertEqual(Site.objects.count(), 1) + + site = Site.objects.get(name="example.com") self.assertEqual(site.domain, "example.com") self.assertEqual(site.name, "example.com") @@ -52,18 +52,18 @@ def test_valid_update_existing_sites(self): sites = Site.objects.order_by("pk") self.assertEqual(sites.count(), 3) - site = sites[1] + site = sites.get(name="example-2") self.assertEqual(site.domain, "example-2.com") self.assertEqual(site.name, "example-2") - site = sites[2] + site = sites.get(name="example-1") self.assertEqual(site.domain, "example-1.com") self.assertEqual(site.name, "example-1") def test_invalid_setup_empty(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) + self.assertEqual(Site.objects.count(), 1) + + site = Site.objects.get(name="example.com") self.assertEqual(site.domain, "example.com") self.assertEqual(site.name, "example.com") @@ -75,8 +75,8 @@ def test_invalid_setup_empty(self): self.assertTrue("Input should be a valid list" in str(command_error.exception)) - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) + self.assertEqual(Site.objects.count(), 1) + + site = Site.objects.get(name="example.com") self.assertEqual(site.domain, "example.com") self.assertEqual(site.name, "example.com") From 96eb6233ef181f158f96c01667d200b808e320a1 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 15:56:27 +0100 Subject: [PATCH 29/63] [maykinmedia/objects-api#481] Improve Site tests --- .../tests/test_site_config.py | 50 ++++--------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index 436be1f..ff13b16 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -13,59 +13,34 @@ class SitesConfigurationStepTests(TestCase): def test_valid_setup_default(self): - self.assertEqual(Site.objects.count(), 1) - - site = Site.objects.get(name="example.com") - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) execute_single_step( SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - sites = Site.objects.order_by("pk") + sites = Site.objects.all() self.assertEqual(sites.count(), 3) - - site = sites.get(name="example-1") - self.assertEqual(site.domain, "example-1.com") - self.assertEqual(site.name, "example-1") - - site = sites.get(name="example-2") - self.assertEqual(site.domain, "example-2.com") - self.assertEqual(site.name, "example-2") + self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) + self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) def test_valid_update_existing_sites(self): - self.assertEqual(Site.objects.count(), 1) - - site = Site.objects.get(name="example.com") - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) Site.objects.create(domain="example-2.com", name="example-3") - sites = Site.objects.order_by("pk") - self.assertEqual(sites.count(), 2) + self.assertEqual(Site.objects.count(), 2) execute_single_step( SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - sites = Site.objects.order_by("pk") + sites = Site.objects.all() self.assertEqual(sites.count(), 3) - - site = sites.get(name="example-2") - self.assertEqual(site.domain, "example-2.com") - self.assertEqual(site.name, "example-2") - - site = sites.get(name="example-1") - self.assertEqual(site.domain, "example-1.com") - self.assertEqual(site.name, "example-1") + self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) + self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) def test_invalid_setup_empty(self): - self.assertEqual(Site.objects.count(), 1) - - site = Site.objects.get(name="example.com") - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) with self.assertRaises(PrerequisiteFailed) as command_error: execute_single_step( @@ -74,9 +49,4 @@ def test_invalid_setup_empty(self): ) self.assertTrue("Input should be a valid list" in str(command_error.exception)) - self.assertEqual(Site.objects.count(), 1) - - site = Site.objects.get(name="example.com") - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") From 5da131b692e1fe1c5109c481e1d0a63567da4f12 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:03:34 +0100 Subject: [PATCH 30/63] [maykinmedia/objects-api#481] Improve Token tests --- .../tests/test_token_auth_config.py | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 7a28d2e..e2eed4f 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -24,11 +24,10 @@ def test_valid_setup_default(self): yaml_source=str(DIR_FILES / "valid_setup_default.yaml"), ) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -36,8 +35,7 @@ def test_valid_setup_default(self): self.assertEqual(token.application, "") self.assertEqual(token.administration, "") - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -51,12 +49,11 @@ def test_valid_setup_complete(self): yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) # Same as configuration - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -65,8 +62,7 @@ def test_valid_setup_complete(self): self.assertEqual(token.administration, "Administration 1") # Token data updated - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -100,12 +96,11 @@ def test_valid_update_existing_tokens(self): yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) # Same as configuration - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -114,8 +109,7 @@ def test_valid_update_existing_tokens(self): self.assertEqual(token.administration, "Administration 1") # Token data updated - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -133,10 +127,10 @@ def test_valid_idempotent_step(self): yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - old_token_a = tokens[0] + old_token_a = tokens.get(identifier="token-1") self.assertEqual(old_token_a.identifier, "token-1") self.assertEqual(old_token_a.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(old_token_a.contact_person, "Person 1") @@ -145,7 +139,7 @@ def test_valid_idempotent_step(self): self.assertEqual(old_token_a.application, "Application 1") self.assertEqual(old_token_a.administration, "Administration 1") - old_token_b = tokens[1] + old_token_b = tokens.get(identifier="token-2") self.assertEqual(old_token_b.identifier, "token-2") self.assertEqual(old_token_b.contact_person, "Person 2") self.assertEqual(old_token_b.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") @@ -159,10 +153,10 @@ def test_valid_idempotent_step(self): yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - tokens = TokenAuth.objects.order_by("created") - new_token_a = tokens[0] + new_token_a = tokens.get(identifier="token-1") self.assertEqual(new_token_a.identifier, old_token_a.identifier) self.assertEqual(new_token_a.token, old_token_a.token) self.assertEqual(new_token_a.contact_person, old_token_a.contact_person) @@ -171,7 +165,7 @@ def test_valid_idempotent_step(self): self.assertEqual(new_token_a.application, old_token_a.application) self.assertEqual(new_token_a.administration, old_token_a.administration) - new_token_b = tokens[1] + new_token_b = tokens.get(identifier="token-2") self.assertEqual(new_token_b.identifier, old_token_b.identifier) self.assertEqual(new_token_b.contact_person, old_token_b.contact_person) self.assertEqual(new_token_b.token, old_token_b.token) @@ -360,13 +354,10 @@ def test_valid_call_command(self): "Successfully executed step: Configuration to set up authentication tokens for ObjectTypes" in stdout.getvalue() ) - self.assertEqual(TokenAuth.objects.count(), 2) - - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -374,8 +365,7 @@ def test_valid_call_command(self): self.assertEqual(token.application, "") self.assertEqual(token.administration, "") - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") From 997025e415d35f2e79b93b30c7b5d0c741ac127f Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:05:32 +0100 Subject: [PATCH 31/63] [maykinmedia/objects-api#481] Black --- .../setup_configuration/tests/test_site_config.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index ff13b16..81b3ceb 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -13,7 +13,9 @@ class SitesConfigurationStepTests(TestCase): def test_valid_setup_default(self): - self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) execute_single_step( SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") @@ -25,7 +27,9 @@ def test_valid_setup_default(self): self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) def test_valid_update_existing_sites(self): - self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) Site.objects.create(domain="example-2.com", name="example-3") self.assertEqual(Site.objects.count(), 2) @@ -40,7 +44,9 @@ def test_valid_update_existing_sites(self): self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) def test_invalid_setup_empty(self): - self.assertTrue(Site.objects.filter(domain="example.com", name="example.com").exists()) + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) with self.assertRaises(PrerequisiteFailed) as command_error: execute_single_step( From 7f19b9c80708aa8e2438d06e3715cf53c2f529d8 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:08:14 +0100 Subject: [PATCH 32/63] [maykinmedia/objects-api#481] Add test for empty token --- .../tests/test_token_auth_config.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index e2eed4f..2e32eab 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -234,6 +234,31 @@ def test_invalid_setup_token(self): ) self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_empty_token(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "token": "", + "contact_person": "Person 1", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } + with self.assertRaises(ConfigurationRunFailed) as command_error: + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) + + self.assertTrue( + "Validation error(s) occured for token-1" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_setup_token_missing(self): object_source = { "objecttypes_tokens_config_enable": True, From 144b593e28fc4a851f8a2aebe773ede9620d3b89 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:10:46 +0100 Subject: [PATCH 33/63] [maykinmedia/objects-api#481] Copy migration test --- .../token/tests/test_migrations.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/objecttypes/token/tests/test_migrations.py diff --git a/src/objecttypes/token/tests/test_migrations.py b/src/objecttypes/token/tests/test_migrations.py new file mode 100644 index 0000000..5788fa1 --- /dev/null +++ b/src/objecttypes/token/tests/test_migrations.py @@ -0,0 +1,133 @@ +from django.core.management import call_command +from django.db import connection +from django.db.migrations.executor import MigrationExecutor +from django.db.migrations.state import StateApps +from django.test import TransactionTestCase + + +class BaseMigrationTest(TransactionTestCase): + app: str + migrate_from: str # The migration before the one we want to test + migrate_to: str # The migration we want to test + + setting_overrides: dict = {} + + old_app_state: StateApps + app_state: StateApps + + def setUp(self) -> None: + """ + Setup the migration test by reversing to `migrate_from` state, + then applying the `migrate_to` state. + """ + assert self.app is not None, "You must define the `app` attribute" + assert self.migrate_from is not None, "You must define `migrate_from`" + assert self.migrate_to is not None, "You must define `migrate_to`" + + # Step 1: Set up the MigrationExecutor + executor = MigrationExecutor(connection) + + # Step 2: Reverse to the starting migration state + migrate_from = [(self.app, self.migrate_from)] + old_migrate_state = executor.migrate(migrate_from) + + self.old_app_state = old_migrate_state.apps + + def _perform_migration(self) -> None: + migrate_to = [(self.app, self.migrate_to)] + + executor = MigrationExecutor(connection) + executor.loader.build_graph() # reload the graph in case of dependency changes + executor.migrate(migrate_to) + + self.apps = executor.loader.project_state(migrate_to).apps + + @classmethod + def tearDownClass(cls) -> None: + super().tearDownClass() + + # reset to latest migration + call_command("migrate", verbosity=0, database=connection._alias) + + +class TestTokenAuthUniqueness(BaseMigrationTest): + app = "token" + migrate_from = "0001_initial" + migrate_to = "0002_identifier_migration" + + def test_migrate_tokens_to_unique_values(self): + TokenAuth = self.old_app_state.get_model("token", "TokenAuth") + + TokenAuth.objects.create( + token="aa018d1c576c9dae33be1e549f739f2834ebc811", + contact_person="Person 1", + email="test@example.com", + ) + + TokenAuth.objects.create( + token="aa018d1c576c9dae33be1e549f739f2834ebc811", + contact_person="Person 2", + email="duplicate@example.com", + ) + + TokenAuth.objects.create( + token="ab700d6bf906c2b4b42a961c529657314c6a8246", + contact_person="Other person", + email="somebody@else.com", + ) + + self._perform_migration() + + TokenAuth = self.apps.get_model("token", "TokenAuth") + + tokens = TokenAuth.objects.all() + + self.assertEqual(tokens.count(), 3) + + test_token = tokens.get(email="test@example.com") + duplicate_token = tokens.get(email="duplicate@example.com") + unrelated_token = tokens.get(email="somebody@else.com") + + self.assertNotEqual( + test_token.token, "aa018d1c576c9dae33be1e549f739f2834ebc811" + ) + self.assertNotEqual( + duplicate_token.token, "aa018d1c576c9dae33be1e549f739f2834ebc811" + ) + self.assertNotEqual(test_token.token, duplicate_token.token) + + # unrelated token should be not be mutated + self.assertEqual( + unrelated_token.token, "ab700d6bf906c2b4b42a961c529657314c6a8246" + ) + + def test_migrate_tokens_to_unique_identifiers(self): + TokenAuth = self.old_app_state.get_model("token", "TokenAuth") + + TokenAuth.objects.create( + token="aa018d1c576c9dae33be1e549f739f2834ebc811", + contact_person="Person 1", + email="test@example.com", + ) + + TokenAuth.objects.create( + token="ab700d6bf906c2b4b42a961c529657314c6a8246", + contact_person="Other person", + email="somebody@else.com", + ) + + self._perform_migration() + + TokenAuth = self.apps.get_model("token", "TokenAuth") + + tokens = TokenAuth.objects.all() + + self.assertEqual(tokens.count(), 2) + + first_token = tokens[0] + last_token = tokens[1] + + self.assertTrue(first_token.token) + self.assertTrue(last_token.token) + + self.assertNotEqual(first_token.identifier, last_token.identifier) \ No newline at end of file From fad25678b7213dea05a809ea582078a981048e0a Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:25:19 +0100 Subject: [PATCH 34/63] [maykinmedia/objects-api#481] Fix migration tests --- .../token/tests/test_migrations.py | 61 +++---------------- 1 file changed, 8 insertions(+), 53 deletions(-) diff --git a/src/objecttypes/token/tests/test_migrations.py b/src/objecttypes/token/tests/test_migrations.py index 5788fa1..6ad23ca 100644 --- a/src/objecttypes/token/tests/test_migrations.py +++ b/src/objecttypes/token/tests/test_migrations.py @@ -52,64 +52,25 @@ def tearDownClass(cls) -> None: class TestTokenAuthUniqueness(BaseMigrationTest): app = "token" - migrate_from = "0001_initial" - migrate_to = "0002_identifier_migration" + migrate_from = "0008_alter_tokenauth_token" + migrate_to = "0009_tokenauth_identifier_alter_tokenauth_token" - def test_migrate_tokens_to_unique_values(self): + def test_migrate_tokens_check_attr(self): TokenAuth = self.old_app_state.get_model("token", "TokenAuth") - - TokenAuth.objects.create( - token="aa018d1c576c9dae33be1e549f739f2834ebc811", - contact_person="Person 1", - email="test@example.com", - ) - - TokenAuth.objects.create( - token="aa018d1c576c9dae33be1e549f739f2834ebc811", - contact_person="Person 2", - email="duplicate@example.com", - ) - - TokenAuth.objects.create( - token="ab700d6bf906c2b4b42a961c529657314c6a8246", - contact_person="Other person", - email="somebody@else.com", - ) + self.assertFalse(hasattr(TokenAuth, "identifier")) self._perform_migration() TokenAuth = self.apps.get_model("token", "TokenAuth") - - tokens = TokenAuth.objects.all() - - self.assertEqual(tokens.count(), 3) - - test_token = tokens.get(email="test@example.com") - duplicate_token = tokens.get(email="duplicate@example.com") - unrelated_token = tokens.get(email="somebody@else.com") - - self.assertNotEqual( - test_token.token, "aa018d1c576c9dae33be1e549f739f2834ebc811" - ) - self.assertNotEqual( - duplicate_token.token, "aa018d1c576c9dae33be1e549f739f2834ebc811" - ) - self.assertNotEqual(test_token.token, duplicate_token.token) - - # unrelated token should be not be mutated - self.assertEqual( - unrelated_token.token, "ab700d6bf906c2b4b42a961c529657314c6a8246" - ) + self.assertTrue(hasattr(TokenAuth, "identifier")) def test_migrate_tokens_to_unique_identifiers(self): TokenAuth = self.old_app_state.get_model("token", "TokenAuth") - TokenAuth.objects.create( token="aa018d1c576c9dae33be1e549f739f2834ebc811", contact_person="Person 1", email="test@example.com", ) - TokenAuth.objects.create( token="ab700d6bf906c2b4b42a961c529657314c6a8246", contact_person="Other person", @@ -119,15 +80,9 @@ def test_migrate_tokens_to_unique_identifiers(self): self._perform_migration() TokenAuth = self.apps.get_model("token", "TokenAuth") - tokens = TokenAuth.objects.all() - self.assertEqual(tokens.count(), 2) - first_token = tokens[0] - last_token = tokens[1] - - self.assertTrue(first_token.token) - self.assertTrue(last_token.token) - - self.assertNotEqual(first_token.identifier, last_token.identifier) \ No newline at end of file + first_token = tokens.get(token="aa018d1c576c9dae33be1e549f739f2834ebc811") + second_token = tokens.get(token="ab700d6bf906c2b4b42a961c529657314c6a8246") + self.assertNotEqual(first_token.identifier, second_token.identifier) \ No newline at end of file From ee56909c05fa9ea40fd9f80245a4009a012431d9 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:29:32 +0100 Subject: [PATCH 35/63] [maykinmedia/objects-api#481] Fix django-setup-configuration version in base.in --- requirements/base.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.in b/requirements/base.in index 7bd8188..8d6f74f 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -7,7 +7,7 @@ furl # Framework libraries django-jsonsuit sharing-configs -django-setup-configuration +django-setup-configuration>=0.4.0 # API libraries drf-nested-routers From f6c3a1e5546415cfefd7edb5809c07b910db604b Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:32:03 +0100 Subject: [PATCH 36/63] [maykinmedia/objects-api#481] Fix docstring --- src/objecttypes/setup_configuration/steps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py index 6cbe2ef..726e30b 100644 --- a/src/objecttypes/setup_configuration/steps.py +++ b/src/objecttypes/setup_configuration/steps.py @@ -20,7 +20,7 @@ class TokenAuthConfigurationStep( BaseConfigurationStep[TokenAuthGroupConfigurationModel] ): """ - Configure configuration groups for the Objects API backend + Configure tokens for other applications to access Objecttypes API """ namespace = "objecttypes_tokens" From a8bc41e2b9f7646decee090978bcd66e29031ffa Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:35:08 +0100 Subject: [PATCH 37/63] [maykinmedia/objects-api#481] Change file_name --- .../{invalid_setup_empty.yaml => invalid_setup.yaml} | 0 .../setup_configuration/tests/test_site_config.py | 2 +- .../setup_configuration/tests/test_token_auth_config.py | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/objecttypes/setup_configuration/tests/files/token_auth/{invalid_setup_empty.yaml => invalid_setup.yaml} (100%) diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index 81b3ceb..170e0dd 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -43,7 +43,7 @@ def test_valid_update_existing_sites(self): self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) - def test_invalid_setup_empty(self): + def test_invalid_setup(self): self.assertTrue( Site.objects.filter(domain="example.com", name="example.com").exists() ) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 2e32eab..a68275a 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -174,11 +174,11 @@ def test_valid_idempotent_step(self): self.assertEqual(new_token_b.application, old_token_b.application) self.assertEqual(new_token_b.administration, old_token_b.administration) - def test_invalid_setup_empty(self): + def test_invalid_setup(self): with self.assertRaises(PrerequisiteFailed) as command_error: execute_single_step( TokenAuthConfigurationStep, - yaml_source=str(DIR_FILES / "invalid_setup_empty.yaml"), + yaml_source=str(DIR_FILES / "invalid_setup.yaml"), ) self.assertTrue("Input should be a valid list" in str(command_error.exception)) @@ -404,7 +404,7 @@ def test_invalid_call_command(self): call_command( "setup_configuration", "--yaml-file", - str(DIR_FILES / "invalid_setup_empty.yaml"), + str(DIR_FILES / "invalid_setup.yaml"), ) self.assertTrue( From 3ca644ebb4b5669c5e05671c6af334969e44784d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:36:30 +0100 Subject: [PATCH 38/63] [maykinmedia/objects-api#481] Fix name objecttypes_sites_config_enable --- docker/setup_configuration/data.yaml | 2 +- src/objecttypes/setup_configuration/steps.py | 2 +- .../setup_configuration/tests/files/sites/valid_setup.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 456d23b..7881cba 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -9,7 +9,7 @@ objecttypes_tokens: application: Application 1 administration: Administration 1 -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: - domain: example.com diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py index 726e30b..363b9bc 100644 --- a/src/objecttypes/setup_configuration/steps.py +++ b/src/objecttypes/setup_configuration/steps.py @@ -79,7 +79,7 @@ class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]) """ namespace = "objecttypes_sites" - enable_setting = "objecttypes_site_config_enable" + enable_setting = "objecttypes_sites_config_enable" verbose_name = "Configuration to set up Sites for ObjectTypes" config_model = SiteGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml index 9034cb6..1999a90 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml @@ -1,4 +1,4 @@ -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: - domain: example-1.com From 00d09ba11a64701cc15f2df7f98d84fc4f391401 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:37:32 +0100 Subject: [PATCH 39/63] [maykinmedia/objects-api#481] Fix name objecttypes_sites_config_enable 2 --- .../setup_configuration/tests/files/sites/invalid_setup.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml index 1c377cf..52fa558 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml @@ -1,3 +1,3 @@ -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: \ No newline at end of file From b71db8e56c0ad716696a3d9496086ddf10da105c Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 10 Dec 2024 16:39:24 +0100 Subject: [PATCH 40/63] [maykinmedia/objects-api#481] Remove tests for call_command --- .../tests/test_token_auth_config.py | 49 ------------------- .../token/tests/test_migrations.py | 2 +- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index a68275a..caee4ec 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -1,7 +1,5 @@ -from io import StringIO from pathlib import Path -from django.core.management import CommandError, call_command from django.test import TestCase from django_setup_configuration.exceptions import ( @@ -365,50 +363,3 @@ def test_invalid_setup_identifier(self): in str(command_error.exception) ) self.assertEqual(TokenAuth.objects.count(), 0) - - def test_valid_call_command(self): - stdout = StringIO() - self.assertEqual(TokenAuth.objects.count(), 0) - call_command( - "setup_configuration", - "--yaml-file", - str(DIR_FILES / "valid_setup_default.yaml"), - stdout=stdout, - ) - self.assertTrue( - "Successfully executed step: Configuration to set up authentication tokens for ObjectTypes" - in stdout.getvalue() - ) - tokens = TokenAuth.objects.all() - self.assertEqual(tokens.count(), 2) - - token = tokens.get(identifier="token-1") - self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") - self.assertEqual(token.contact_person, "Person 1") - self.assertEqual(token.email, "person-1@example.com") - self.assertEqual(token.organization, "") - self.assertEqual(token.application, "") - self.assertEqual(token.administration, "") - - token = tokens.get(identifier="token-2") - self.assertEqual(token.contact_person, "Person 2") - self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") - self.assertEqual(token.email, "person-2@example.com") - self.assertEqual(token.organization, "") - self.assertEqual(token.application, "") - self.assertEqual(token.administration, "") - - def test_invalid_call_command(self): - self.assertEqual(TokenAuth.objects.count(), 0) - with self.assertRaises(CommandError) as command_error: - call_command( - "setup_configuration", - "--yaml-file", - str(DIR_FILES / "invalid_setup.yaml"), - ) - - self.assertTrue( - "Failed to load config model for Configuration to set up authentication tokens for ObjectTypes" - in str(command_error.exception) - ) - self.assertEqual(TokenAuth.objects.count(), 0) diff --git a/src/objecttypes/token/tests/test_migrations.py b/src/objecttypes/token/tests/test_migrations.py index 6ad23ca..0597b31 100644 --- a/src/objecttypes/token/tests/test_migrations.py +++ b/src/objecttypes/token/tests/test_migrations.py @@ -85,4 +85,4 @@ def test_migrate_tokens_to_unique_identifiers(self): first_token = tokens.get(token="aa018d1c576c9dae33be1e549f739f2834ebc811") second_token = tokens.get(token="ab700d6bf906c2b4b42a961c529657314c6a8246") - self.assertNotEqual(first_token.identifier, second_token.identifier) \ No newline at end of file + self.assertNotEqual(first_token.identifier, second_token.identifier) From 97e62773d532b7f751c5d5588b38c43e2dfdbacc Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 09:40:00 +0100 Subject: [PATCH 41/63] [maykinmedia/objects-api#481] Change env variables in docker-compose --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4aab988..95486a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,8 @@ services: db: image: postgres:12-alpine environment: - - POSTGRES_USER=objecttypes - - POSTGRES_PASSWORD=objecttypes + POSTGRES_USER: objecttypes + POSTGRES_PASSWORD: objecttypes command: postgres -c max_connections=300 -c log_min_messages=LOG redis: From 7145b301285b32ac5e42799a7d10da01524bfe77 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 10:11:10 +0100 Subject: [PATCH 42/63] [maykinmedia/objects-api#481] Split in directories --- .../{models.py => models/sites.py} | 0 .../setup_configuration/models/token_auth.py | 39 ++++++ .../{steps.py => steps/sites.py} | 0 .../setup_configuration/steps/token_auth.py | 121 ++++++++++++++++++ ...st_site_config.py => test_sites_config.py} | 0 5 files changed, 160 insertions(+) rename src/objecttypes/setup_configuration/{models.py => models/sites.py} (100%) create mode 100644 src/objecttypes/setup_configuration/models/token_auth.py rename src/objecttypes/setup_configuration/{steps.py => steps/sites.py} (100%) create mode 100644 src/objecttypes/setup_configuration/steps/token_auth.py rename src/objecttypes/setup_configuration/tests/{test_site_config.py => test_sites_config.py} (100%) diff --git a/src/objecttypes/setup_configuration/models.py b/src/objecttypes/setup_configuration/models/sites.py similarity index 100% rename from src/objecttypes/setup_configuration/models.py rename to src/objecttypes/setup_configuration/models/sites.py diff --git a/src/objecttypes/setup_configuration/models/token_auth.py b/src/objecttypes/setup_configuration/models/token_auth.py new file mode 100644 index 0000000..522f0d5 --- /dev/null +++ b/src/objecttypes/setup_configuration/models/token_auth.py @@ -0,0 +1,39 @@ +from django.contrib.sites.models import Site + +from django_setup_configuration.models import ConfigurationModel +from pydantic import Field + +from objecttypes.token.models import TokenAuth + + +class TokenAuthConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + TokenAuth: ( + "identifier", + "token", + "contact_person", + "email", + "organization", + "application", + "administration", + ) + } + + +class TokenAuthGroupConfigurationModel(ConfigurationModel): + items: list[TokenAuthConfigurationModel] = Field() + + +class SiteConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + Site: ( + "domain", + "name", + ) + } + + +class SiteGroupConfigurationModel(ConfigurationModel): + items: list[SiteConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps/sites.py similarity index 100% rename from src/objecttypes/setup_configuration/steps.py rename to src/objecttypes/setup_configuration/steps/sites.py diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py new file mode 100644 index 0000000..363b9bc --- /dev/null +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -0,0 +1,121 @@ +import logging + +from django.contrib.sites.models import Site +from django.core.exceptions import ValidationError +from django.db import IntegrityError + +from django_setup_configuration.configuration import BaseConfigurationStep +from django_setup_configuration.exceptions import ConfigurationRunFailed + +from objecttypes.setup_configuration.models import ( + SiteGroupConfigurationModel, + TokenAuthGroupConfigurationModel, +) +from objecttypes.token.models import TokenAuth + +logger = logging.getLogger(__name__) + + +class TokenAuthConfigurationStep( + BaseConfigurationStep[TokenAuthGroupConfigurationModel] +): + """ + Configure tokens for other applications to access Objecttypes API + """ + + namespace = "objecttypes_tokens" + enable_setting = "objecttypes_tokens_config_enable" + + verbose_name = "Configuration to set up authentication tokens for ObjectTypes" + config_model = TokenAuthGroupConfigurationModel + + def execute(self, model: TokenAuthGroupConfigurationModel) -> None: + for item in model.items: + logger.info(f"Configuring {item.identifier}") + + model_kwargs = { + "identifier": item.identifier, + "token": item.token, + "contact_person": item.contact_person, + "email": item.email, + "organization": item.organization, + "application": item.application, + "administration": item.administration, + } + + token_instance = TokenAuth(**model_kwargs) + + try: + token_instance.full_clean(exclude=("id",), validate_unique=False) + except ValidationError as exception: + exception_message = ( + f"Validation error(s) occured for {item.identifier}." + ) + raise ConfigurationRunFailed(exception_message) from exception + + logger.debug(f"No validation errors found for {item.identifier}") + + try: + logger.debug(f"Saving {item.identifier}") + + TokenAuth.objects.update_or_create( + identifier=item.identifier, + defaults={ + key: value + for key, value in model_kwargs.items() + if key != "identifier" + }, + ) + except IntegrityError as exception: + exception_message = f"Failed configuring token {item.identifier}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.info(f"Configured {item.identifier}") + + +class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): + """ + Configure the application site/domain. + """ + + namespace = "objecttypes_sites" + enable_setting = "objecttypes_sites_config_enable" + + verbose_name = "Configuration to set up Sites for ObjectTypes" + config_model = SiteGroupConfigurationModel + + def execute(self, model: SiteGroupConfigurationModel) -> None: + for item in model.items: + logger.info(f"Configuring {item.domain}") + + model_kwargs = { + "domain": item.domain, + "name": item.name, + } + + instance = Site(**model_kwargs) + + try: + instance.full_clean(exclude=("id",), validate_unique=False) + except ValidationError as exception: + exception_message = f"Validation error(s) occured for {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.debug(f"No validation errors found for {item.domain}") + + try: + logger.debug(f"Saving {item.domain}") + Site.objects.update_or_create( + domain=item.domain, + defaults={ + key: value + for key, value in model_kwargs.items() + if key != "domain" + }, + ) + + except IntegrityError as exception: + exception_message = f"Failed configuring token {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.info(f"Configured {item.domain}") diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_sites_config.py similarity index 100% rename from src/objecttypes/setup_configuration/tests/test_site_config.py rename to src/objecttypes/setup_configuration/tests/test_sites_config.py From dce654ef029d1211427da2f7a4433a4e6eefdc89 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 10:14:16 +0100 Subject: [PATCH 43/63] [maykinmedia/objects-api#481] Fix paths --- .../setup_configuration/models/__init__.py | 0 .../setup_configuration/models/sites.py | 21 ------- .../setup_configuration/models/token_auth.py | 16 ----- .../setup_configuration/steps/__init__.py | 0 .../setup_configuration/steps/sites.py | 63 +------------------ .../setup_configuration/steps/token_auth.py | 52 +-------------- .../tests/test_sites_config.py | 2 +- .../tests/test_token_auth_config.py | 2 +- 8 files changed, 4 insertions(+), 152 deletions(-) create mode 100644 src/objecttypes/setup_configuration/models/__init__.py create mode 100644 src/objecttypes/setup_configuration/steps/__init__.py diff --git a/src/objecttypes/setup_configuration/models/__init__.py b/src/objecttypes/setup_configuration/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objecttypes/setup_configuration/models/sites.py b/src/objecttypes/setup_configuration/models/sites.py index 522f0d5..17f1944 100644 --- a/src/objecttypes/setup_configuration/models/sites.py +++ b/src/objecttypes/setup_configuration/models/sites.py @@ -3,27 +3,6 @@ from django_setup_configuration.models import ConfigurationModel from pydantic import Field -from objecttypes.token.models import TokenAuth - - -class TokenAuthConfigurationModel(ConfigurationModel): - class Meta: - django_model_refs = { - TokenAuth: ( - "identifier", - "token", - "contact_person", - "email", - "organization", - "application", - "administration", - ) - } - - -class TokenAuthGroupConfigurationModel(ConfigurationModel): - items: list[TokenAuthConfigurationModel] = Field() - class SiteConfigurationModel(ConfigurationModel): class Meta: diff --git a/src/objecttypes/setup_configuration/models/token_auth.py b/src/objecttypes/setup_configuration/models/token_auth.py index 522f0d5..3457bb5 100644 --- a/src/objecttypes/setup_configuration/models/token_auth.py +++ b/src/objecttypes/setup_configuration/models/token_auth.py @@ -1,5 +1,3 @@ -from django.contrib.sites.models import Site - from django_setup_configuration.models import ConfigurationModel from pydantic import Field @@ -23,17 +21,3 @@ class Meta: class TokenAuthGroupConfigurationModel(ConfigurationModel): items: list[TokenAuthConfigurationModel] = Field() - - -class SiteConfigurationModel(ConfigurationModel): - class Meta: - django_model_refs = { - Site: ( - "domain", - "name", - ) - } - - -class SiteGroupConfigurationModel(ConfigurationModel): - items: list[SiteConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/steps/__init__.py b/src/objecttypes/setup_configuration/steps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/objecttypes/setup_configuration/steps/sites.py b/src/objecttypes/setup_configuration/steps/sites.py index 363b9bc..911b24f 100644 --- a/src/objecttypes/setup_configuration/steps/sites.py +++ b/src/objecttypes/setup_configuration/steps/sites.py @@ -7,72 +7,11 @@ from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import ConfigurationRunFailed -from objecttypes.setup_configuration.models import ( - SiteGroupConfigurationModel, - TokenAuthGroupConfigurationModel, -) -from objecttypes.token.models import TokenAuth +from objecttypes.setup_configuration.models.sites import SiteGroupConfigurationModel logger = logging.getLogger(__name__) -class TokenAuthConfigurationStep( - BaseConfigurationStep[TokenAuthGroupConfigurationModel] -): - """ - Configure tokens for other applications to access Objecttypes API - """ - - namespace = "objecttypes_tokens" - enable_setting = "objecttypes_tokens_config_enable" - - verbose_name = "Configuration to set up authentication tokens for ObjectTypes" - config_model = TokenAuthGroupConfigurationModel - - def execute(self, model: TokenAuthGroupConfigurationModel) -> None: - for item in model.items: - logger.info(f"Configuring {item.identifier}") - - model_kwargs = { - "identifier": item.identifier, - "token": item.token, - "contact_person": item.contact_person, - "email": item.email, - "organization": item.organization, - "application": item.application, - "administration": item.administration, - } - - token_instance = TokenAuth(**model_kwargs) - - try: - token_instance.full_clean(exclude=("id",), validate_unique=False) - except ValidationError as exception: - exception_message = ( - f"Validation error(s) occured for {item.identifier}." - ) - raise ConfigurationRunFailed(exception_message) from exception - - logger.debug(f"No validation errors found for {item.identifier}") - - try: - logger.debug(f"Saving {item.identifier}") - - TokenAuth.objects.update_or_create( - identifier=item.identifier, - defaults={ - key: value - for key, value in model_kwargs.items() - if key != "identifier" - }, - ) - except IntegrityError as exception: - exception_message = f"Failed configuring token {item.identifier}." - raise ConfigurationRunFailed(exception_message) from exception - - logger.info(f"Configured {item.identifier}") - - class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): """ Configure the application site/domain. diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index 363b9bc..a8787e1 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -1,14 +1,12 @@ import logging -from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.db import IntegrityError from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import ConfigurationRunFailed -from objecttypes.setup_configuration.models import ( - SiteGroupConfigurationModel, +from objecttypes.setup_configuration.models.token_auth import ( TokenAuthGroupConfigurationModel, ) from objecttypes.token.models import TokenAuth @@ -71,51 +69,3 @@ def execute(self, model: TokenAuthGroupConfigurationModel) -> None: raise ConfigurationRunFailed(exception_message) from exception logger.info(f"Configured {item.identifier}") - - -class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): - """ - Configure the application site/domain. - """ - - namespace = "objecttypes_sites" - enable_setting = "objecttypes_sites_config_enable" - - verbose_name = "Configuration to set up Sites for ObjectTypes" - config_model = SiteGroupConfigurationModel - - def execute(self, model: SiteGroupConfigurationModel) -> None: - for item in model.items: - logger.info(f"Configuring {item.domain}") - - model_kwargs = { - "domain": item.domain, - "name": item.name, - } - - instance = Site(**model_kwargs) - - try: - instance.full_clean(exclude=("id",), validate_unique=False) - except ValidationError as exception: - exception_message = f"Validation error(s) occured for {item.domain}." - raise ConfigurationRunFailed(exception_message) from exception - - logger.debug(f"No validation errors found for {item.domain}") - - try: - logger.debug(f"Saving {item.domain}") - Site.objects.update_or_create( - domain=item.domain, - defaults={ - key: value - for key, value in model_kwargs.items() - if key != "domain" - }, - ) - - except IntegrityError as exception: - exception_message = f"Failed configuring token {item.domain}." - raise ConfigurationRunFailed(exception_message) from exception - - logger.info(f"Configured {item.domain}") diff --git a/src/objecttypes/setup_configuration/tests/test_sites_config.py b/src/objecttypes/setup_configuration/tests/test_sites_config.py index 170e0dd..a2ee69c 100644 --- a/src/objecttypes/setup_configuration/tests/test_sites_config.py +++ b/src/objecttypes/setup_configuration/tests/test_sites_config.py @@ -6,7 +6,7 @@ from django_setup_configuration.exceptions import PrerequisiteFailed from django_setup_configuration.test_utils import execute_single_step -from objecttypes.setup_configuration.steps import SitesConfigurationStep +from objecttypes.setup_configuration.steps.sites import SitesConfigurationStep DIR_FILES = (Path(__file__).parent / "files/sites").resolve() diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index caee4ec..bee2f47 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -8,7 +8,7 @@ ) from django_setup_configuration.test_utils import execute_single_step -from objecttypes.setup_configuration.steps import TokenAuthConfigurationStep +from objecttypes.setup_configuration.steps.token_auth import TokenAuthConfigurationStep from objecttypes.token.models import TokenAuth from objecttypes.token.tests.factories.token import TokenAuthFactory From 41818cabfaa61810377531c228a96fe28de6065d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 10:25:23 +0100 Subject: [PATCH 44/63] [maykinmedia/objects-api#481] Update name for SitesConfiguration --- src/objecttypes/setup_configuration/models/sites.py | 2 +- src/objecttypes/setup_configuration/steps/sites.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/objecttypes/setup_configuration/models/sites.py b/src/objecttypes/setup_configuration/models/sites.py index 17f1944..347d8e0 100644 --- a/src/objecttypes/setup_configuration/models/sites.py +++ b/src/objecttypes/setup_configuration/models/sites.py @@ -14,5 +14,5 @@ class Meta: } -class SiteGroupConfigurationModel(ConfigurationModel): +class SitesConfigurationModel(ConfigurationModel): items: list[SiteConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/steps/sites.py b/src/objecttypes/setup_configuration/steps/sites.py index 911b24f..8010a88 100644 --- a/src/objecttypes/setup_configuration/steps/sites.py +++ b/src/objecttypes/setup_configuration/steps/sites.py @@ -7,12 +7,12 @@ from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import ConfigurationRunFailed -from objecttypes.setup_configuration.models.sites import SiteGroupConfigurationModel +from objecttypes.setup_configuration.models.sites import SitesConfigurationModel logger = logging.getLogger(__name__) -class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): +class SitesConfigurationStep(BaseConfigurationStep[SitesConfigurationModel]): """ Configure the application site/domain. """ @@ -21,9 +21,9 @@ class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]) enable_setting = "objecttypes_sites_config_enable" verbose_name = "Configuration to set up Sites for ObjectTypes" - config_model = SiteGroupConfigurationModel + config_model = SitesConfigurationModel - def execute(self, model: SiteGroupConfigurationModel) -> None: + def execute(self, model: SitesConfigurationModel) -> None: for item in model.items: logger.info(f"Configuring {item.domain}") From dfc50cd09a28065125be410b317e7de536f297fa Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 13:48:42 +0100 Subject: [PATCH 45/63] [maykinmedia/objects-api#481] Update namespace for Site --- docker/setup_configuration/data.yaml | 4 ++-- src/objecttypes/setup_configuration/steps/sites.py | 4 ++-- .../setup_configuration/tests/files/sites/invalid_setup.yaml | 4 ++-- .../setup_configuration/tests/files/sites/valid_setup.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 7881cba..d57d47a 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -9,8 +9,8 @@ objecttypes_tokens: application: Application 1 administration: Administration 1 -objecttypes_sites_config_enable: true -objecttypes_sites: +sites_config_enable: true +sites_config: items: - domain: example.com name: example \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/steps/sites.py b/src/objecttypes/setup_configuration/steps/sites.py index 8010a88..5185454 100644 --- a/src/objecttypes/setup_configuration/steps/sites.py +++ b/src/objecttypes/setup_configuration/steps/sites.py @@ -17,8 +17,8 @@ class SitesConfigurationStep(BaseConfigurationStep[SitesConfigurationModel]): Configure the application site/domain. """ - namespace = "objecttypes_sites" - enable_setting = "objecttypes_sites_config_enable" + namespace = "sites_config" + enable_setting = "sites_config_enable" verbose_name = "Configuration to set up Sites for ObjectTypes" config_model = SitesConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml index 52fa558..c6bda53 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml @@ -1,3 +1,3 @@ -objecttypes_sites_config_enable: true -objecttypes_sites: +sites_config_enable: true +sites_config: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml index 1999a90..08f6085 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml @@ -1,5 +1,5 @@ -objecttypes_sites_config_enable: true -objecttypes_sites: +sites_config_enable: true +sites_config: items: - domain: example-1.com name: example-1 From 443b9da12bace9fc32eba4253a2dc372476a3391 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 14:04:35 +0100 Subject: [PATCH 46/63] [maykinmedia/objects-api#481] Update namespace for TokenAuthConfiguration --- docker/setup_configuration/data.yaml | 4 +-- src/objecttypes/conf/base.py | 4 +-- .../setup_configuration/steps/token_auth.py | 4 +-- .../tests/files/token_auth/invalid_setup.yaml | 4 +-- .../token_auth/valid_setup_complete.yaml | 4 +-- .../files/token_auth/valid_setup_default.yaml | 4 +-- .../tests/test_token_auth_config.py | 28 +++++++++---------- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index d57d47a..58c9981 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -1,5 +1,5 @@ -objecttypes_tokens_config_enable: true -objecttypes_tokens: +token_tokenauth_config_enable: true +token_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/conf/base.py b/src/objecttypes/conf/base.py index 1abeba9..60bbf72 100644 --- a/src/objecttypes/conf/base.py +++ b/src/objecttypes/conf/base.py @@ -52,6 +52,6 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - "objecttypes.setup_configuration.steps.SitesConfigurationStep", - "objecttypes.setup_configuration.steps.TokenAuthConfigurationStep", + "objecttypes.setup_configuration.steps.sites.SitesConfigurationStep", + "objecttypes.setup_configuration.steps.token_auth.TokenAuthConfigurationStep", ] diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index a8787e1..7cdf620 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -21,8 +21,8 @@ class TokenAuthConfigurationStep( Configure tokens for other applications to access Objecttypes API """ - namespace = "objecttypes_tokens" - enable_setting = "objecttypes_tokens_config_enable" + namespace = "token_tokenauth" + enable_setting = "token_tokenauth_config_enable" verbose_name = "Configuration to set up authentication tokens for ObjectTypes" config_model = TokenAuthGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml index cb982c0..2749f17 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml @@ -1,3 +1,3 @@ -objecttypes_tokens_config_enable: true -objecttypes_tokens: +token_tokenauth_config_enable: true +token_tokenauth: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml index e3ce216..aca6cca 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml @@ -1,5 +1,5 @@ -objecttypes_tokens_config_enable: true -objecttypes_tokens: +token_tokenauth_config_enable: true +token_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml index 7c23ff0..b9af947 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml @@ -1,5 +1,5 @@ -objecttypes_tokens_config_enable: true -objecttypes_tokens: +token_tokenauth_config_enable: true +token_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index bee2f47..15ebd6d 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -184,8 +184,8 @@ def test_invalid_setup(self): def test_invalid_setup_email(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -209,8 +209,8 @@ def test_invalid_setup_email(self): def test_invalid_setup_token(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -234,8 +234,8 @@ def test_invalid_setup_token(self): def test_invalid_empty_token(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -259,8 +259,8 @@ def test_invalid_empty_token(self): def test_invalid_setup_token_missing(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -281,8 +281,8 @@ def test_invalid_setup_token_missing(self): def test_invalid_setup_token_unique(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -315,8 +315,8 @@ def test_invalid_setup_token_unique(self): def test_invalid_setup_contact_person(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "token-1", @@ -340,8 +340,8 @@ def test_invalid_setup_contact_person(self): def test_invalid_setup_identifier(self): object_source = { - "objecttypes_tokens_config_enable": True, - "objecttypes_tokens": { + "token_tokenauth_config_enable": True, + "token_tokenauth": { "items": [ { "identifier": "invalid identifier", From bf963a644c0c6f15af484c8b66225c2f8ad661dd Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 14:08:58 +0100 Subject: [PATCH 47/63] [maykinmedia/objects-api#481] Remove Field from ConfigurationModel --- src/objecttypes/setup_configuration/models/sites.py | 3 +-- src/objecttypes/setup_configuration/models/token_auth.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/objecttypes/setup_configuration/models/sites.py b/src/objecttypes/setup_configuration/models/sites.py index 347d8e0..360bdbb 100644 --- a/src/objecttypes/setup_configuration/models/sites.py +++ b/src/objecttypes/setup_configuration/models/sites.py @@ -1,7 +1,6 @@ from django.contrib.sites.models import Site from django_setup_configuration.models import ConfigurationModel -from pydantic import Field class SiteConfigurationModel(ConfigurationModel): @@ -15,4 +14,4 @@ class Meta: class SitesConfigurationModel(ConfigurationModel): - items: list[SiteConfigurationModel] = Field() + items: list[SiteConfigurationModel] diff --git a/src/objecttypes/setup_configuration/models/token_auth.py b/src/objecttypes/setup_configuration/models/token_auth.py index 3457bb5..20beab1 100644 --- a/src/objecttypes/setup_configuration/models/token_auth.py +++ b/src/objecttypes/setup_configuration/models/token_auth.py @@ -1,5 +1,4 @@ from django_setup_configuration.models import ConfigurationModel -from pydantic import Field from objecttypes.token.models import TokenAuth @@ -20,4 +19,4 @@ class Meta: class TokenAuthGroupConfigurationModel(ConfigurationModel): - items: list[TokenAuthConfigurationModel] = Field() + items: list[TokenAuthConfigurationModel] From 579f6ef20fd477417fdc35c1f1bab73ab8875654 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Wed, 11 Dec 2024 14:58:32 +0100 Subject: [PATCH 48/63] [maykinmedia/objects-api#481] Update INSTALL.rst --- INSTALL.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/INSTALL.rst b/INSTALL.rst index abf9398..dcc927a 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -274,3 +274,14 @@ There are no specific commands for the project. See ``python src/manage.py --help``. .. _Django framework commands: https://docs.djangoproject.com/en/dev/ref/django-admin/#available-commands + +Configuration (CLI) +======== + +After deploying Objecttypes API, they need to be configured to be fully functional. The command line tool setup_configuration assist with this configuration: + +You can get the full command documentation with: + +.. code-block:: bash + + python src/manage.py setup_configuration --help \ No newline at end of file From 715684f6cbc602ecfe276f013f72f8b7081bcc50 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 10:21:23 +0100 Subject: [PATCH 49/63] [maykinmedia/objects-api#481] Fix tests --- .../setup_configuration/tests/test_token_auth_config.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 15ebd6d..50e4b5c 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -68,10 +68,6 @@ def test_valid_setup_complete(self): self.assertEqual(token.application, "Application 2") self.assertEqual(token.administration, "Administration 2") - self.assertNotEqual(token.token, "1cad42916dfa439af8c69000bf7b6af6a66782af") - self.assertNotEqual(token.contact_person, "Person 3") - self.assertNotEqual(token.email, "person-3@example.com") - def test_valid_update_existing_tokens(self): TokenAuthFactory( identifier="token-1", From 58f90b04cb679cd9f67bf56fa8cf0c845a859b2d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 11:33:36 +0100 Subject: [PATCH 50/63] [maykinmedia/objects-api#481] Remove unnecessary volumes in docker --- docker-compose.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 95486a7..1e9d68c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,8 +23,6 @@ services: DISABLE_2FA: yes SUBPATH: ${SUBPATH:-/} volumes: - - media:/app/media - - private_media:/app/private_media - log:/app/log ports: - 8000:8000 @@ -48,6 +46,4 @@ services: volumes: db: - log: - media: - private_media: \ No newline at end of file + log: \ No newline at end of file From b9c2fc488e045fec0a45bc48372d097f68bbecaa Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 11:59:05 +0100 Subject: [PATCH 51/63] [maykinmedia/objects-api#481] Change namespace --- docker/setup_configuration/data.yaml | 4 +-- .../setup_configuration/steps/token_auth.py | 4 +-- .../tests/files/token_auth/invalid_setup.yaml | 2 +- .../token_auth/valid_setup_complete.yaml | 4 +-- .../files/token_auth/valid_setup_default.yaml | 4 +-- .../tests/test_token_auth_config.py | 28 +++++++++---------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 58c9981..2b89efd 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -1,5 +1,5 @@ -token_tokenauth_config_enable: true -token_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index 7cdf620..ce6a565 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -21,8 +21,8 @@ class TokenAuthConfigurationStep( Configure tokens for other applications to access Objecttypes API """ - namespace = "token_tokenauth" - enable_setting = "token_tokenauth_config_enable" + namespace = "tokenauth" + enable_setting = "tokenauth_config_enable" verbose_name = "Configuration to set up authentication tokens for ObjectTypes" config_model = TokenAuthGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml index 2749f17..8293044 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml @@ -1,3 +1,3 @@ -token_tokenauth_config_enable: true +tokenauth_config_enable: true token_tokenauth: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml index aca6cca..fc7d6ea 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml @@ -1,5 +1,5 @@ -token_tokenauth_config_enable: true -token_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml index b9af947..40d4c9a 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml @@ -1,5 +1,5 @@ -token_tokenauth_config_enable: true -token_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 50e4b5c..2b494f9 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -180,8 +180,8 @@ def test_invalid_setup(self): def test_invalid_setup_email(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -205,8 +205,8 @@ def test_invalid_setup_email(self): def test_invalid_setup_token(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -230,8 +230,8 @@ def test_invalid_setup_token(self): def test_invalid_empty_token(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -255,8 +255,8 @@ def test_invalid_empty_token(self): def test_invalid_setup_token_missing(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -277,8 +277,8 @@ def test_invalid_setup_token_missing(self): def test_invalid_setup_token_unique(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -311,8 +311,8 @@ def test_invalid_setup_token_unique(self): def test_invalid_setup_contact_person(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -336,8 +336,8 @@ def test_invalid_setup_contact_person(self): def test_invalid_setup_identifier(self): object_source = { - "token_tokenauth_config_enable": True, - "token_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "invalid identifier", From 975e0abf9d3ba5bc332a86f7e17d80a8aa8ed448 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 12:06:54 +0100 Subject: [PATCH 52/63] [maykinmedia/objects-api#481] Change namespace in file --- .../tests/files/token_auth/invalid_setup.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml index 8293044..c4481cd 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml @@ -1,3 +1,3 @@ tokenauth_config_enable: true -token_tokenauth: +tokenauth: items: \ No newline at end of file From a2c9c59439a8b3ed45c95f6009dc61cb568aea64 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 12:20:52 +0100 Subject: [PATCH 53/63] [maykinmedia/objects-api#481] Change namespace zgw_tokens_tokenauth --- docker/setup_configuration/data.yaml | 4 +-- .../setup_configuration/steps/token_auth.py | 4 +-- .../tests/files/token_auth/invalid_setup.yaml | 4 +-- .../token_auth/valid_setup_complete.yaml | 4 +-- .../files/token_auth/valid_setup_default.yaml | 4 +-- .../tests/test_token_auth_config.py | 28 +++++++++---------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 2b89efd..2fe064f 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -1,5 +1,5 @@ -tokenauth_config_enable: true -tokenauth: +zgw_tokens_tokenauth_config_enable: true +zgw_tokens_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index ce6a565..859ee5d 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -21,8 +21,8 @@ class TokenAuthConfigurationStep( Configure tokens for other applications to access Objecttypes API """ - namespace = "tokenauth" - enable_setting = "tokenauth_config_enable" + namespace = "zgw_tokens_tokenauth" + enable_setting = "zgw_tokens_tokenauth_config_enable" verbose_name = "Configuration to set up authentication tokens for ObjectTypes" config_model = TokenAuthGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml index c4481cd..eaf9846 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml @@ -1,3 +1,3 @@ -tokenauth_config_enable: true -tokenauth: +zgw_tokens_tokenauth_config_enable: true +zgw_tokens_tokenauth: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml index fc7d6ea..8ce35f6 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml @@ -1,5 +1,5 @@ -tokenauth_config_enable: true -tokenauth: +zgw_tokens_tokenauth_config_enable: true +zgw_tokens_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml index 40d4c9a..f73977d 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml @@ -1,5 +1,5 @@ -tokenauth_config_enable: true -tokenauth: +zgw_tokens_tokenauth_config_enable: true +zgw_tokens_tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 2b494f9..46b39d6 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -180,8 +180,8 @@ def test_invalid_setup(self): def test_invalid_setup_email(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -205,8 +205,8 @@ def test_invalid_setup_email(self): def test_invalid_setup_token(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -230,8 +230,8 @@ def test_invalid_setup_token(self): def test_invalid_empty_token(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -255,8 +255,8 @@ def test_invalid_empty_token(self): def test_invalid_setup_token_missing(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -277,8 +277,8 @@ def test_invalid_setup_token_missing(self): def test_invalid_setup_token_unique(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -311,8 +311,8 @@ def test_invalid_setup_token_unique(self): def test_invalid_setup_contact_person(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "token-1", @@ -336,8 +336,8 @@ def test_invalid_setup_contact_person(self): def test_invalid_setup_identifier(self): object_source = { - "tokenauth_config_enable": True, - "tokenauth": { + "zgw_tokens_tokenauth_config_enable": True, + "zgw_tokens_tokenauth": { "items": [ { "identifier": "invalid identifier", From a757ed44ce2e4128397116cb9d2e131272bc8892 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 16:32:23 +0100 Subject: [PATCH 54/63] Revert "[maykinmedia/objects-api#481] Change namespace zgw_tokens_tokenauth" This reverts commit a2c9c59439a8b3ed45c95f6009dc61cb568aea64. --- docker/setup_configuration/data.yaml | 4 +-- .../setup_configuration/steps/token_auth.py | 4 +-- .../tests/files/token_auth/invalid_setup.yaml | 4 +-- .../token_auth/valid_setup_complete.yaml | 4 +-- .../files/token_auth/valid_setup_default.yaml | 4 +-- .../tests/test_token_auth_config.py | 28 +++++++++---------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 2fe064f..2b89efd 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -1,5 +1,5 @@ -zgw_tokens_tokenauth_config_enable: true -zgw_tokens_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index 859ee5d..ce6a565 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -21,8 +21,8 @@ class TokenAuthConfigurationStep( Configure tokens for other applications to access Objecttypes API """ - namespace = "zgw_tokens_tokenauth" - enable_setting = "zgw_tokens_tokenauth_config_enable" + namespace = "tokenauth" + enable_setting = "tokenauth_config_enable" verbose_name = "Configuration to set up authentication tokens for ObjectTypes" config_model = TokenAuthGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml index eaf9846..c4481cd 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml @@ -1,3 +1,3 @@ -zgw_tokens_tokenauth_config_enable: true -zgw_tokens_tokenauth: +tokenauth_config_enable: true +tokenauth: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml index 8ce35f6..fc7d6ea 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml @@ -1,5 +1,5 @@ -zgw_tokens_tokenauth_config_enable: true -zgw_tokens_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml index f73977d..40d4c9a 100644 --- a/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml +++ b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml @@ -1,5 +1,5 @@ -zgw_tokens_tokenauth_config_enable: true -zgw_tokens_tokenauth: +tokenauth_config_enable: true +tokenauth: items: - identifier: token-1 token: 18b2b74ef994314b84021d47b9422e82b685d82f diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index 46b39d6..2b494f9 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -180,8 +180,8 @@ def test_invalid_setup(self): def test_invalid_setup_email(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -205,8 +205,8 @@ def test_invalid_setup_email(self): def test_invalid_setup_token(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -230,8 +230,8 @@ def test_invalid_setup_token(self): def test_invalid_empty_token(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -255,8 +255,8 @@ def test_invalid_empty_token(self): def test_invalid_setup_token_missing(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -277,8 +277,8 @@ def test_invalid_setup_token_missing(self): def test_invalid_setup_token_unique(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -311,8 +311,8 @@ def test_invalid_setup_token_unique(self): def test_invalid_setup_contact_person(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "token-1", @@ -336,8 +336,8 @@ def test_invalid_setup_contact_person(self): def test_invalid_setup_identifier(self): object_source = { - "zgw_tokens_tokenauth_config_enable": True, - "zgw_tokens_tokenauth": { + "tokenauth_config_enable": True, + "tokenauth": { "items": [ { "identifier": "invalid identifier", From a6dd40cc1d4b5de63c5f34531dfb59f44b5b5481 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Thu, 12 Dec 2024 16:39:33 +0100 Subject: [PATCH 55/63] [maykinmedia/objects-api#481] Fix unnecessary "loop" for one element --- src/objecttypes/setup_configuration/steps/sites.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/objecttypes/setup_configuration/steps/sites.py b/src/objecttypes/setup_configuration/steps/sites.py index 5185454..b5993d1 100644 --- a/src/objecttypes/setup_configuration/steps/sites.py +++ b/src/objecttypes/setup_configuration/steps/sites.py @@ -47,9 +47,7 @@ def execute(self, model: SitesConfigurationModel) -> None: Site.objects.update_or_create( domain=item.domain, defaults={ - key: value - for key, value in model_kwargs.items() - if key != "domain" + "name": item.name, }, ) From 7f11929ed8482ba63e298ce2237dca80dfe7812f Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 10:58:33 +0100 Subject: [PATCH 56/63] [maykinmedia/objects-api#481] Fix INSTALL.rst --- INSTALL.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.rst b/INSTALL.rst index dcc927a..ccb6a29 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -276,7 +276,7 @@ There are no specific commands for the project. See .. _Django framework commands: https://docs.djangoproject.com/en/dev/ref/django-admin/#available-commands Configuration (CLI) -======== +=================== After deploying Objecttypes API, they need to be configured to be fully functional. The command line tool setup_configuration assist with this configuration: From ea8953925a8ce821410680144f69a130b23a6c8d Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 11:35:56 +0100 Subject: [PATCH 57/63] [maykinmedia/objects-api#481] Add link to repo in INSTALL.rst --- INSTALL.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/INSTALL.rst b/INSTALL.rst index ccb6a29..6b99d42 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -278,10 +278,12 @@ There are no specific commands for the project. See Configuration (CLI) =================== -After deploying Objecttypes API, they need to be configured to be fully functional. The command line tool setup_configuration assist with this configuration: +After deploying Objecttypes API, they need to be configured to be fully functional. +The command line tool ``setup_configuration`` assist with this configuration. You can get the full command documentation with: -.. code-block:: bash +See `Django Setup Configuration`_ for all documentation, or type +``python src/manage.py setup_configuration --help``. - python src/manage.py setup_configuration --help \ No newline at end of file +.. _Django Setup Configuration: https://github.com/maykinmedia/django-setup-configuration \ No newline at end of file From 3310deb7875aca0e516a95329d1a62ec14b52efe Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 12:01:03 +0100 Subject: [PATCH 58/63] [maykinmedia/objects-api#481] Fix token tests --- .../token/tests/test_authenticaton.py | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/objecttypes/token/tests/test_authenticaton.py b/src/objecttypes/token/tests/test_authenticaton.py index fb1748d..1873c43 100644 --- a/src/objecttypes/token/tests/test_authenticaton.py +++ b/src/objecttypes/token/tests/test_authenticaton.py @@ -19,16 +19,42 @@ def test_valid_token(self): ) self.assertEqual(response.status_code, status.HTTP_200_OK) - def test_invalid_token(self): + def test_valid_token_with_no_spaces(self): + token_auth = TokenAuth.objects.create( + contact_person="contact_person", + email="contact_person@gmail.nl", + identifier="token-1", + token="1234-Token-5678", + ) response = self.client.get( reverse("v2:objecttype-list"), - HTTP_AUTHORIZATION="Token 1234-Token-5678", + HTTP_AUTHORIZATION=f"Token {token_auth.token}", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_invalid_token_with_spaces(self): + token_auth = TokenAuth.objects.create( + contact_person="contact_person", + email="contact_person@gmail.nl", + identifier="token-1", + token="1234 Token 5678", + ) + response = self.client.get( + reverse("v2:objecttype-list"), + HTTP_AUTHORIZATION=f"Token {token_auth.token}", ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + def test_invalid_token_existing(self): + response = self.client.get( + reverse("v2:objecttype-list"), + HTTP_AUTHORIZATION="Token 1234-Token-5678", + ) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + def test_empty_token(self): response = self.client.get( - reverse("v2:objecttype-list"), HTTP_AUTHORIZATION="Token" + reverse("v2:objecttype-list"), HTTP_AUTHORIZATION="Token " ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) From fd26732dd2a1c871696fd8ac0da0846b7874e602 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 12:03:16 +0100 Subject: [PATCH 59/63] [maykinmedia/objects-api#481] Change name validate_no_whitespace --- ...enauth_identifier_alter_tokenauth_token.py | 2 +- src/objecttypes/token/models.py | 4 ++-- .../token/tests/test_validators.py | 22 +++++++++---------- src/objecttypes/token/validators.py | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py index caa6c34..5adbaf5 100644 --- a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py +++ b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py @@ -59,7 +59,7 @@ class Migration(migrations.Migration): field=models.CharField( max_length=40, unique=True, - validators=[objecttypes.token.validators.validate_whitespace], + validators=[objecttypes.token.validators.validate_no_whitespace], verbose_name="token", ), ), diff --git a/src/objecttypes/token/models.py b/src/objecttypes/token/models.py index cf011b8..6a3b659 100644 --- a/src/objecttypes/token/models.py +++ b/src/objecttypes/token/models.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from objecttypes.token.validators import validate_whitespace +from objecttypes.token.validators import validate_no_whitespace class TokenAuth(models.Model): @@ -16,7 +16,7 @@ class TokenAuth(models.Model): _("token"), max_length=40, unique=True, - validators=[validate_whitespace], + validators=[validate_no_whitespace], ) contact_person = models.CharField( diff --git a/src/objecttypes/token/tests/test_validators.py b/src/objecttypes/token/tests/test_validators.py index a1fe023..8877f97 100644 --- a/src/objecttypes/token/tests/test_validators.py +++ b/src/objecttypes/token/tests/test_validators.py @@ -1,45 +1,45 @@ from django.core.exceptions import ValidationError from django.test import SimpleTestCase -from objecttypes.token.validators import validate_whitespace +from objecttypes.token.validators import validate_no_whitespace class WhiteSpaceValidatorTestCase(SimpleTestCase): def test_characters_only(self): - self.assertIsNone(validate_whitespace("test123")) + self.assertIsNone(validate_no_whitespace("test123")) def test_trailing_whitespace(self): with self.assertRaises(ValidationError): - validate_whitespace("test123 ") + validate_no_whitespace("test123 ") def test_leading_whitespace(self): with self.assertRaises(ValidationError): - validate_whitespace(" test123") + validate_no_whitespace(" test123") def test_whitespace_in_between(self): with self.assertRaises(ValidationError): - validate_whitespace("test 123") + validate_no_whitespace("test 123") def test_whitespace_only(self): with self.assertRaises(ValidationError): - validate_whitespace(" ") + validate_no_whitespace(" ") def test_trailing_tab_character(self): with self.assertRaises(ValidationError): - validate_whitespace("test123\t") + validate_no_whitespace("test123\t") def test_leading_tab_character(self): with self.assertRaises(ValidationError): - validate_whitespace("\ttest123") + validate_no_whitespace("\ttest123") def test_tab_character_in_between(self): with self.assertRaises(ValidationError): - validate_whitespace("test\t123") + validate_no_whitespace("test\t123") def test_tab_characters_only(self): with self.assertRaises(ValidationError): - validate_whitespace("\t\t") + validate_no_whitespace("\t\t") def test_blank_value(self): with self.assertRaises(ValidationError): - validate_whitespace("") + validate_no_whitespace("") diff --git a/src/objecttypes/token/validators.py b/src/objecttypes/token/validators.py index a8fc228..9bab299 100644 --- a/src/objecttypes/token/validators.py +++ b/src/objecttypes/token/validators.py @@ -7,7 +7,7 @@ WHITESPACE_PATTERN = re.compile(r".*\s.*") -def validate_whitespace(value: str) -> None: +def validate_no_whitespace(value: str) -> None: if not value: raise ValidationError(code="invalid", message=_("Blank values are not allowed")) From dcecf538071f8a8f02795a739cfc13e4fe63036b Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 12:08:48 +0100 Subject: [PATCH 60/63] [maykinmedia/objects-api#481] Remove unused validation control --- src/objecttypes/token/tests/test_validators.py | 4 ---- src/objecttypes/token/validators.py | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/objecttypes/token/tests/test_validators.py b/src/objecttypes/token/tests/test_validators.py index 8877f97..a042f29 100644 --- a/src/objecttypes/token/tests/test_validators.py +++ b/src/objecttypes/token/tests/test_validators.py @@ -39,7 +39,3 @@ def test_tab_character_in_between(self): def test_tab_characters_only(self): with self.assertRaises(ValidationError): validate_no_whitespace("\t\t") - - def test_blank_value(self): - with self.assertRaises(ValidationError): - validate_no_whitespace("") diff --git a/src/objecttypes/token/validators.py b/src/objecttypes/token/validators.py index 9bab299..4101ac2 100644 --- a/src/objecttypes/token/validators.py +++ b/src/objecttypes/token/validators.py @@ -8,9 +8,6 @@ def validate_no_whitespace(value: str) -> None: - if not value: - raise ValidationError(code="invalid", message=_("Blank values are not allowed")) - if WHITESPACE_PATTERN.match(value): raise ValidationError( code="all-whitespace", From f3f44d2ffd05a61148a2cfadc72eb1c9c3d845bc Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 12:09:50 +0100 Subject: [PATCH 61/63] [maykinmedia/objects-api#481] Black --- src/objecttypes/token/tests/test_authenticaton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objecttypes/token/tests/test_authenticaton.py b/src/objecttypes/token/tests/test_authenticaton.py index 1873c43..b6c3930 100644 --- a/src/objecttypes/token/tests/test_authenticaton.py +++ b/src/objecttypes/token/tests/test_authenticaton.py @@ -51,7 +51,7 @@ def test_invalid_token_existing(self): HTTP_AUTHORIZATION="Token 1234-Token-5678", ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - + def test_empty_token(self): response = self.client.get( reverse("v2:objecttype-list"), HTTP_AUTHORIZATION="Token " From 18ec10e71a4384386f7221d55ef5b6afbf435066 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 15:38:14 +0100 Subject: [PATCH 62/63] [maykinmedia/objects-api#481] Add warning if list is empty --- src/objecttypes/setup_configuration/steps/token_auth.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/objecttypes/setup_configuration/steps/token_auth.py b/src/objecttypes/setup_configuration/steps/token_auth.py index ce6a565..e9f0806 100644 --- a/src/objecttypes/setup_configuration/steps/token_auth.py +++ b/src/objecttypes/setup_configuration/steps/token_auth.py @@ -28,6 +28,9 @@ class TokenAuthConfigurationStep( config_model = TokenAuthGroupConfigurationModel def execute(self, model: TokenAuthGroupConfigurationModel) -> None: + if len(model.items) == 0: + logger.warning("No tokens provided for configuration") + for item in model.items: logger.info(f"Configuring {item.identifier}") From d6df41a229cc9e32bd906d8b7b16f553b8509ecf Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 13 Dec 2024 15:39:35 +0100 Subject: [PATCH 63/63] [maykinmedia/objects-api#481] Add validate_no_empty + tests --- ..._tokenauth_identifier_alter_tokenauth_token.py | 5 ++++- src/objecttypes/token/models.py | 4 ++-- src/objecttypes/token/tests/test_validators.py | 15 ++++++++++++++- src/objecttypes/token/validators.py | 5 +++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py index 5adbaf5..6a67bd8 100644 --- a/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py +++ b/src/objecttypes/token/migrations/0009_tokenauth_identifier_alter_tokenauth_token.py @@ -59,7 +59,10 @@ class Migration(migrations.Migration): field=models.CharField( max_length=40, unique=True, - validators=[objecttypes.token.validators.validate_no_whitespace], + validators=[ + objecttypes.token.validators.validate_no_empty, + objecttypes.token.validators.validate_no_whitespace, + ], verbose_name="token", ), ), diff --git a/src/objecttypes/token/models.py b/src/objecttypes/token/models.py index 6a3b659..c6220f3 100644 --- a/src/objecttypes/token/models.py +++ b/src/objecttypes/token/models.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from objecttypes.token.validators import validate_no_whitespace +from objecttypes.token.validators import validate_no_empty, validate_no_whitespace class TokenAuth(models.Model): @@ -16,7 +16,7 @@ class TokenAuth(models.Model): _("token"), max_length=40, unique=True, - validators=[validate_no_whitespace], + validators=[validate_no_empty, validate_no_whitespace], ) contact_person = models.CharField( diff --git a/src/objecttypes/token/tests/test_validators.py b/src/objecttypes/token/tests/test_validators.py index a042f29..c112583 100644 --- a/src/objecttypes/token/tests/test_validators.py +++ b/src/objecttypes/token/tests/test_validators.py @@ -1,7 +1,20 @@ from django.core.exceptions import ValidationError from django.test import SimpleTestCase -from objecttypes.token.validators import validate_no_whitespace +from objecttypes.token.validators import validate_no_empty, validate_no_whitespace + + +class NoEmptyValidatorTestCase(SimpleTestCase): + def test_valid(self): + self.assertIsNone(validate_no_empty("test123")) + + def test_invalid_string(self): + with self.assertRaises(ValidationError): + validate_no_empty("") + + def test_invalid_none(self): + with self.assertRaises(ValidationError): + validate_no_empty(None) class WhiteSpaceValidatorTestCase(SimpleTestCase): diff --git a/src/objecttypes/token/validators.py b/src/objecttypes/token/validators.py index 4101ac2..21d2327 100644 --- a/src/objecttypes/token/validators.py +++ b/src/objecttypes/token/validators.py @@ -13,3 +13,8 @@ def validate_no_whitespace(value: str) -> None: code="all-whitespace", message=_("Tokens cannot contain whitespace-like characters"), ) + + +def validate_no_empty(value: str) -> None: + if not value: + raise ValidationError(code="invalid", message=_("Blank values are not allowed"))