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.
- {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/METADATA +22 -23
- {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/RECORD +30 -27
- {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/WHEEL +1 -1
- vng_api_common/__init__.py +1 -1
- vng_api_common/admin.py +1 -20
- vng_api_common/authorizations/admin.py +1 -1
- vng_api_common/authorizations/middleware.py +244 -0
- vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py +76 -0
- vng_api_common/authorizations/models.py +38 -3
- vng_api_common/authorizations/utils.py +17 -0
- vng_api_common/client.py +56 -27
- vng_api_common/conf/api.py +0 -3
- vng_api_common/management/commands/generate_swagger.py +1 -1
- vng_api_common/middleware.py +1 -227
- vng_api_common/migrations/0006_delete_apicredential.py +119 -0
- vng_api_common/mocks.py +4 -1
- vng_api_common/models.py +0 -111
- vng_api_common/notifications/api/views.py +1 -1
- vng_api_common/notifications/handlers.py +8 -3
- vng_api_common/notifications/migrations/0011_remove_subscription_config_and_more.py +23 -0
- vng_api_common/oas.py +0 -3
- vng_api_common/routers.py +3 -3
- vng_api_common/tests/schema.py +12 -0
- vng_api_common/utils.py +0 -22
- vng_api_common/validators.py +0 -38
- vng_api_common/views.py +26 -22
- vng_api_common/notifications/constants.py +0 -3
- vng_api_common/notifications/models.py +0 -97
- {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/generate_schema +0 -0
- {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/patch_content_types +0 -0
- {commonground_api_common-1.13.3.data → commonground_api_common-2.0.0.data}/scripts/use_external_components +0 -0
- {commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/top_level.txt +0 -0
{commonground_api_common-1.13.3.dist-info → commonground_api_common-2.0.0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: commonground-api-common
|
3
|
-
Version:
|
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>=
|
24
|
-
Requires-Dist: django-filter
|
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.
|
27
|
-
Requires-Dist:
|
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.
|
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.
|
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
|
-
|
40
|
-
|
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-
|
2
|
-
commonground_api_common-
|
3
|
-
commonground_api_common-
|
4
|
-
vng_api_common/__init__.py,sha256=
|
5
|
-
vng_api_common/admin.py,sha256=
|
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=
|
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=
|
23
|
-
vng_api_common/mocks.py,sha256=
|
24
|
-
vng_api_common/models.py,sha256=
|
25
|
-
vng_api_common/oas.py,sha256=
|
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=
|
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=
|
36
|
-
vng_api_common/validators.py,sha256=
|
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=
|
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=
|
79
|
-
vng_api_common/authorizations/
|
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=
|
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=
|
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/
|
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=
|
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=
|
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-
|
192
|
-
commonground_api_common-
|
193
|
-
commonground_api_common-
|
194
|
-
commonground_api_common-
|
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,,
|
vng_api_common/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "
|
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
|
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 = ("
|
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)
|
vng_api_common/authorizations/migrations/0016_remove_authorizationsconfig_api_root_and_more.py
ADDED
@@ -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
|
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
|
-
|
14
|
-
class AuthorizationsConfig(
|
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):
|