commonground-api-common 1.13.0__py3-none-any.whl → 2.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. commonground_api_common-1.13.0.data/scripts/patch_content_types → commonground_api_common-2.4.1.data/scripts/generate_schema +2 -4
  2. {commonground_api_common-1.13.0.dist-info → commonground_api_common-2.4.1.dist-info}/METADATA +45 -37
  3. {commonground_api_common-1.13.0.dist-info → commonground_api_common-2.4.1.dist-info}/RECORD +45 -50
  4. {commonground_api_common-1.13.0.dist-info → commonground_api_common-2.4.1.dist-info}/WHEEL +1 -1
  5. vng_api_common/__init__.py +1 -1
  6. vng_api_common/admin.py +1 -20
  7. vng_api_common/api/views.py +1 -0
  8. vng_api_common/apps.py +44 -26
  9. vng_api_common/audittrails/utils.py +44 -0
  10. vng_api_common/authorizations/admin.py +1 -1
  11. vng_api_common/authorizations/middleware.py +244 -0
  12. vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py +76 -0
  13. vng_api_common/authorizations/models.py +62 -3
  14. vng_api_common/authorizations/utils.py +17 -0
  15. vng_api_common/client.py +61 -29
  16. vng_api_common/conf/api.py +33 -48
  17. vng_api_common/contrib/setup_configuration/models.py +32 -0
  18. vng_api_common/contrib/setup_configuration/steps.py +46 -0
  19. vng_api_common/extensions/file.py +26 -0
  20. vng_api_common/extensions/gegevensgroep.py +16 -0
  21. vng_api_common/extensions/geojson.py +270 -0
  22. vng_api_common/extensions/hyperlink.py +37 -0
  23. vng_api_common/extensions/polymorphic.py +68 -0
  24. vng_api_common/extensions/query.py +20 -0
  25. vng_api_common/filters.py +0 -1
  26. vng_api_common/generators.py +12 -113
  27. vng_api_common/middleware.py +1 -227
  28. vng_api_common/migrations/0006_delete_apicredential.py +120 -0
  29. vng_api_common/mocks.py +4 -1
  30. vng_api_common/models.py +10 -111
  31. vng_api_common/notifications/api/views.py +8 -8
  32. vng_api_common/notifications/handlers.py +8 -3
  33. vng_api_common/notifications/migrations/0011_remove_subscription_config_and_more.py +23 -0
  34. vng_api_common/oas.py +6 -10
  35. vng_api_common/pagination.py +10 -0
  36. vng_api_common/routers.py +3 -3
  37. vng_api_common/schema.py +414 -158
  38. vng_api_common/tests/schema.py +13 -0
  39. vng_api_common/utils.py +0 -22
  40. vng_api_common/validators.py +92 -73
  41. vng_api_common/views.py +35 -20
  42. commonground_api_common-1.13.0.data/scripts/generate_schema +0 -39
  43. commonground_api_common-1.13.0.data/scripts/use_external_components +0 -16
  44. vng_api_common/inspectors/cache.py +0 -57
  45. vng_api_common/inspectors/fields.py +0 -126
  46. vng_api_common/inspectors/files.py +0 -121
  47. vng_api_common/inspectors/geojson.py +0 -360
  48. vng_api_common/inspectors/polymorphic.py +0 -72
  49. vng_api_common/inspectors/query.py +0 -91
  50. vng_api_common/inspectors/utils.py +0 -40
  51. vng_api_common/inspectors/view.py +0 -547
  52. vng_api_common/management/commands/generate_autorisaties.py +0 -43
  53. vng_api_common/management/commands/generate_notificaties.py +0 -40
  54. vng_api_common/management/commands/generate_swagger.py +0 -197
  55. vng_api_common/management/commands/patch_error_contenttypes.py +0 -61
  56. vng_api_common/management/commands/use_external_components.py +0 -94
  57. vng_api_common/notifications/constants.py +0 -3
  58. vng_api_common/notifications/models.py +0 -97
  59. vng_api_common/templates/vng_api_common/api_schema_to_markdown_table.md +0 -16
  60. vng_api_common/templates/vng_api_common/autorisaties.md +0 -15
  61. vng_api_common/templates/vng_api_common/notificaties.md +0 -24
  62. {commonground_api_common-1.13.0.dist-info → commonground_api_common-2.4.1.dist-info}/top_level.txt +0 -0
  63. /vng_api_common/{inspectors → contrib}/__init__.py +0 -0
  64. /vng_api_common/{management → contrib/setup_configuration}/__init__.py +0 -0
  65. /vng_api_common/{management/commands → extensions}/__init__.py +0 -0
@@ -0,0 +1,244 @@
1
+ import logging
2
+ from typing import Any, Dict, Iterable, List, Optional
3
+
4
+ from django.conf import settings
5
+ from django.db import models, transaction
6
+ from django.db.models import QuerySet
7
+ from django.utils.translation import gettext as _
8
+
9
+ import jwt
10
+ from djangorestframework_camel_case.util import underscoreize
11
+ from requests import RequestException
12
+ from rest_framework.exceptions import PermissionDenied
13
+
14
+ from vng_api_common.client import ClientError, to_internal_data
15
+ from vng_api_common.constants import VertrouwelijkheidsAanduiding
16
+
17
+ from ..models import JWTSecret
18
+ from ..utils import get_uuid_from_path
19
+ from .models import Applicatie, AuthorizationsConfig, Autorisatie
20
+ from .serializers import ApplicatieUuidSerializer
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class JWTAuth:
26
+ def __init__(self, encoded: Optional[str] = None):
27
+ self.encoded = encoded
28
+
29
+ @property
30
+ def applicaties(self) -> Iterable[Applicatie]:
31
+ if self.client_id is None:
32
+ return []
33
+
34
+ applicaties = self._get_auth()
35
+
36
+ if not applicaties:
37
+ auth_data = self._request_auth()
38
+ applicaties = self._save_auth(auth_data)
39
+
40
+ return applicaties
41
+
42
+ @property
43
+ def autorisaties(self) -> models.QuerySet:
44
+ """
45
+ Retrieve all authorizations relevant to this component.
46
+ """
47
+ app_ids = [app.id for app in self.applicaties]
48
+ config = AuthorizationsConfig.get_solo()
49
+ return Autorisatie.objects.filter(
50
+ applicatie_id__in=app_ids, component=config.component
51
+ )
52
+
53
+ def _request_auth(self) -> list:
54
+ client = AuthorizationsConfig.get_client()
55
+
56
+ if not client:
57
+ logger.warning("Authorization component can't be accessed")
58
+ return []
59
+
60
+ try:
61
+ response = client.get("applicaties", params={"clientIds": self.client_id})
62
+
63
+ data = to_internal_data(response)
64
+ except RequestException:
65
+ logger.warning("Authorization component can't be accessed")
66
+ return []
67
+ except ClientError as exc:
68
+ response = exc.args[0]
69
+ # friendly debug - hint at where the problem is located
70
+ if response["status"] == 403 and response["code"] == "not_authenticated":
71
+ detail = _(
72
+ "Component could not authenticate against the AC - "
73
+ "authorizations could not be retrieved"
74
+ )
75
+ raise PermissionDenied(detail=detail, code="not_authenticated_for_ac")
76
+ logger.warning("Authorization component can't be accessed")
77
+ return []
78
+
79
+ return underscoreize(data["results"])
80
+
81
+ def _get_auth(self):
82
+ return Applicatie.objects.filter(client_ids__contains=[self.client_id])
83
+
84
+ @transaction.atomic
85
+ def _save_auth(self, auth_data):
86
+ applicaties = []
87
+
88
+ for applicatie_data in auth_data:
89
+ applicatie_serializer = ApplicatieUuidSerializer(data=applicatie_data)
90
+ uuid = get_uuid_from_path(applicatie_data["url"])
91
+ applicatie_data["uuid"] = uuid
92
+ applicatie_serializer.is_valid()
93
+ applicaties.append(applicatie_serializer.save())
94
+
95
+ return applicaties
96
+
97
+ @property
98
+ def payload(self) -> Optional[Dict[str, Any]]:
99
+ if self.encoded is None:
100
+ return None
101
+
102
+ if not hasattr(self, "_payload"):
103
+ # decode the JWT and validate it
104
+
105
+ # jwt check
106
+ try:
107
+ payload = jwt.decode(
108
+ self.encoded,
109
+ algorithms=["HS256"],
110
+ options={"verify_signature": False},
111
+ leeway=settings.JWT_LEEWAY,
112
+ )
113
+ except jwt.DecodeError:
114
+ logger.info("Invalid JWT encountered")
115
+ raise PermissionDenied(
116
+ _(
117
+ "JWT could not be decoded. Possibly you made a copy-paste mistake."
118
+ ),
119
+ code="jwt-decode-error",
120
+ )
121
+
122
+ # get client_id
123
+ try:
124
+ client_id = payload["client_id"]
125
+ except KeyError:
126
+ raise PermissionDenied(
127
+ "Client identifier is niet aanwezig in JWT",
128
+ code="missing-client-identifier",
129
+ )
130
+
131
+ # find client_id in DB and retrieve its secret
132
+ try:
133
+ jwt_secret = JWTSecret.objects.exclude(secret="").get(
134
+ identifier=client_id
135
+ )
136
+ except JWTSecret.DoesNotExist:
137
+ raise PermissionDenied(
138
+ "Client identifier bestaat niet", code="invalid-client-identifier"
139
+ )
140
+ else:
141
+ key = jwt_secret.secret
142
+
143
+ # check signature of the token
144
+ try:
145
+ payload = jwt.decode(
146
+ self.encoded,
147
+ key,
148
+ algorithms=["HS256"],
149
+ leeway=settings.JWT_LEEWAY,
150
+ )
151
+ except jwt.InvalidSignatureError:
152
+ logger.exception("Invalid signature - possible payload tampering?")
153
+ raise PermissionDenied(
154
+ "Client credentials zijn niet geldig", code="invalid-jwt-signature"
155
+ )
156
+
157
+ self._payload = payload
158
+
159
+ return self._payload
160
+
161
+ @property
162
+ def client_id(self) -> Optional[str]:
163
+ if not self.payload:
164
+ return None
165
+ return self.payload["client_id"]
166
+
167
+ def filter_vertrouwelijkheidaanduiding(self, base: QuerySet, value) -> QuerySet:
168
+ if value is None:
169
+ return base
170
+
171
+ order_provided = VertrouwelijkheidsAanduiding.get_choice_order(value)
172
+ order_case = VertrouwelijkheidsAanduiding.get_order_expression(
173
+ "max_vertrouwelijkheidaanduiding"
174
+ )
175
+
176
+ # In this case we are filtering Autorisatie model to look for auth which meets our needs.
177
+ # Therefore we're only considering authorizations here that have a max_vertrouwelijkheidaanduiding
178
+ # bigger or equal than what we're checking for the object.
179
+ # In cases when we are filtering data objects (Zaak, InformatieObject etc) it's the other way around
180
+
181
+ return base.annotate(max_vertr=order_case).filter(max_vertr__gte=order_provided)
182
+
183
+ def filter_default(self, base: QuerySet, name, value) -> QuerySet:
184
+ if value is None:
185
+ return base
186
+
187
+ return base.filter(**{name: value})
188
+
189
+ def has_auth(
190
+ self, scopes: List[str], component: Optional[str] = None, **fields
191
+ ) -> bool:
192
+ if scopes is None:
193
+ return False
194
+
195
+ scopes_provided = set()
196
+ config = AuthorizationsConfig.get_solo()
197
+ if component is None:
198
+ component = config.component
199
+
200
+ for applicatie in self.applicaties:
201
+ # allow everything
202
+ if applicatie.heeft_alle_autorisaties is True:
203
+ return True
204
+
205
+ autorisaties = applicatie.autorisaties.filter(component=component)
206
+
207
+ # filter on all additional components
208
+ for field_name, field_value in fields.items():
209
+ if hasattr(self, f"filter_{field_name}"):
210
+ autorisaties = getattr(self, f"filter_{field_name}")(
211
+ autorisaties, field_value
212
+ )
213
+ else:
214
+ autorisaties = self.filter_default(
215
+ autorisaties, field_name, field_value
216
+ )
217
+
218
+ for autorisatie in autorisaties:
219
+ scopes_provided.update(autorisatie.scopes)
220
+
221
+ return scopes.is_contained_in(list(scopes_provided))
222
+
223
+
224
+ class AuthMiddleware:
225
+ header = "HTTP_AUTHORIZATION"
226
+ auth_type = "Bearer"
227
+
228
+ def __init__(self, get_response=None):
229
+ self.get_response = get_response
230
+
231
+ def __call__(self, request):
232
+ self.extract_jwt_payload(request)
233
+ return self.get_response(request) if self.get_response else None
234
+
235
+ def extract_jwt_payload(self, request):
236
+ authorization = request.META.get(self.header, "")
237
+ prefix = f"{self.auth_type} "
238
+ if authorization.startswith(prefix):
239
+ # grab the actual token
240
+ encoded = authorization[len(prefix) :]
241
+ else:
242
+ encoded = None
243
+
244
+ request.jwt_auth = JWTAuth(encoded)
@@ -0,0 +1,76 @@
1
+ # Generated by Django 5.1.2 on 2024-10-25 14:07
2
+
3
+ import logging
4
+
5
+ import django.db.models.deletion
6
+ from django.db import migrations, models
7
+ from django.utils.text import slugify
8
+
9
+ from zgw_consumers.constants import APITypes, AuthTypes
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def migrate_authorization_config_to_service(apps, _) -> None:
15
+ AuthorizationsConfig = apps.get_model("authorizations", "AuthorizationsConfig")
16
+ Service = apps.get_model("zgw_consumers", "Service")
17
+
18
+ service_label = "Authorization API service"
19
+
20
+ config, _ = AuthorizationsConfig.objects.get_or_create()
21
+ service, created = Service.objects.get_or_create(
22
+ api_root=config.api_root,
23
+ defaults=dict(
24
+ label="Authorization API service",
25
+ slug=slugify(service_label),
26
+ api_type=APITypes.ac,
27
+ auth_type=AuthTypes.zgw,
28
+ ),
29
+ )
30
+ config.authorizations_api_service = service
31
+ config.save()
32
+
33
+ if created:
34
+ logger.info(f"Created new Service for {config.api_root}")
35
+ else:
36
+ logger.info(f"Existing service found for {config.api_root}")
37
+
38
+
39
+ def migrate_authorization_config_to_config(apps, _) -> None:
40
+ AuthorizationsConfig = apps.get_model("authorizations", "AuthorizationsConfig")
41
+
42
+ config, _ = AuthorizationsConfig.objects.get_or_create()
43
+ if config.authorizations_api_service:
44
+ config.api_root = config.authorizations_api_service.api_root
45
+ config.save()
46
+
47
+
48
+ class Migration(migrations.Migration):
49
+
50
+ dependencies = [
51
+ ("authorizations", "0015_auto_20220318_1608"),
52
+ ("zgw_consumers", "0022_set_default_service_slug"),
53
+ ]
54
+
55
+ operations = [
56
+ migrations.AddField(
57
+ model_name="authorizationsconfig",
58
+ name="authorizations_api_service",
59
+ field=models.ForeignKey(
60
+ blank=True,
61
+ limit_choices_to={"api_type": "ac", "auth_type": "zgw"},
62
+ null=True,
63
+ on_delete=django.db.models.deletion.SET_NULL,
64
+ to="zgw_consumers.service",
65
+ verbose_name="autorisations api service",
66
+ ),
67
+ ),
68
+ migrations.RunPython(
69
+ migrate_authorization_config_to_service,
70
+ reverse_code=migrate_authorization_config_to_config,
71
+ ),
72
+ migrations.RemoveField(
73
+ model_name="authorizationsconfig",
74
+ name="api_root",
75
+ ),
76
+ ]
@@ -1,17 +1,28 @@
1
1
  import uuid
2
+ from typing import Optional
2
3
 
3
4
  from django.contrib.postgres.fields import ArrayField
4
5
  from django.db import models
5
6
  from django.utils.translation import gettext_lazy as _
6
7
 
8
+ from solo.models import SingletonModel
9
+ from zgw_consumers.constants import APITypes, AuthTypes
10
+
11
+ from vng_api_common.client import Client, get_client
12
+
7
13
  from ..constants import ComponentTypes, VertrouwelijkheidsAanduiding
8
14
  from ..decorators import field_default
9
15
  from ..fields import VertrouwelijkheidsAanduidingField
10
- from ..models import APIMixin, ClientConfig
16
+ from ..models import APIMixin
17
+
11
18
 
19
+ class AuthorizationsConfigManager(models.Manager):
20
+ def get_queryset(self):
21
+ qs = super().get_queryset()
22
+ return qs.select_related("authorizations_api_service")
12
23
 
13
- @field_default("api_root", "https://autorisaties-api.vng.cloud/api/v1")
14
- class AuthorizationsConfig(ClientConfig):
24
+
25
+ class AuthorizationsConfig(SingletonModel):
15
26
  component = models.CharField(
16
27
  _("component"),
17
28
  max_length=50,
@@ -19,9 +30,38 @@ class AuthorizationsConfig(ClientConfig):
19
30
  default=ComponentTypes.zrc,
20
31
  )
21
32
 
33
+ authorizations_api_service = models.ForeignKey(
34
+ "zgw_consumers.Service",
35
+ limit_choices_to=dict(
36
+ api_type=APITypes.ac,
37
+ auth_type=AuthTypes.zgw,
38
+ ),
39
+ verbose_name=_("autorisations api service"),
40
+ on_delete=models.SET_NULL,
41
+ blank=True,
42
+ null=True,
43
+ )
44
+
45
+ objects = AuthorizationsConfigManager()
46
+
22
47
  class Meta:
23
48
  verbose_name = _("Autorisatiecomponentconfiguratie")
24
49
 
50
+ @classmethod
51
+ def get_client(cls) -> Optional[Client]:
52
+ """
53
+ Construct a client, prepared with the required auth.
54
+ """
55
+ config = cls.get_solo()
56
+ if config.authorizations_api_service:
57
+ return get_client(config.authorizations_api_service.api_root)
58
+ return None
59
+
60
+
61
+ class ApplicatieManager(models.Manager):
62
+ def get_by_natural_key(self, uuid):
63
+ return self.get(uuid=uuid)
64
+
25
65
 
26
66
  class Applicatie(APIMixin, models.Model):
27
67
  """
@@ -52,10 +92,20 @@ class Applicatie(APIMixin, models.Model):
52
92
  ),
53
93
  )
54
94
 
95
+ objects = ApplicatieManager()
96
+
97
+ def natural_key(self):
98
+ return (str(self.uuid),)
99
+
55
100
  def __str__(self):
56
101
  return f"Applicatie ({self.label})"
57
102
 
58
103
 
104
+ class AutorisatieManager(models.Manager):
105
+ def get_by_natural_key(self, applicatie, component, scopes):
106
+ return self.get(applicatie=applicatie, component=component, scopes=scopes)
107
+
108
+
59
109
  class Autorisatie(APIMixin, models.Model):
60
110
  applicatie = models.ForeignKey(
61
111
  "Applicatie",
@@ -109,6 +159,15 @@ class Autorisatie(APIMixin, models.Model):
109
159
  blank=True,
110
160
  )
111
161
 
162
+ objects = AutorisatieManager()
163
+
164
+ def natural_key(self):
165
+ return (
166
+ self.applicatie,
167
+ self.component,
168
+ self.scopes,
169
+ )
170
+
112
171
  def satisfy_vertrouwelijkheid(self, vertrouwelijkheidaanduiding: str) -> bool:
113
172
  max_confid_level = VertrouwelijkheidsAanduiding.get_choice(
114
173
  self.max_vertrouwelijkheidaanduiding
@@ -0,0 +1,17 @@
1
+ def generate_jwt(client_id, secret, user_id, user_representation):
2
+ from zgw_consumers.client import ZGWAuth
3
+
4
+ class FakeService:
5
+ def __init__(self, **kwargs):
6
+ for key, value in kwargs.items():
7
+ setattr(self, key, value)
8
+
9
+ auth = ZGWAuth(
10
+ service=FakeService( # type: ignore
11
+ client_id=client_id,
12
+ secret=secret,
13
+ user_id=user_id,
14
+ user_representation=user_representation,
15
+ )
16
+ )
17
+ return f"Bearer {auth._token}"
vng_api_common/client.py CHANGED
@@ -1,44 +1,76 @@
1
1
  """
2
- Interface to get a zds_client object for a given URL.
2
+ Interface to get a client object for a given URL.
3
3
  """
4
4
 
5
- from typing import Optional
5
+ import logging
6
6
 
7
- from django.apps import apps
8
- from django.conf import settings
9
- from django.utils.module_loading import import_string
7
+ from ape_pie import APIClient
8
+ from requests import JSONDecodeError, RequestException, Response
10
9
 
11
- from zds_client import Client
10
+ logger = logging.getLogger(__name__)
12
11
 
13
12
 
14
- def get_client(url: str, url_is_api_root=False) -> Optional[Client]:
15
- """
16
- Get a client instance for the given URL.
13
+ class ClientError(RuntimeError):
14
+ pass
17
15
 
18
- If the setting CUSTOM_CLIENT_FETCHER is defined, then this callable is invoked.
19
- Otherwise we fall back on the default implementation.
20
16
 
21
- If no suitable client is found, ``None`` is returned.
22
- """
23
- custom_client_fetcher = getattr(settings, "CUSTOM_CLIENT_FETCHER", None)
24
- if custom_client_fetcher:
25
- client_getter = import_string(custom_client_fetcher)
26
- return client_getter(url)
17
+ class NoServiceConfigured(RuntimeError):
18
+ pass
19
+
20
+
21
+ # TODO: use more approriate method name
22
+ def to_internal_data(response: Response) -> dict | list | None:
23
+ try:
24
+ response_json = response.json()
25
+ except JSONDecodeError:
26
+ logger.exception("Unable to parse json from response")
27
+ response_json = None
28
+
29
+ try:
30
+ response.raise_for_status()
31
+ except RequestException as exc:
32
+ if response.status_code >= 500:
33
+ raise
34
+ raise ClientError(response_json if response_json is not None else {}) from exc
35
+
36
+ return response_json
37
+
27
38
 
28
- # default implementation
29
- Client = import_string(settings.ZDS_CLIENT_CLASS)
39
+ class Client(APIClient):
40
+ def request(
41
+ self, method: str | bytes, url: str | bytes, *args, **kwargs
42
+ ) -> Response:
30
43
 
31
- if url_is_api_root and not url.endswith("/"):
32
- url = f"{url}/"
44
+ headers = kwargs.pop("headers", {})
45
+ headers.setdefault("Accept", "application/json")
46
+ headers.setdefault("Content-Type", "application/json")
33
47
 
34
- client = Client.from_url(url)
35
- if client is None:
36
- return None
48
+ kwargs["headers"] = headers
49
+
50
+ data = kwargs.get("data", {})
51
+
52
+ if data:
53
+ kwargs["json"] = data
54
+
55
+ return super().request(method, url, *args, **kwargs)
56
+
57
+
58
+ def get_client(
59
+ url: str, raise_exceptions: bool = False, **client_kwargs
60
+ ) -> Client | None:
61
+ """
62
+ Get a client instance for the given URL.
63
+ If no suitable client is found, ``None`` is returned.
64
+ """
65
+ from zgw_consumers.client import build_client
66
+ from zgw_consumers.models import Service
37
67
 
38
- APICredential = apps.get_model("vng_api_common", "APICredential")
68
+ service: Service | None = Service.get_service(url)
39
69
 
40
- if url_is_api_root:
41
- client.base_url = url
70
+ if not service:
71
+ logger.warning(f"No service configured for {url}")
72
+ if raise_exceptions:
73
+ raise NoServiceConfigured(f"{url} API should be added to Service model")
74
+ return
42
75
 
43
- client.auth = APICredential.get_auth(url)
44
- return client
76
+ return build_client(service, client_factory=Client, **client_kwargs)
@@ -1,10 +1,9 @@
1
1
  __all__ = [
2
2
  "API_VERSION",
3
3
  "BASE_REST_FRAMEWORK",
4
- "BASE_SWAGGER_SETTINGS",
4
+ "BASE_SPECTACULAR_SETTINGS",
5
5
  "COMMON_SPEC",
6
6
  "LINK_FETCHER",
7
- "ZDS_CLIENT_CLASS",
8
7
  "GEMMA_URL_TEMPLATE",
9
8
  "GEMMA_URL_COMPONENTTYPE",
10
9
  "GEMMA_URL_INFORMATIEMODEL",
@@ -13,43 +12,30 @@ __all__ = [
13
12
  "NOTIFICATIONS_KANAAL",
14
13
  "NOTIFICATIONS_DISABLED",
15
14
  "JWT_LEEWAY",
15
+ "SECURITY_DEFINITION_NAME",
16
16
  "COMMONGROUND_API_COMMON_GET_DOMAIN",
17
+ "JWT_SPECTACULAR_SETTINGS",
17
18
  ]
18
19
 
19
20
  API_VERSION = "1.0.0-rc1" # semantic version
20
21
 
22
+ SECURITY_DEFINITION_NAME = "JWT-Claims"
23
+
21
24
  BASE_REST_FRAMEWORK = {
25
+ "DEFAULT_SCHEMA_CLASS": "vng_api_common.schema.AutoSchema",
22
26
  "DEFAULT_RENDERER_CLASSES": (
23
27
  "djangorestframework_camel_case.render.CamelCaseJSONRenderer",
24
28
  ),
25
29
  "DEFAULT_PARSER_CLASSES": (
26
30
  "djangorestframework_camel_case.parser.CamelCaseJSONParser",
27
31
  ),
28
- "DEFAULT_AUTHENTICATION_CLASSES": (
29
- # 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
30
- # 'rest_framework.authentication.SessionAuthentication',
31
- # 'rest_framework.authentication.BasicAuthentication'
32
- ),
33
32
  # there is no authentication of 'end-users', only authorization (via JWT)
34
33
  # of applications
35
34
  "DEFAULT_AUTHENTICATION_CLASSES": (),
36
- # 'DEFAULT_PERMISSION_CLASSES': (
37
- # 'oauth2_provider.contrib.rest_framework.TokenHasReadWriteScope',
38
- # # 'rest_framework.permissions.IsAuthenticated',
39
- # # 'rest_framework.permissions.AllowAny',
40
- # ),
41
35
  "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
42
- #
43
- # # Generic view behavior
44
- # 'DEFAULT_PAGINATION_CLASS': 'ztc.api.utils.pagination.HALPagination',
45
- "DEFAULT_FILTER_BACKENDS": (
46
- "vng_api_common.filters.Backend",
47
- # 'rest_framework.filters.SearchFilter',
48
- # 'rest_framework.filters.OrderingFilter',
49
- ),
36
+ "DEFAULT_FILTER_BACKENDS": ("vng_api_common.filters.Backend",),
50
37
  #
51
38
  # # Filtering
52
- # 'SEARCH_PARAM': 'zoek', # 'search',
53
39
  "ORDERING_PARAM": "ordering", # 'ordering',
54
40
  #
55
41
  # Versioning
@@ -61,41 +47,40 @@ BASE_REST_FRAMEWORK = {
61
47
  "EXCEPTION_HANDLER": "vng_api_common.views.exception_handler",
62
48
  "TEST_REQUEST_DEFAULT_FORMAT": "json",
63
49
  }
64
-
65
- BASE_SWAGGER_SETTINGS = {
50
+ BASE_SPECTACULAR_SETTINGS = {
66
51
  "DEFAULT_GENERATOR_CLASS": "vng_api_common.generators.OpenAPISchemaGenerator",
67
- "DEFAULT_AUTO_SCHEMA_CLASS": "vng_api_common.inspectors.view.AutoSchema",
68
- "DEFAULT_INFO": "must.be.overridden",
69
- "DEFAULT_FIELD_INSPECTORS": (
70
- # GeometryFieldInspector has external dependencies, and is opt-in
71
- # 'vng_api_common.inspectors.geojson.GeometryFieldInspector',
72
- "vng_api_common.inspectors.fields.HyperlinkedIdentityFieldInspector",
73
- "vng_api_common.inspectors.fields.ReadOnlyFieldInspector",
74
- "vng_api_common.inspectors.polymorphic.PolymorphicSerializerInspector",
75
- "vng_api_common.inspectors.fields.GegevensGroepInspector",
76
- "drf_yasg.inspectors.CamelCaseJSONFilter",
77
- "drf_yasg.inspectors.RecursiveFieldInspector",
78
- "drf_yasg.inspectors.ReferencingSerializerInspector",
79
- "drf_yasg.inspectors.ChoiceFieldInspector",
80
- "drf_yasg.inspectors.FileFieldInspector",
81
- "drf_yasg.inspectors.DictFieldInspector",
82
- "drf_yasg.inspectors.JSONFieldInspector",
83
- "drf_yasg.inspectors.HiddenFieldInspector",
84
- "drf_yasg.inspectors.RelatedFieldInspector",
85
- "drf_yasg.inspectors.SerializerMethodFieldInspector",
86
- "drf_yasg.inspectors.SimpleFieldInspector",
87
- "drf_yasg.inspectors.StringDefaultFieldInspector",
88
- ),
89
- "DEFAULT_FILTER_INSPECTORS": ("vng_api_common.inspectors.query.FilterInspector",),
52
+ "SERVE_INCLUDE_SCHEMA": False,
53
+ "POSTPROCESSING_HOOKS": [
54
+ "drf_spectacular.hooks.postprocess_schema_enums",
55
+ "drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields",
56
+ ],
57
+ "SCHEMA_PATH_PREFIX": "/api/v1",
58
+ }
59
+
60
+ # add to SPECTACULAR_SETTINGS if you are using the AuthMiddleware
61
+ JWT_SPECTACULAR_SETTINGS = {
62
+ "APPEND_COMPONENTS": {
63
+ "securitySchemes": {
64
+ SECURITY_DEFINITION_NAME: {
65
+ "type": "http",
66
+ "bearerFormat": "JWT",
67
+ "scheme": "bearer",
68
+ }
69
+ },
70
+ },
71
+ "SECURITY": [
72
+ {
73
+ SECURITY_DEFINITION_NAME: [],
74
+ }
75
+ ],
90
76
  }
91
77
 
78
+
92
79
  REDOC_SETTINGS = {"EXPAND_RESPONSES": "200,201", "SPEC_URL": "openapi.json"}
93
80
 
94
81
  # See: https://github.com/Rebilly/ReDoc#redoc-options-object
95
82
  LINK_FETCHER = "requests.get"
96
83
 
97
- ZDS_CLIENT_CLASS = "zds_client.Client"
98
-
99
84
  GEMMA_URL_TEMPLATE = "https://www.gemmaonline.nl/index.php/{informatiemodel}_{versie}/doc/{componenttype}/{component}"
100
85
  GEMMA_URL_COMPONENTTYPE = "objecttype"
101
86
  GEMMA_URL_INFORMATIEMODEL = "Rgbz"