Skip to content

Commit deda664

Browse files
danielmursa-devSonnyBA
authored andcommitted
Feature/486 token permissions configuration (#497)
* parent 64fa395 author Daniel Mursa <daniel@maykinmedia.nl> 1734341658 +0100 committer Daniel Mursa <daniel@maykinmedia.nl> 1734604578 +0100 parent 64fa395 author Daniel Mursa <daniel@maykinmedia.nl> 1734341658 +0100 committer Daniel Mursa <daniel@maykinmedia.nl> 1734604572 +0100 [#485] Merge closed branch [#485] Add SitesConfigurationStep and TokenAuthConfigurationStep in settings [#485] Black and isort [#485] Fix requirements [#485] Update namespace [#486] Update TokenAuthConfigurationStep [#486] Permissions can be empty list [#486] Update tests [#486] New tests [#486] Update tests [#486] Fix old tests [#486] Update data.yaml [#486] Activate ObjectTypesConfigurationStep [#486] Add new test [#486] Update config_cli.rst [#486] Fix config_cli.rst [#486] Fix config_cli.rst [#486] Uniform data.yaml and config_cli.rst * [#486] Update PrerequisiteFailed in ConfigurationRunFailed * [#486] Fields type in permissions * [#486] Update debug message * [#486] Update permissions fields type * [#486] Fix tests * [#486] New test for field_based_authorization * [#486] Flake8
1 parent 732a52a commit deda664

File tree

7 files changed

+604
-54
lines changed

7 files changed

+604
-54
lines changed

docker/setup_configuration/data.yaml

+17-4
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,33 @@ objecttypes:
4242
name: Object Type 1
4343
service_identifier: objecttypes-api
4444

45+
- uuid: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
46+
name: Object Type 2
47+
service_identifier: objecttypes-api
48+
4549

4650
tokenauth_config_enable: true
4751
tokenauth:
4852
items:
4953
- identifier: token-1
50-
token: 18b2b74ef994314b84021d47b9422e82b685d82f
54+
token: ba9d233e95e04c4a8a661a27daffe7c9bd019067
5155
contact_person: Person 1
5256
email: person-1@example.com
5357
organization: Organization 1
5458
application: Application 1
5559
administration: Administration 1
56-
is_superuser: true
57-
58-
60+
permissions:
61+
- object_type: b427ef84-189d-43aa-9efd-7bb2c459e281
62+
mode: read_and_write
63+
- object_type: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
64+
mode: read_only
65+
use_fields: true
66+
fields:
67+
'1':
68+
- record__data__leeftijd
69+
- record__data__kiemjaar
70+
71+
5972
oidc_db_config_enable: true
6073
oidc_db_config_admin_auth:
6174
items:

docs/installation/config_cli.rst

+39-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Objecttypes configuration
3737
To configure objecttypes the following configuration could be used:
3838

3939
.. code-block:: yaml
40+
4041
...
4142
zgw_consumers_config_enable: true
4243
zgw_consumers:
@@ -68,6 +69,7 @@ To configure objecttypes the following configuration could be used:
6869
name: Object Type 2
6970
service_identifier: objecttypen-bar
7071
...
72+
7173
.. note:: The ``uuid`` field will be used to lookup existing ``ObjectType``'s.
7274

7375
Objecttypes require a corresponding ``Service`` to work correctly. Creating
@@ -81,8 +83,8 @@ In order to be able to retrieve objecttypes, a corresponding ``Service`` should
8183
created. An example of a configuration could be seen below:
8284

8385
.. code-block:: yaml
84-
...
8586
87+
...
8688
zgw_consumers_config_enable: true
8789
zgw_consumers:
8890
services:
@@ -102,7 +104,8 @@ created. An example of a configuration could be seen below:
102104
auth_type: api_key
103105
header_key: Authorization
104106
header_value: Token b9f100590925b529664ed9d370f5f8da124b2c20
105-
....
107+
...
108+
106109
107110
Tokens configuration
108111
--------------------
@@ -121,14 +124,28 @@ Create or update the (single) YAML configuration file with your settings:
121124
organization: Organization XYZ # optional
122125
application: Application XYZ # optional
123126
administration: Administration XYZ # optional
124-
is_superuser: true # optional
127+
permissions:
128+
- object_type: b427ef84-189d-43aa-9efd-7bb2c459e281
129+
mode: read_and_write
125130
126131
- identifier: token-2
127132
token: 7b2b212d9f16d171a70a1d927cdcfbd5ca7a4799
128133
contact_person: Person 2
129134
email: person-2@example.com
135+
permissions:
136+
- object_type: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
137+
mode: read_only
138+
use_fields: true
139+
fields:
140+
'1':
141+
- record__data__leeftijd
142+
- record__data__kiemjaar
130143
...
131144
145+
.. note:: To ensure the proper functioning of the tokens, it is essential to first configure the ``objecttypes``.
146+
Then, the token configuration must be completed to guarantee the correct configuration of the ``Permissions``.
147+
148+
132149
Mozilla-django-oidc-db
133150
----------------------
134151

@@ -158,16 +175,32 @@ can be found at the _`documentation`: https://mozilla-django-oidc-db.readthedocs
158175
Sites configuration
159176
-------------------
160177

178+
.. code-block:: yaml
179+
180+
...
181+
sites_config_enable: true
182+
sites_config:
183+
items:
184+
- domain: example.com
185+
name: Example site
186+
- domain: test.example.com
187+
name: Test site
188+
...
189+
190+
More details about sites configuration through ``setup_configuration``
191+
can be found at the _`site documentation`: https://github.com/maykinmedia/django-setup-configuration/blob/main/docs/sites_config.rst
192+
193+
161194
Notifications configuration
162-
-------------------------
195+
---------------------------
163196

164197
To configure sending notifications for the application ensure there is a ``services``
165198
item present that matches the ``notifications_api_service_identifier`` in the
166199
``notifications_config`` namespace:
167200

168201
.. code-block:: yaml
169-
...
170202
203+
...
171204
zgw_consumers_config_enable: true
172205
zgw_consumers:
173206
services:
@@ -184,7 +217,7 @@ item present that matches the ``notifications_api_service_identifier`` in the
184217
notification_delivery_max_retries: 1
185218
notification_delivery_retry_backoff: 2
186219
notification_delivery_retry_backoff_max: 3
187-
....
220+
...
188221
189222
190223
Execution

src/objects/conf/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,6 @@
8888
"zgw_consumers.contrib.setup_configuration.steps.ServiceConfigurationStep",
8989
"notifications_api_common.contrib.setup_configuration.steps.NotificationConfigurationStep",
9090
"mozilla_django_oidc_db.setup_configuration.steps.AdminOIDCConfigurationStep",
91+
"objects.setup_configuration.steps.objecttypes.ObjectTypesConfigurationStep",
9192
"objects.setup_configuration.steps.token_auth.TokenAuthConfigurationStep",
9293
)

src/objects/setup_configuration/models/token_auth.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1+
from django_setup_configuration.fields import DjangoModelRef
12
from django_setup_configuration.models import ConfigurationModel
3+
from pydantic import UUID4, Field
24

3-
from objects.token.models import TokenAuth
5+
from objects.token.models import Permission, TokenAuth
6+
7+
8+
class TokenAuthPermissionConfigurationModel(ConfigurationModel):
9+
object_type: UUID4
10+
fields: dict[str, list[str]] | None = DjangoModelRef(
11+
Permission, "fields", default=None
12+
)
13+
14+
class Meta:
15+
django_model_refs = {
16+
Permission: (
17+
"mode",
18+
"use_fields",
19+
),
20+
}
421

522

623
class TokenAuthConfigurationModel(ConfigurationModel):
24+
permissions: list[TokenAuthPermissionConfigurationModel] | None = Field(
25+
default_factory=list,
26+
)
27+
728
class Meta:
829
django_model_refs = {
930
TokenAuth: (
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import logging
2+
from typing import Any
23

3-
from django.core.exceptions import ValidationError
4+
from django.core.exceptions import ObjectDoesNotExist, ValidationError
45
from django.db import IntegrityError
56

67
from django_setup_configuration.configuration import BaseConfigurationStep
78
from django_setup_configuration.exceptions import ConfigurationRunFailed
89

10+
from objects.core.models import ObjectType
911
from objects.setup_configuration.models.token_auth import (
1012
TokenAuthGroupConfigurationModel,
1113
)
12-
from objects.token.models import TokenAuth
14+
from objects.token.models import Permission, TokenAuth
1315

1416
logger = logging.getLogger(__name__)
1517

@@ -18,7 +20,7 @@ class TokenAuthConfigurationStep(
1820
BaseConfigurationStep[TokenAuthGroupConfigurationModel]
1921
):
2022
"""
21-
Configure tokens for other applications to access Objects API
23+
Configure tokens with permissions for other applications to access Objects API
2224
"""
2325

2426
namespace = "tokenauth"
@@ -27,14 +29,61 @@ class TokenAuthConfigurationStep(
2729
verbose_name = "Configuration to set up authentication tokens for objects"
2830
config_model = TokenAuthGroupConfigurationModel
2931

32+
def _full_clean(self, instance: Any) -> None:
33+
try:
34+
instance.full_clean(exclude=("id",), validate_unique=False)
35+
except ValidationError as exception:
36+
raise ConfigurationRunFailed(
37+
("Validation error(s) during instance cleaning: %s" % type(instance))
38+
) from exception
39+
40+
def _configure_permissions(self, token: TokenAuth, permissions: list) -> None:
41+
if len(permissions) == 0:
42+
logger.warning("No permissions provided for %s", token.identifier)
43+
44+
for permission in permissions:
45+
try:
46+
permission_kwargs = {
47+
"token_auth": token,
48+
"object_type": ObjectType.objects.get(uuid=permission.object_type),
49+
"mode": permission.mode,
50+
"use_fields": permission.use_fields,
51+
"fields": permission.fields,
52+
}
53+
except ObjectDoesNotExist as exception:
54+
raise ConfigurationRunFailed(
55+
("Object type with %s does not exist" % permission.object_type)
56+
) from exception
57+
58+
permission_instance = Permission(**permission_kwargs)
59+
self._full_clean(permission_instance)
60+
61+
try:
62+
Permission.objects.update_or_create(
63+
token_auth=permission_kwargs["token_auth"],
64+
object_type=permission_kwargs["object_type"],
65+
defaults={
66+
"mode": permission_kwargs["mode"],
67+
"use_fields": permission_kwargs["use_fields"],
68+
"fields": permission_kwargs["fields"],
69+
},
70+
)
71+
except IntegrityError as exception:
72+
raise ConfigurationRunFailed(
73+
(
74+
"Failed configuring permission for token %s and object type %s"
75+
% (token.identifier, permission.object_type)
76+
)
77+
) from exception
78+
3079
def execute(self, model: TokenAuthGroupConfigurationModel) -> None:
3180
if len(model.items) == 0:
3281
logger.warning("No tokens provided for configuration")
3382

3483
for item in model.items:
35-
logger.info(f"Configuring {item.identifier}")
84+
logger.info("Configuring %s", item.identifier)
3685

37-
model_kwargs = {
86+
token_kwargs = {
3887
"identifier": item.identifier,
3988
"token": item.token,
4089
"contact_person": item.contact_person,
@@ -45,31 +94,24 @@ def execute(self, model: TokenAuthGroupConfigurationModel) -> None:
4594
"is_superuser": item.is_superuser,
4695
}
4796

48-
token_instance = TokenAuth(**model_kwargs)
49-
97+
token_instance = TokenAuth(**token_kwargs)
98+
self._full_clean(token_instance)
5099
try:
51-
token_instance.full_clean(exclude=("id",), validate_unique=False)
52-
except ValidationError as exception:
53-
exception_message = (
54-
f"Validation error(s) occured for {item.identifier}."
55-
)
56-
raise ConfigurationRunFailed(exception_message) from exception
57-
58-
logger.debug(f"No validation errors found for {item.identifier}")
59-
60-
try:
61-
logger.debug(f"Saving {item.identifier}")
62-
63-
TokenAuth.objects.update_or_create(
100+
logger.debug("Saving %s", item.identifier)
101+
token, _ = TokenAuth.objects.update_or_create(
64102
identifier=item.identifier,
65103
defaults={
66104
key: value
67-
for key, value in model_kwargs.items()
105+
for key, value in token_kwargs.items()
68106
if key != "identifier"
69107
},
70108
)
109+
110+
self._configure_permissions(token, item.permissions)
111+
71112
except IntegrityError as exception:
72-
exception_message = f"Failed configuring token {item.identifier}."
73-
raise ConfigurationRunFailed(exception_message) from exception
113+
raise ConfigurationRunFailed(
114+
"Failed configuring token %s" % item.identifier
115+
) from exception
74116

75-
logger.info(f"Configured {item.identifier}")
117+
logger.info("Configured %s", item.identifier)

src/objects/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml

+22-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,17 @@ tokenauth:
88
organization: Organization 1
99
application: Application 1
1010
administration: Administration 1
11-
is_superuser: True
11+
permissions:
12+
- object_type: 3a82fb7f-fc9b-4104-9804-993f639d6d0d
13+
mode: read_only
14+
use_fields: true
15+
fields:
16+
'1':
17+
- record__data__leeftijd
18+
- record__data__kiemjaar
19+
20+
- object_type: ca754b52-3f37-4c49-837c-130e8149e337
21+
mode: read_and_write
1222

1323
- identifier: token-2
1424
token: e882642bd0ec2482adcdc97258c2e6f98cb06d85
@@ -17,4 +27,15 @@ tokenauth:
1727
organization: Organization 2
1828
application: Application 2
1929
administration: Administration 2
30+
permissions:
31+
- object_type: feeaa795-d212-4fa2-bb38-2c34996e5702
32+
mode: read_only
33+
34+
- identifier: token-3
35+
token: ff835859ecf8df4d541aab09f2d0854d17b41a77
36+
contact_person: Person 3
37+
email: person-3@example.com
38+
organization: Organization 3
39+
application: Application 3
40+
administration: Administration 3
2041
is_superuser: True

0 commit comments

Comments
 (0)