commonground-api-common 1.13.3__py3-none-any.whl → 2.0.0__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 (32) hide show
  1. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/METADATA +22 -23
  2. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/RECORD +30 -27
  3. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/WHEEL +1 -1
  4. vng_api_common/__init__.py +1 -1
  5. vng_api_common/admin.py +1 -20
  6. vng_api_common/authorizations/admin.py +1 -1
  7. vng_api_common/authorizations/middleware.py +244 -0
  8. vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py +76 -0
  9. vng_api_common/authorizations/models.py +38 -3
  10. vng_api_common/authorizations/utils.py +17 -0
  11. vng_api_common/client.py +56 -27
  12. vng_api_common/conf/api.py +0 -3
  13. vng_api_common/management/commands/generate_swagger.py +1 -1
  14. vng_api_common/middleware.py +1 -227
  15. vng_api_common/migrations/0006_delete_apicredential.py +119 -0
  16. vng_api_common/mocks.py +4 -1
  17. vng_api_common/models.py +0 -111
  18. vng_api_common/notifications/api/views.py +1 -1
  19. vng_api_common/notifications/handlers.py +8 -3
  20. vng_api_common/notifications/migrations/0011_remove_subscription_config_and_more.py +23 -0
  21. vng_api_common/oas.py +0 -3
  22. vng_api_common/routers.py +3 -3
  23. vng_api_common/tests/schema.py +12 -0
  24. vng_api_common/utils.py +0 -22
  25. vng_api_common/validators.py +0 -38
  26. vng_api_common/views.py +26 -22
  27. vng_api_common/notifications/constants.py +0 -3
  28. vng_api_common/notifications/models.py +0 -97
  29. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/generate_schema +0 -0
  30. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/patch_content_types +0 -0
  31. {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/use_external_components +0 -0
  32. {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: commonground-api-common
3
- Version: 1.13.3
3
+ Version: 2.0.0
4
4
  Summary: Commonground API tooling
5
5
  Home-page: https://github.com/maykinmedia/commonground-api-common
6
6
  Author: Maykin Media, VNG-Realisatie
@@ -9,7 +9,6 @@ License: EUPL 1.2
9
9
  Keywords: openapi,swagger,django
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Framework :: Django
12
- Classifier: Framework :: Django :: 3.2
13
12
  Classifier: Framework :: Django :: 4.2
14
13
  Classifier: Intended Audience :: Developers
15
14
  Classifier: Operating System :: Unix
@@ -20,35 +19,27 @@ Classifier: Programming Language :: Python :: 3 :: Only
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Requires-Dist: django>=3.2.0
24
- Requires-Dist: django-filter>=2.0
22
+ Requires-Dist: django>=4.2.0
23
+ Requires-Dist: django-filter<=25.1,>=23.1
25
24
  Requires-Dist: django-solo
26
- Requires-Dist: djangorestframework>=3.11.0
27
- Requires-Dist: djangorestframework-camel-case>=1.2.0
25
+ Requires-Dist: djangorestframework>=3.15.0
26
+ Requires-Dist: djangorestframework_camel_case>=1.2.0
28
27
  Requires-Dist: django-rest-framework-condition
29
28
  Requires-Dist: drf-yasg>=1.20.0
30
- Requires-Dist: drf-nested-routers>=0.93.3
31
- Requires-Dist: gemma-zds-client>=0.14.0
29
+ Requires-Dist: drf-nested-routers>=0.94.1
32
30
  Requires-Dist: iso-639
33
31
  Requires-Dist: isodate
34
- Requires-Dist: notifications-api-common>=0.2.2
32
+ Requires-Dist: notifications-api-common>=0.3.0
33
+ Requires-Dist: zgw-consumers>=0.35.1
35
34
  Requires-Dist: oyaml
36
35
  Requires-Dist: PyJWT>=2.0.0
37
36
  Requires-Dist: requests
37
+ Requires-Dist: requests-mock
38
38
  Requires-Dist: coreapi
39
- Provides-Extra: coverage
40
- Requires-Dist: pytest-cov; extra == "coverage"
41
- Provides-Extra: docs
42
- Requires-Dist: psycopg2; extra == "docs"
43
- Requires-Dist: sphinx; extra == "docs"
44
- Requires-Dist: sphinx-rtd-theme; extra == "docs"
45
- Provides-Extra: markdown_docs
39
+ Requires-Dist: ape-pie
40
+ Provides-Extra: markdown-docs
46
41
  Requires-Dist: django-markup<=1.3; extra == "markdown-docs"
47
42
  Requires-Dist: markdown; extra == "markdown-docs"
48
- Provides-Extra: pep8
49
- Requires-Dist: flake8; extra == "pep8"
50
- Provides-Extra: release
51
- Requires-Dist: bump2version; extra == "release"
52
43
  Provides-Extra: tests
53
44
  Requires-Dist: psycopg2; extra == "tests"
54
45
  Requires-Dist: pytest; extra == "tests"
@@ -59,6 +50,17 @@ Requires-Dist: isort; extra == "tests"
59
50
  Requires-Dist: black; extra == "tests"
60
51
  Requires-Dist: requests-mock; extra == "tests"
61
52
  Requires-Dist: freezegun; extra == "tests"
53
+ Requires-Dist: zgw-consumers-oas; extra == "tests"
54
+ Provides-Extra: pep8
55
+ Requires-Dist: flake8; extra == "pep8"
56
+ Provides-Extra: coverage
57
+ Requires-Dist: pytest-cov; extra == "coverage"
58
+ Provides-Extra: docs
59
+ Requires-Dist: psycopg2; extra == "docs"
60
+ Requires-Dist: sphinx; extra == "docs"
61
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
62
+ Provides-Extra: release
63
+ Requires-Dist: bump2version; extra == "release"
62
64
 
63
65
  ===================================================
64
66
  Commonground-API-common - Tooling voor RESTful APIs
@@ -97,9 +99,6 @@ Features
97
99
  * ``UniekeIdentificatieValidator`` (in combinatie met organisatie)
98
100
  * ``InformatieObjectUniqueValidator`` om te valideren dat M2M entries
99
101
  slechts eenmalig voorkomen
100
- * ``ObjectInformatieObjectValidator`` om te valideren dat de synchronisatie
101
- van een object-informatieobject relatie pas kan nadat deze relatie in het
102
- DRC gemaakt is
103
102
  * ``IsImmutableValidator`` - valideer dat bepaalde velden niet gewijzigd
104
103
  worden bij een (partial) update, maar wel mogen gezet worden bij een create
105
104
  * ``ResourceValidator`` - valideer dat een URL een bepaalde resource ontsluit
@@ -1,12 +1,12 @@
1
- commonground_api_common-1.13.3.data/scripts/generate_schema,sha256=UKhznmbHX1zUjPx8G3XtxUPQiqnyd3TOIXs27Tnjl7U,952
2
- commonground_api_common-1.13.3.data/scripts/patch_content_types,sha256=dpGpYrZOZe8O5CHWd0F0QnP6Wk_7lK6DyuVZpBPr4mY,319
3
- commonground_api_common-1.13.3.data/scripts/use_external_components,sha256=xvvbngO2aDUagVXv4xRUqPaVtH_eOaVMWLQ8lyAPhEA,369
4
- vng_api_common/__init__.py,sha256=KEnQD-7zBoKmB5YS3RYdSTAm0jGS_-WENjL881LrJOY,23
5
- vng_api_common/admin.py,sha256=oWKpUl5yjeYOIsh60ruCmBDyPURGq4ALle4zGhlUQsE,902
1
+ commonground_api_common-2.0.0.data/scripts/generate_schema,sha256=UKhznmbHX1zUjPx8G3XtxUPQiqnyd3TOIXs27Tnjl7U,952
2
+ commonground_api_common-2.0.0.data/scripts/patch_content_types,sha256=dpGpYrZOZe8O5CHWd0F0QnP6Wk_7lK6DyuVZpBPr4mY,319
3
+ commonground_api_common-2.0.0.data/scripts/use_external_components,sha256=xvvbngO2aDUagVXv4xRUqPaVtH_eOaVMWLQ8lyAPhEA,369
4
+ vng_api_common/__init__.py,sha256=_7OlQdbVkK4jad0CLdpI0grT-zEAb-qgFmH5mFzDXiA,22
5
+ vng_api_common/admin.py,sha256=iFtUPGf-ha0I-bXgq8QIFrP23Kzk_H3FlgAjt0U-ip0,259
6
6
  vng_api_common/apps.py,sha256=wOQuxUImMpH39R0JrDdCZp5ADaUM8jU8vFPFW9froSs,3458
7
7
  vng_api_common/checks.py,sha256=tOyfV7MMLGh4anrd_W30LvJCxiyQ4sFs1mGd9mtrEc0,1175
8
8
  vng_api_common/choices.py,sha256=dboFRoM34GpRUpxB9WexexccopcQSogu1QIyY4B9ACY,541
9
- vng_api_common/client.py,sha256=IChAkSlxmu0_ZHLmfCo_lGH8MMnbahCkcYXm3_ujUAo,1203
9
+ vng_api_common/client.py,sha256=EZWygrJum6pLeKS77mzynfV9kq0gPTjXXSHEpeyEZHg,1888
10
10
  vng_api_common/compat.py,sha256=n4jDFSPzXnh-D_VXjftKMCJYj_q_t6eJrUTWLayl8jQ,768
11
11
  vng_api_common/constants.py,sha256=yYtnYId9alRuhGBoqaO8lkXYi_ATi2FW-wEDGRpiRuQ,10948
12
12
  vng_api_common/decorators.py,sha256=_p-mAyi5Na_-ll00ErcQ3mRoZNCsJzYDAliXn50Bmes,278
@@ -19,23 +19,23 @@ vng_api_common/filters.py,sha256=77m9s3V6woMqE7uOQoqzit7w1AQpWcGqZozzQFdKAYs,568
19
19
  vng_api_common/filtersets.py,sha256=Ioarp5t_cz6Hf3iIcP2Rc0D7tGYhcMQPVdIJJ2dvtv8,867
20
20
  vng_api_common/generators.py,sha256=tD7ZyyFgCQ3KiktpBFtn5YiVdl3jioGxzyvkEZFYdQQ,4911
21
21
  vng_api_common/geo.py,sha256=AZbrw0rwGYOmaSUk8JJSkx-4_tVrfT_cgggh9omRwhU,1862
22
- vng_api_common/middleware.py,sha256=DhdGA_9_bk7bYr-keHEt4F_VS6IkYJmzzAhkjqnzeYw,8857
23
- vng_api_common/mocks.py,sha256=JjpJyVmHReRUXbwDi2cnmuq4NYI1hLRr_W962TIhN6Y,6118
24
- vng_api_common/models.py,sha256=JmUfkM1PuiqW3wuTA-chMtcDGvohy4j0JsB95s7VK4Q,5041
25
- vng_api_common/oas.py,sha256=FiqBZ3n36q-fPRZEgpmLDxjnqmoAKmaTKZ-FlFcuRp0,2712
22
+ vng_api_common/middleware.py,sha256=2DRw0hPpvUMqwoH1Ze8S7tDB16lmj4Bnd6vN5ijkGM8,875
23
+ vng_api_common/mocks.py,sha256=0sELLs-uy2ndu29m5P6FN_p1ehNhYv7IoRayYPs379Y,6200
24
+ vng_api_common/models.py,sha256=3WgpCWQpkCOrMCtqp7EbnP681II43Sg_cch8ZOm4EcU,1707
25
+ vng_api_common/oas.py,sha256=v_M_nsPRk12VF4lFa3Kw2c-wUThfY8PJ6JlEgeUhz9E,2618
26
26
  vng_api_common/pagination.py,sha256=sI6VJ5AHDBhtNRQ8OFrD2heQK-NxaL_6n73ijiqWDLc,255
27
27
  vng_api_common/permissions.py,sha256=ayDxk9Wt8j0yO57FhZ8XaRkPET4lAqd8SSw3m4o3EGs,7562
28
28
  vng_api_common/polymorphism.py,sha256=N-x39pG2unD4N0ZbxFvuhE8ibPaJ0eKeqfvAJW3NnEU,6623
29
- vng_api_common/routers.py,sha256=Tq_V-ewibFfcMl-0TS91_jvel6SxQGF-649GSKqlexE,1906
29
+ vng_api_common/routers.py,sha256=hEnhBulkgMM-7W_lYaykKTgTBj3-avl7DGsR9P7BbTU,1897
30
30
  vng_api_common/schema.py,sha256=1Bt9dH67AOLpfPNF7Q4IH1cXJeaIevSamZQzJ8DJ7ks,6144
31
31
  vng_api_common/scopes.py,sha256=PGs6CkXorAAdWXGFY1bSy-jmsPn122Njen9aFFOpFIQ,2351
32
32
  vng_api_common/search.py,sha256=yehS6boCOk1JXLCqAMU-B62hWtbTBSf_WKIVGPgp0Mg,1045
33
33
  vng_api_common/serializers.py,sha256=7_7IotlRfVb0woe6Wy79l_el3sPzrSY8-bfiYLbEUqs,10162
34
34
  vng_api_common/urls.py,sha256=9IWHYLlEIIHNaZ_Zq02qNQ2HJpETb7o-89r7yBM_tQs,270
35
- vng_api_common/utils.py,sha256=CFmG4xVabUA4GcWiOEbGSaL5mdFi8njsspTXRl8yjCY,9057
36
- vng_api_common/validators.py,sha256=iYtOZs9Lqm4FDZ0PMfT44wxqlHN58RRd42Bv46iNn0I,12164
35
+ vng_api_common/utils.py,sha256=zrtpssOA-NcJHhAlxioBiXeY3G2R_uf0l8oWkDD_EiE,8511
36
+ vng_api_common/validators.py,sha256=Y1OQrmnH3U01hnCizWc_xjpSSGUWKlwiFxeHHGCThpI,11012
37
37
  vng_api_common/version.py,sha256=yJV9_yTM7Qnzg0zGNkJQkN9Uai3I_ZUkcyseJRPRk5I,129
38
- vng_api_common/views.py,sha256=2fraDBtq9x2hMauRuOKkn6cDUw0IYdzfPKTxFFRRFXk,7309
38
+ vng_api_common/views.py,sha256=seRGa-_2BPtrtHP6C6mmQCnT10q9ECfRHa8lpwyEcmM,7230
39
39
  vng_api_common/viewsets.py,sha256=z5pzvSymFiiuCjP_-uuW-3OJKZY_psPAt8fWeWySU0c,2278
40
40
  vng_api_common/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  vng_api_common/api/permissions.py,sha256=okbwiscxlAtbQWTCRDLL2reOxgj0rRDZeDcrtXAYq00,739
@@ -75,9 +75,11 @@ vng_api_common/audittrails/migrations/0018_auto_20221212_0745.py,sha256=pf4NBYI0
75
75
  vng_api_common/audittrails/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  vng_api_common/audittrails/migrations/_operations.py,sha256=UOMv0zAK8CIQ73cSu6wwQG_hkW46Isdy7JCnljn8GII,3280
77
77
  vng_api_common/authorizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
- vng_api_common/authorizations/admin.py,sha256=BmJQgNH7KlDB3flAXrSMm_w0NowpJrDYWDY61RZcKCI,795
79
- vng_api_common/authorizations/models.py,sha256=ahwEMG5R6ILTlrnKo5TggYp52Z7dAJMk2QSZyZW8Qa0,4236
78
+ vng_api_common/authorizations/admin.py,sha256=Tk0yYKbb005E0XZaYYWbucMf_K5M8Hhz62wSBDi8rhM,813
79
+ vng_api_common/authorizations/middleware.py,sha256=KJ3znCXPRMOVqSur62SmBjvC6RcKxtcWq1rzaHdYR98,8416
80
+ vng_api_common/authorizations/models.py,sha256=0bcZwRc0ntIlPzUM7I8R5yhQRrnXvAZ_l9FpTnpbnxk,5207
80
81
  vng_api_common/authorizations/serializers.py,sha256=3HeKWEqhI3UWwI8SttC4rEID-Epbk7SWsC-bEjolbaw,5151
82
+ vng_api_common/authorizations/utils.py,sha256=VCXdU4q3CQ1cvuYkg1dPWSbZym1Ufoz5gSmbHYkQcgw,521
81
83
  vng_api_common/authorizations/validators.py,sha256=u7fKm0QgGy8fiAeYmIEB9Gy-yIE9C-tC2ZpnNQBXPso,2816
82
84
  vng_api_common/authorizations/migrations/0001_initial.py,sha256=ooAZtQeDtWgDxXzAP-KnSyyFYLRPM-PMrK5RgOnTPjQ,4360
83
85
  vng_api_common/authorizations/migrations/0002_authorizationsconfig.py,sha256=m4taH6ClHI-YHYGGOKaq_qYXGx9lq1InXOGLQKg9MSw,1364
@@ -94,6 +96,7 @@ vng_api_common/authorizations/migrations/0012_auto_20200619_0545.py,sha256=ECvJJ
94
96
  vng_api_common/authorizations/migrations/0013_auto_20201207_0846.py,sha256=fyJ0HpQMM0d1UyK9AK5_c7S_as_NejzrOdbMYiIogOA,1788
95
97
  vng_api_common/authorizations/migrations/0014_auto_20201221_0905.py,sha256=pkg7Y9yvUIY-QB9elvCxfR7OZJ3xMBIha_4WSZeDlwY,1659
96
98
  vng_api_common/authorizations/migrations/0015_auto_20220318_1608.py,sha256=jR1MDuqapBZU4bYVx0V3MRauRpDk_DtCVcBSe7gmtUA,1751
99
+ vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py,sha256=-UbTWExHaPL0O8585mm4_OFCm8xJXAmfwmU7V0ysSgg,2407
97
100
  vng_api_common/authorizations/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
101
  vng_api_common/caching/__init__.py,sha256=v_FC6HaHESwr_J94TWzaQBKtIjDLnsQADvqHylEW7g8,687
99
102
  vng_api_common/caching/decorators.py,sha256=lU5dnBjD9aHjoYaV32FlYxY7VFZPdMZe26fVDa_h3Q4,1804
@@ -103,7 +106,7 @@ vng_api_common/caching/models.py,sha256=RroS9HFiKNXDV59Odh0x8BO8Az8E81v4gwuprF1A
103
106
  vng_api_common/caching/registry.py,sha256=mY1r99x7m0DQ1BN9lZF2zMjFj_oDNq7urxDkOH7-8Yg,6205
104
107
  vng_api_common/caching/signals.py,sha256=78ej5cVan-JpNHKzZNAfs0m2ON_TXKphe8ZKtP-6FVY,4615
105
108
  vng_api_common/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
- vng_api_common/conf/api.py,sha256=m7ITWOQTZL0_4gh7f0K_MYcFp8KcoT-IxyhzZGrBS_8,4436
109
+ vng_api_common/conf/api.py,sha256=bRc488POvBdKeaKVit7s1_LFP-BRLpETmTzcQ6JD614,4372
107
110
  vng_api_common/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
111
  vng_api_common/db/operations.py,sha256=3vj9zD4EntMSmYsNGN1pRDjAcVM68MESDdoAk9wl0U0,3333
109
112
  vng_api_common/inspectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,7 +124,7 @@ vng_api_common/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
121
124
  vng_api_common/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
125
  vng_api_common/management/commands/generate_autorisaties.py,sha256=kMaZQo6e6vCXA9y0e6P2PlxObyjHMyNynRcM4WtzVZI,1130
123
126
  vng_api_common/management/commands/generate_notificaties.py,sha256=0p7CkLkQo-BeV2IljGv0rcRNw2aWY_HwlvybjggZ_gs,1090
124
- vng_api_common/management/commands/generate_swagger.py,sha256=t9_w2cEEa8ibMzFgg6Nl-8amgVj3qOc4hD44R89arHk,6134
127
+ vng_api_common/management/commands/generate_swagger.py,sha256=DyRRSDMFEZeXSJp_PWiMc6vYFK3GPEO4WIRWZV-tlm0,6120
125
128
  vng_api_common/management/commands/patch_error_contenttypes.py,sha256=xyFIQ4JRutLZCTcLrWqon0FDeAJCw1bkVg4jM8-EM24,2025
126
129
  vng_api_common/management/commands/use_external_components.py,sha256=FLkhZ5jAIZoAXQWwxqu4yT94LiBJ4GRr6g4ZRp7b_Eg,2936
127
130
  vng_api_common/migrations/0001_initial.py,sha256=frE83rux6uVHK4v-1fsb_1C00TBQQ6koFBA3DoVPHmk,895
@@ -129,15 +132,14 @@ vng_api_common/migrations/0002_apicredential.py,sha256=AYgkLMizI0g-Ogth8j_FVJHTD
129
132
  vng_api_common/migrations/0003_auto_20190417_1145.py,sha256=0ABqDggAdjmZxhSsLrMb2ZBwTcR1BOWo1Tn8StfiG0A,1001
130
133
  vng_api_common/migrations/0004_auto_20190517_0903.py,sha256=-Qcei-AJx4SXbEYMojmYInhV74uKtDncGP1PLs6hn0M,707
131
134
  vng_api_common/migrations/0005_auto_20190614_1346.py,sha256=5n8SVpbEJzLrSx-7d446Vy1--SaDfO05hAltrSRQx6o,2949
135
+ vng_api_common/migrations/0006_delete_apicredential.py,sha256=VMVwH4vj9Zq-oF_Z1Do9wobgePtkujwd8bO8M57ACt0,3621
132
136
  vng_api_common/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
137
  vng_api_common/notifications/__init__.py,sha256=UvHr_M8s-dZjCQOp60rLDqP_T3D0CgRIqc8ry5bIM8c,229
134
138
  vng_api_common/notifications/apps.py,sha256=7grk1hxQn5usAWV-sWATmNJ4TwSvT3izXVvV-L-zb_s,208
135
- vng_api_common/notifications/constants.py,sha256=R9c0Ejzw2nt-PRk0dCFbC8ugP_mTdgNJHnZ7gvzrTVE,156
136
- vng_api_common/notifications/handlers.py,sha256=-r52nIKbHdFUxE7hob9PiFRe0D3cizhkxbW8qhpK-Ks,2005
137
- vng_api_common/notifications/models.py,sha256=Ez3JYeM7BNSiFYtxAtVxWUWDc8t7jOFQlRTjrtE6SlI,2942
139
+ vng_api_common/notifications/handlers.py,sha256=xjuE_qn5h4gbRNGMsTaCTrHIjXTBcF8na6oMURGAuUU,2089
138
140
  vng_api_common/notifications/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
141
  vng_api_common/notifications/api/urls.py,sha256=c1T_6tuRpgN0V80Xg_ehZzGGlF1FNMmHdBEPAjR736I,164
140
- vng_api_common/notifications/api/views.py,sha256=8LMh4XU_qCQURx7BLn6HnaFt1auUXlF7PHlOrcJSZ0w,2303
142
+ vng_api_common/notifications/api/views.py,sha256=EvppqaANutuOIrkNepavmzFTX6fxo32c6l5c6DKHe8k,2326
141
143
  vng_api_common/notifications/migrations/0001_initial.py,sha256=zEIVIB--6FStaTl3r9GCXxHL1cSCWRO3T90rEpJr3hY,3049
142
144
  vng_api_common/notifications/migrations/0002_subscription__subscription.py,sha256=eqaj6-FAl7vuCswdvin9Lf8nzjP8U6WqVQO4opNKKCc,550
143
145
  vng_api_common/notifications/migrations/0003_auto_20190319_1048.py,sha256=C-ccYUwQPNCjTctpDDFhnQbm_OcpAlTjYQbEuiEi-ys,987
@@ -148,6 +150,7 @@ vng_api_common/notifications/migrations/0007_auto_20190429_1442.py,sha256=S9Q_WC
148
150
  vng_api_common/notifications/migrations/0008_auto_20190502_0415.py,sha256=Wwh3BsSg0r1I7HZZmrcCHEQMLmaeWEjbqYKapVafb2s,523
149
151
  vng_api_common/notifications/migrations/0009_auto_20190729_0427.py,sha256=Bfw-Axr9lNiXL8Ghns3RK33Dmjvpd_E5MWC740WJlb8,529
150
152
  vng_api_common/notifications/migrations/0010_auto_20220704_1419.py,sha256=Y57o91fbzWZyQO4tgg9Tn4r99hRErRlcrzpLii9Tm3I,1593
153
+ vng_api_common/notifications/migrations/0011_remove_subscription_config_and_more.py,sha256=3FA3rlpRDp8FB8Azl22pv7C4i1oUAQkRHHK9PRpNeJU,499
151
154
  vng_api_common/notifications/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
155
  vng_api_common/static/vng_api_common/css/screen.css,sha256=1Zt-yVFbm8qR9LakcYTF5x8p_91r8dMA-VRjIadYeP0,4674
153
156
  vng_api_common/static/vng_api_common/css/admin/admin_overrides.css,sha256=81xuIyYfiTgq8O9h5NsSf-v8Sx885ICSEx1f-5fWy4U,1748
@@ -186,9 +189,9 @@ vng_api_common/templatetags/vng_api_common.py,sha256=j4Fu4rNErLcXStBbuZ9Zd_xD_ft
186
189
  vng_api_common/tests/__init__.py,sha256=zBI2etwu2lEkKkIN1_cPuugJgPcoohQuJbdiUGtoceU,426
187
190
  vng_api_common/tests/auth.py,sha256=IKDWTEFv4Bign4F70-ibsFcnJqRxEJaXvqaPQJWa1xY,4544
188
191
  vng_api_common/tests/caching.py,sha256=zfIw5cRRvO9cekHZZKfRqZc8cx5IfJUYNmcH6cuIMg4,624
189
- vng_api_common/tests/schema.py,sha256=9LFgEXzcYGkHZtlpCiHNCWywR5lF7Pl0S43cwlwKDgQ,1954
192
+ vng_api_common/tests/schema.py,sha256=irt_kIp1uJsnYIJlrpk0-qM9W6i44WyX3GSiJvGUfdU,2288
190
193
  vng_api_common/tests/urls.py,sha256=PFrYzQbBC0TFPMEn3uPhcBG0IQs9JsEPqckicJT1UA4,2159
191
- commonground_api_common-1.13.3.dist-info/METADATA,sha256=fTK_yDoGxHnrz53LNSXzEKHACw0VXJdfeoyaeRjjiFM,6529
192
- commonground_api_common-1.13.3.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
193
- commonground_api_common-1.13.3.dist-info/top_level.txt,sha256=vPismc83zPzWXTmlNCCwfDlFV9iygJYxNJW5iDjKTgw,15
194
- commonground_api_common-1.13.3.dist-info/RECORD,,
194
+ commonground_api_common-2.0.0.dist-info/METADATA,sha256=hkvF9wlMO9qfbWy3oEhGKRQpuVRJUuzzODv5Rx4Ogic,6416
195
+ commonground_api_common-2.0.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
196
+ commonground_api_common-2.0.0.dist-info/top_level.txt,sha256=vPismc83zPzWXTmlNCCwfDlFV9iygJYxNJW5iDjKTgw,15
197
+ commonground_api_common-2.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +1 @@
1
- __version__ = "1.13.3"
1
+ __version__ = "2.0.0"
vng_api_common/admin.py CHANGED
@@ -1,29 +1,10 @@
1
1
  from django.contrib import admin
2
2
  from django.utils.translation import gettext_lazy as _
3
3
 
4
- from .models import APICredential, JWTSecret
4
+ from .models import JWTSecret
5
5
 
6
6
 
7
7
  @admin.register(JWTSecret)
8
8
  class JWTSecretAdmin(admin.ModelAdmin):
9
9
  list_display = ("identifier",)
10
10
  search_fields = ("identifier",)
11
-
12
-
13
- @admin.register(APICredential)
14
- class APICredentialAdmin(admin.ModelAdmin):
15
- list_display = ("label", "api_root", "client_id", "user_id")
16
- search_fields = ("label", "api_root")
17
- fieldsets = (
18
- (_("external API"), {"fields": ["api_root", "label"]}),
19
- (
20
- _("credentials"),
21
- {
22
- "description": _(
23
- "Credentials that indicate how this API or application identifies itself at the external "
24
- "API."
25
- ),
26
- "fields": ["client_id", "secret", "user_id", "user_representation"],
27
- },
28
- ),
29
- )
@@ -7,7 +7,7 @@ from .models import Applicatie, AuthorizationsConfig, Autorisatie
7
7
 
8
8
  @admin.register(AuthorizationsConfig)
9
9
  class AuthorizationsConfigAdmin(SingletonModelAdmin):
10
- list_display = ("api_root", "component")
10
+ list_display = ("authorizations_api_service", "component")
11
11
 
12
12
 
13
13
  @admin.register(Autorisatie)
@@ -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,33 @@ 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
+
25
60
 
26
61
  class ApplicatieManager(models.Manager):
27
62
  def get_by_natural_key(self, uuid):