Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/485 tokenauth setup config #493

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
15b00a6
[#467] update requirements
SonnyBA Dec 6, 2024
9380291
[#467] update objecttypes step
SonnyBA Dec 6, 2024
4c605ec
[#467] add tests
SonnyBA Dec 6, 2024
38da55d
[#467] update documentation
SonnyBA Dec 6, 2024
19a8008
[#467] add missing optional requirement for zgw-consumers
SonnyBA Dec 6, 2024
89f8d60
[#467] remove previous test suite
SonnyBA Dec 6, 2024
8423892
[#467] update docker compose setup & remove previous setup configurat…
SonnyBA Dec 6, 2024
6fb8d83
[#467] update setup configuration project structure
SonnyBA Dec 6, 2024
09e092a
[#467] remove test override
SonnyBA Dec 6, 2024
8f60c30
[#467] update django-setup-configuration site step
SonnyBA Dec 10, 2024
f91b37d
[#467] remove demo user documentation
SonnyBA Dec 10, 2024
43964bd
[#467] apply code formatting
SonnyBA Dec 10, 2024
3461fca
[#467] include service configuration documentation
SonnyBA Dec 10, 2024
c5cd151
[#468] Add identifier in TokenAuth and create migrations
danielmursa-dev Dec 11, 2024
4a256d9
[#467] Update objecttypes configuration step
SonnyBA Dec 11, 2024
fc99137
[#467] update config namespace
SonnyBA Dec 11, 2024
951e53f
[#467] update sites config namespace
SonnyBA Dec 11, 2024
9615bcf
[#467] remove `Field` call
SonnyBA Dec 11, 2024
9cda2c1
[#468] Enable TokenAuthConfiguration
danielmursa-dev Dec 11, 2024
c00257d
Merge branch 'feature/467-objecttypes-setup-config' into feature/486-…
danielmursa-dev Dec 11, 2024
045088e
[#467] update invalid connection test
SonnyBA Dec 11, 2024
433867d
[#467] Prefix sites configuration namespace with project name
SonnyBA Dec 11, 2024
95bd575
Revert "[#467] Prefix sites configuration namespace with project name"
SonnyBA Dec 11, 2024
7f4fd36
[#467] remove typo in documentation
SonnyBA Dec 11, 2024
d4b1de1
[#467] update docker-compose example configuration
SonnyBA Dec 11, 2024
4f1767d
[#467] remove unused configuration step
SonnyBA Dec 11, 2024
709b5c1
[#468] Change namespaces for TokenAuthConfiguration
danielmursa-dev Dec 11, 2024
3f53b77
Merge branch 'feature/467-objecttypes-setup-config' into feature/486-…
danielmursa-dev Dec 11, 2024
e871559
[#468] Add tests TokenAuthConfiguration
danielmursa-dev Dec 11, 2024
5536da3
[#468] Add migrations test
danielmursa-dev Dec 11, 2024
971c775
[#468] Update config_cli.rst
danielmursa-dev Dec 11, 2024
d7a2dd7
[#468] Improvements
danielmursa-dev Dec 11, 2024
3903e57
[#468] Fix tests
danielmursa-dev Dec 12, 2024
23f5756
[#467] use zgw-consumers step to configure objecttypes connection
Dec 12, 2024
7c3bf67
Merge branch 'feature/467-objecttypes-setup-config' into feature/486-…
danielmursa-dev Dec 13, 2024
6af7c09
Merge branch 'master' into feature/485-tokenauth-setup-config
danielmursa-dev Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docker/setup_configuration/data.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
token_tokenauth_config_enable: true
token_tokenauth:
items:
- identifier: token-1
token: 18b2b74ef994314b84021d47b9422e82b685d82f
contact_person: Person 1
email: person-1@example.com
organization: Organization 1
application: Application 1
administration: Administration 1

sites_config_enable: true
sites_config:
items:
- domain: example.com
name: Example site

zgw_consumers_config_enable: true
zgw_consumers:
services:
Expand Down
1 change: 1 addition & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ jsonschema
furl

# Common ground libraries
django-setup-configuration>=0.4.0
notifications-api-common[setup-configuration]
zgw-consumers[setup-configuration]
mozilla-django-oidc-db[setup-configuration]
17 changes: 17 additions & 0 deletions src/objects/setup_configuration/models/sites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.contrib.sites.models import Site

from django_setup_configuration.models import ConfigurationModel


class SiteConfigurationModel(ConfigurationModel):
class Meta:
django_model_refs = {
Site: (
"domain",
"name",
)
}


class SitesConfigurationModel(ConfigurationModel):
items: list[SiteConfigurationModel]
24 changes: 24 additions & 0 deletions src/objects/setup_configuration/models/token_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django_setup_configuration.models import ConfigurationModel
from pydantic import Field

from objects.token.models import TokenAuth


class TokenAuthConfigurationModel(ConfigurationModel):
class Meta:
django_model_refs = {
TokenAuth: (
"identifier",
"token",
"contact_person",
"email",
"organization",
"application",
"administration",
"is_superuser",
)
}


class TokenAuthGroupConfigurationModel(ConfigurationModel):
items: list[TokenAuthConfigurationModel]
37 changes: 37 additions & 0 deletions src/objects/setup_configuration/steps/sites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 objects.setup_configuration.models.sites import SitesConfigurationModel


class SitesConfigurationStep(BaseConfigurationStep):
config_model = SitesConfigurationModel
verbose_name = "Sites configuration"

namespace = "sites_config"
enable_setting = "sites_config_enable"

def execute(self, model: SitesConfigurationModel) -> None:
for item in model.items:
site_kwargs = dict(domain=item.domain, name=item.name)
site_instance = Site(**site_kwargs)

try:
site_instance.full_clean(exclude=("id",), validate_unique=False)
except ValidationError as exception:
exception_message = (
f"Validation error(s) occured for site {item.domain}."
)
raise ConfigurationRunFailed(exception_message) from exception

try:
Site.objects.update_or_create(
domain=item.domain, defaults=dict(name=item.name)
)
except IntegrityError as exception:
exception_message = f"Failed configuring site {item.domain}."
raise ConfigurationRunFailed(exception_message) from exception
72 changes: 72 additions & 0 deletions src/objects/setup_configuration/steps/token_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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 objects.setup_configuration.models.token_auth import (
TokenAuthGroupConfigurationModel,
)
from objects.token.models import TokenAuth

logger = logging.getLogger(__name__)


class TokenAuthConfigurationStep(
BaseConfigurationStep[TokenAuthGroupConfigurationModel]
):
"""
Configure tokens for other applications to access Objects API
"""

namespace = "token_tokenauth"
enable_setting = "token_tokenauth_config_enable"

verbose_name = "Configuration to set up authentication tokens for objects"
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,
"is_superuser": item.is_superuser,
}

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}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sites_config_enable: true
sites_config:
items:
- domain: example.com
name: Example site

- domain: alternative.example.com
name: Alternative example site
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sites_config_enable: true
sites_config:
items:
- domain: example.com
name: Example site (revised)

- domain: test.example.com
name: Test site
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sites_config_enable: true
sites_config:
items:
- domain: example.com
name: Example site

- domain: alternative.example.com
name: Alternative example site
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sites_config_enable: true
sites_config:
items:
- domain: example.com
name: Example site

- domain: foobar whitespace.com
name: Invalid site
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
token_tokenauth_config_enable: true
token_tokenauth:
items:
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
token_tokenauth_config_enable: true
token_tokenauth:
items:
- identifier: token-1
token: 18b2b74ef994314b84021d47b9422e82b685d82f
contact_person: Person 1
email: person-1@example.com
organization: Organization 1
application: Application 1
administration: Administration 1
is_superuser: True

- identifier: token-2
token: e882642bd0ec2482adcdc97258c2e6f98cb06d85
contact_person: Person 2
email: person-2@example.com
organization: Organization 2
application: Application 2
administration: Administration 2
is_superuser: True
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
token_tokenauth_config_enable: true
token_tokenauth:
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
100 changes: 100 additions & 0 deletions src/objects/setup_configuration/tests/test_site_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from pathlib import Path

from django.contrib.sites.models import Site
from django.db.models import QuerySet
from django.test import TestCase

from django_setup_configuration.exceptions import ConfigurationRunFailed
from django_setup_configuration.test_utils import execute_single_step

from objects.setup_configuration.steps.sites import SitesConfigurationStep

TEST_FILES = (Path(__file__).parent / "files").resolve()


class SitesConfigurationStepTests(TestCase):
def test_empty_database(self):
test_file_path = str(TEST_FILES / "sites_empty_database.yaml")

execute_single_step(SitesConfigurationStep, yaml_source=test_file_path)

sites: QuerySet[Site] = Site.objects.all()

self.assertEqual(sites.count(), 2)

example_site: Site = sites.get(name="Example site")
self.assertEqual(example_site.domain, "example.com")

alternative_site: Site = sites.get(name="Alternative example site")
self.assertEqual(alternative_site.domain, "alternative.example.com")

def test_existing_sites(self):
test_file_path = str(TEST_FILES / "sites_existing_sites.yaml")

example_site, _ = Site.objects.get_or_create(
domain="example.com", defaults=dict(name="Example site")
)

alternative_site = Site.objects.create(
domain="alternative.example.com",
name="Alternative example site",
)

execute_single_step(SitesConfigurationStep, yaml_source=test_file_path)

sites: QuerySet[Site] = Site.objects.order_by("name")

self.assertEqual(sites.count(), 3)

example_site: Site = sites.get(name="Example site (revised)")
self.assertEqual(example_site.domain, "example.com")

alternative_site: Site = sites.get(name="Alternative example site")
self.assertEqual(alternative_site.domain, "alternative.example.com")

test_site: Site = sites.get(name="Test site")
self.assertEqual(test_site.domain, "test.example.com")

def test_invalid_domain(self):
test_file_path = str(TEST_FILES / "sites_invalid_domain.yaml")

with self.assertRaises(ConfigurationRunFailed):
execute_single_step(SitesConfigurationStep, yaml_source=test_file_path)

sites: QuerySet[Site] = Site.objects.all()

# the default test site created during test runs
self.assertEqual(sites.count(), 1)

site: Site = sites.get()

self.assertEqual(site.domain, "example.com")

def test_idempotent_step(self):
test_file_path = str(TEST_FILES / "sites_idempotent_step.yaml")

execute_single_step(SitesConfigurationStep, yaml_source=test_file_path)

sites: QuerySet[Site] = Site.objects.all()

self.assertEqual(sites.count(), 2)

example_site: Site = sites.get(name="Example site")
self.assertEqual(example_site.domain, "example.com")

alternative_site: Site = sites.get(name="Alternative example site")
self.assertEqual(alternative_site.domain, "alternative.example.com")

execute_single_step(SitesConfigurationStep, yaml_source=test_file_path)

self.assertEqual(Site.objects.count(), 2)

example_site.refresh_from_db()

self.assertEqual(example_site.name, "Example site")
self.assertEqual(example_site.domain, "example.com")

alternative_site.refresh_from_db()

self.assertEqual(alternative_site.name, "Alternative example site")
self.assertEqual(alternative_site.domain, "alternative.example.com")
Loading
Loading