udata 14.0.0__py3-none-any.whl → 14.5.1.dev6__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.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (152) hide show
  1. udata/api/__init__.py +2 -0
  2. udata/api_fields.py +35 -4
  3. udata/app.py +18 -20
  4. udata/auth/__init__.py +29 -6
  5. udata/auth/forms.py +2 -2
  6. udata/auth/views.py +13 -6
  7. udata/commands/dcat.py +1 -1
  8. udata/commands/serve.py +3 -11
  9. udata/commands/tests/test_fixtures.py +9 -9
  10. udata/core/access_type/api.py +1 -1
  11. udata/core/access_type/constants.py +12 -8
  12. udata/core/activity/api.py +5 -6
  13. udata/core/badges/tests/test_commands.py +6 -6
  14. udata/core/csv.py +5 -0
  15. udata/core/dataservices/api.py +8 -1
  16. udata/core/dataservices/apiv2.py +2 -5
  17. udata/core/dataservices/models.py +5 -2
  18. udata/core/dataservices/rdf.py +2 -1
  19. udata/core/dataservices/tasks.py +13 -2
  20. udata/core/dataset/api.py +10 -0
  21. udata/core/dataset/models.py +6 -6
  22. udata/core/dataset/permissions.py +31 -0
  23. udata/core/dataset/rdf.py +8 -2
  24. udata/core/dataset/tasks.py +23 -7
  25. udata/core/discussions/api.py +15 -1
  26. udata/core/discussions/models.py +6 -0
  27. udata/core/legal/__init__.py +0 -0
  28. udata/core/legal/mails.py +128 -0
  29. udata/core/organization/api.py +16 -5
  30. udata/core/organization/apiv2.py +2 -3
  31. udata/core/organization/mails.py +1 -1
  32. udata/core/organization/models.py +15 -2
  33. udata/core/organization/notifications.py +84 -0
  34. udata/core/organization/permissions.py +1 -1
  35. udata/core/organization/tasks.py +3 -0
  36. udata/core/pages/tests/test_api.py +32 -0
  37. udata/core/post/api.py +24 -69
  38. udata/core/post/models.py +84 -16
  39. udata/core/post/tests/test_api.py +24 -1
  40. udata/core/reports/api.py +18 -0
  41. udata/core/reports/models.py +42 -2
  42. udata/core/reuse/api.py +8 -0
  43. udata/core/reuse/apiv2.py +2 -5
  44. udata/core/reuse/models.py +1 -1
  45. udata/core/reuse/tasks.py +7 -0
  46. udata/core/spatial/forms.py +2 -2
  47. udata/core/topic/models.py +8 -2
  48. udata/core/user/api.py +10 -3
  49. udata/core/user/models.py +12 -2
  50. udata/features/notifications/api.py +7 -18
  51. udata/features/notifications/models.py +56 -0
  52. udata/features/notifications/tasks.py +25 -0
  53. udata/flask_mongoengine/engine.py +0 -4
  54. udata/flask_mongoengine/pagination.py +1 -1
  55. udata/frontend/markdown.py +2 -1
  56. udata/harvest/actions.py +21 -1
  57. udata/harvest/api.py +25 -8
  58. udata/harvest/backends/base.py +27 -1
  59. udata/harvest/backends/ckan/harvesters.py +11 -2
  60. udata/harvest/backends/dcat.py +4 -1
  61. udata/harvest/commands.py +33 -0
  62. udata/harvest/filters.py +17 -6
  63. udata/harvest/models.py +16 -0
  64. udata/harvest/permissions.py +27 -0
  65. udata/harvest/tests/ckan/test_ckan_backend.py +33 -0
  66. udata/harvest/tests/test_actions.py +58 -5
  67. udata/harvest/tests/test_api.py +276 -122
  68. udata/harvest/tests/test_base_backend.py +86 -1
  69. udata/harvest/tests/test_dcat_backend.py +81 -10
  70. udata/harvest/tests/test_filters.py +6 -0
  71. udata/i18n.py +1 -4
  72. udata/mail.py +19 -1
  73. udata/migrations/2025-10-31-create-membership-request-notifications.py +55 -0
  74. udata/migrations/2025-12-04-add-uuid-to-discussion-messages.py +28 -0
  75. udata/mongo/slug_fields.py +1 -1
  76. udata/rdf.py +58 -10
  77. udata/routing.py +2 -2
  78. udata/settings.py +11 -0
  79. udata/tasks.py +1 -0
  80. udata/templates/mail/message.html +5 -31
  81. udata/tests/__init__.py +27 -2
  82. udata/tests/api/__init__.py +108 -21
  83. udata/tests/api/test_activities_api.py +36 -0
  84. udata/tests/api/test_auth_api.py +121 -95
  85. udata/tests/api/test_base_api.py +7 -4
  86. udata/tests/api/test_datasets_api.py +50 -19
  87. udata/tests/api/test_organizations_api.py +192 -197
  88. udata/tests/api/test_reports_api.py +157 -0
  89. udata/tests/api/test_reuses_api.py +147 -147
  90. udata/tests/api/test_security_api.py +12 -12
  91. udata/tests/api/test_swagger.py +4 -4
  92. udata/tests/api/test_tags_api.py +8 -8
  93. udata/tests/api/test_user_api.py +1 -1
  94. udata/tests/apiv2/test_search.py +30 -0
  95. udata/tests/apiv2/test_swagger.py +4 -4
  96. udata/tests/cli/test_cli_base.py +8 -9
  97. udata/tests/dataservice/test_dataservice_tasks.py +29 -0
  98. udata/tests/dataset/test_dataset_commands.py +4 -4
  99. udata/tests/dataset/test_dataset_model.py +66 -26
  100. udata/tests/dataset/test_dataset_rdf.py +99 -5
  101. udata/tests/dataset/test_dataset_tasks.py +25 -0
  102. udata/tests/frontend/test_auth.py +58 -1
  103. udata/tests/frontend/test_csv.py +0 -3
  104. udata/tests/helpers.py +31 -27
  105. udata/tests/organization/test_notifications.py +67 -2
  106. udata/tests/plugin.py +6 -261
  107. udata/tests/search/test_search_integration.py +33 -0
  108. udata/tests/site/test_site_csv_exports.py +22 -10
  109. udata/tests/test_activity.py +9 -9
  110. udata/tests/test_api_fields.py +10 -0
  111. udata/tests/test_dcat_commands.py +2 -2
  112. udata/tests/test_discussions.py +5 -5
  113. udata/tests/test_legal_mails.py +359 -0
  114. udata/tests/test_migrations.py +21 -21
  115. udata/tests/test_notifications.py +15 -57
  116. udata/tests/test_notifications_task.py +43 -0
  117. udata/tests/test_owned.py +81 -1
  118. udata/tests/test_storages.py +25 -19
  119. udata/tests/test_topics.py +77 -61
  120. udata/tests/test_uris.py +33 -0
  121. udata/tests/workers/test_jobs_commands.py +23 -23
  122. udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
  123. udata/translations/ar/LC_MESSAGES/udata.po +187 -108
  124. udata/translations/de/LC_MESSAGES/udata.mo +0 -0
  125. udata/translations/de/LC_MESSAGES/udata.po +187 -108
  126. udata/translations/es/LC_MESSAGES/udata.mo +0 -0
  127. udata/translations/es/LC_MESSAGES/udata.po +187 -108
  128. udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
  129. udata/translations/fr/LC_MESSAGES/udata.po +188 -109
  130. udata/translations/it/LC_MESSAGES/udata.mo +0 -0
  131. udata/translations/it/LC_MESSAGES/udata.po +187 -108
  132. udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
  133. udata/translations/pt/LC_MESSAGES/udata.po +187 -108
  134. udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
  135. udata/translations/sr/LC_MESSAGES/udata.po +187 -108
  136. udata/translations/udata.pot +215 -106
  137. udata/uris.py +0 -2
  138. udata-14.5.1.dev6.dist-info/METADATA +109 -0
  139. {udata-14.0.0.dist-info → udata-14.5.1.dev6.dist-info}/RECORD +143 -140
  140. udata/core/post/forms.py +0 -30
  141. udata/flask_mongoengine/json.py +0 -38
  142. udata/templates/mail/base.html +0 -105
  143. udata/templates/mail/base.txt +0 -6
  144. udata/templates/mail/button.html +0 -3
  145. udata/templates/mail/layouts/1-column.html +0 -19
  146. udata/templates/mail/layouts/2-columns.html +0 -20
  147. udata/templates/mail/layouts/center-panel.html +0 -16
  148. udata-14.0.0.dist-info/METADATA +0 -132
  149. {udata-14.0.0.dist-info → udata-14.5.1.dev6.dist-info}/WHEEL +0 -0
  150. {udata-14.0.0.dist-info → udata-14.5.1.dev6.dist-info}/entry_points.txt +0 -0
  151. {udata-14.0.0.dist-info → udata-14.5.1.dev6.dist-info}/licenses/LICENSE +0 -0
  152. {udata-14.0.0.dist-info → udata-14.5.1.dev6.dist-info}/top_level.txt +0 -0
@@ -67,34 +67,34 @@ def oauth(app, request):
67
67
 
68
68
 
69
69
  class APIAuthTest(PytestOnlyAPITestCase):
70
- def test_no_auth(self, api):
70
+ def test_no_auth(self):
71
71
  """Should not return a content type if there is no content on delete"""
72
- response = api.get(url_for("api.fake"))
72
+ response = self.get(url_for("api.fake"))
73
73
 
74
74
  assert200(response)
75
75
  assert response.content_type == "application/json"
76
76
  assert response.json == {"success": True}
77
77
 
78
- def test_session_auth(self, api):
78
+ def test_session_auth(self):
79
79
  """Should handle session authentication"""
80
- api.client.login() # Session auth
80
+ self.login() # Session auth
81
81
 
82
- response = api.post(url_for("api.fake"))
82
+ response = self.post(url_for("api.fake"))
83
83
 
84
84
  assert200(response)
85
85
  assert response.content_type == "application/json"
86
86
  assert response.json == {"success": True}
87
87
 
88
- def test_header_auth(self, api):
88
+ def test_header_auth(self):
89
89
  """Should handle header API Key authentication"""
90
- with api.user() as user: # API Key auth
91
- response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
90
+ with self.api_user() as user: # API Key auth
91
+ response = self.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
92
92
 
93
93
  assert200(response)
94
94
  assert response.content_type == "application/json"
95
95
  assert response.json == {"success": True}
96
96
 
97
- def test_oauth_auth(self, api, oauth):
97
+ def test_oauth_auth(self, oauth):
98
98
  """Should handle OAuth header authentication"""
99
99
  user = UserFactory()
100
100
  token = OAuth2Token.objects.create(
@@ -104,7 +104,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
104
104
  refresh_token="refresh-token",
105
105
  )
106
106
 
107
- response = api.post(
107
+ response = self.post(
108
108
  url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token.access_token])}
109
109
  )
110
110
 
@@ -112,7 +112,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
112
112
  assert response.content_type == "application/json"
113
113
  assert response.json == {"success": True}
114
114
 
115
- def test_bad_oauth_auth(self, api, oauth):
115
+ def test_bad_oauth_auth(self, oauth):
116
116
  """Should handle wrong OAuth header authentication"""
117
117
  user = UserFactory()
118
118
  OAuth2Token.objects.create(
@@ -122,53 +122,53 @@ class APIAuthTest(PytestOnlyAPITestCase):
122
122
  refresh_token="refresh-token",
123
123
  )
124
124
 
125
- response = api.post(
125
+ response = self.post(
126
126
  url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", "not-my-token"])}
127
127
  )
128
128
 
129
129
  assert401(response)
130
130
  assert response.content_type == "application/json"
131
131
 
132
- def test_no_apikey(self, api):
132
+ def test_no_apikey(self):
133
133
  """Should raise a HTTP 401 if no API Key is provided"""
134
- response = api.post(url_for("api.fake"))
134
+ response = self.post(url_for("api.fake"))
135
135
 
136
136
  assert401(response)
137
137
  assert response.content_type == "application/json"
138
138
  assert "message" in response.json
139
139
 
140
- def test_invalid_apikey(self, api):
140
+ def test_invalid_apikey(self):
141
141
  """Should raise a HTTP 401 if an invalid API Key is provided"""
142
- response = api.post(url_for("api.fake"), headers={"X-API-KEY": "fake"})
142
+ response = self.post(url_for("api.fake"), headers={"X-API-KEY": "fake"})
143
143
 
144
144
  assert401(response)
145
145
  assert response.content_type == "application/json"
146
146
  assert "message" in response.json
147
147
 
148
- def test_inactive_user(self, api):
148
+ def test_inactive_user(self):
149
149
  """Should raise a HTTP 401 if the user is inactive"""
150
150
  user = UserFactory(active=False)
151
- with api.user(user) as user:
152
- response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
151
+ with self.api_user(user) as user:
152
+ response = self.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
153
153
 
154
154
  assert401(response)
155
155
  assert response.content_type == "application/json"
156
156
  assert "message" in response.json
157
157
 
158
- def test_deleted_user(self, api):
158
+ def test_deleted_user(self):
159
159
  """Should raise a HTTP 401 if the user is deleted"""
160
160
  user = UserFactory()
161
161
  user.mark_as_deleted()
162
- with api.user(user) as user:
163
- response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
162
+ with self.api_user(user) as user:
163
+ response = self.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
164
164
 
165
165
  assert401(response)
166
166
  assert response.content_type == "application/json"
167
167
  assert "message" in response.json
168
168
 
169
- def test_validation_errors(self, api):
169
+ def test_validation_errors(self):
170
170
  """Should raise a HTTP 400 and returns errors on validation error"""
171
- response = api.put(url_for("api.fake"), {"email": "wrong"})
171
+ response = self.put(url_for("api.fake"), {"email": "wrong"})
172
172
 
173
173
  assert400(response)
174
174
  assert response.content_type == "application/json"
@@ -177,9 +177,9 @@ class APIAuthTest(PytestOnlyAPITestCase):
177
177
  assert field in response.json["errors"]
178
178
  assert isinstance(response.json["errors"][field], list)
179
179
 
180
- def test_no_validation_error(self, api):
180
+ def test_no_validation_error(self):
181
181
  """Should pass if no validation error"""
182
- response = api.put(
182
+ response = self.put(
183
183
  url_for("api.fake"),
184
184
  {
185
185
  "required": "value",
@@ -191,11 +191,11 @@ class APIAuthTest(PytestOnlyAPITestCase):
191
191
  assert200(response)
192
192
  assert response.json == {"success": True}
193
193
 
194
- def test_authorization_decline(self, client, oauth):
194
+ def test_authorization_decline(self, oauth):
195
195
  """Should redirect to the redirect_uri on authorization denied"""
196
- client.login()
196
+ self.login()
197
197
 
198
- response = client.post(
198
+ response = self.post(
199
199
  url_for(
200
200
  "oauth.authorize",
201
201
  response_type="code",
@@ -206,17 +206,18 @@ class APIAuthTest(PytestOnlyAPITestCase):
206
206
  "scope": "default",
207
207
  "refuse": "",
208
208
  },
209
+ json=False,
209
210
  )
210
211
 
211
212
  assert_status(response, 302)
212
213
  uri, params = response.location.split("?")
213
214
  assert uri == oauth.default_redirect_uri
214
215
 
215
- def test_authorization_accept(self, client, oauth):
216
+ def test_authorization_accept(self, oauth):
216
217
  """Should redirect to the redirect_uri on authorization accepted"""
217
- client.login()
218
+ self.login()
218
219
 
219
- response = client.post(
220
+ response = self.post(
220
221
  url_for(
221
222
  "oauth.authorize",
222
223
  response_type="code",
@@ -227,6 +228,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
227
228
  "scope": "default",
228
229
  "accept": "",
229
230
  },
231
+ json=False,
230
232
  )
231
233
 
232
234
  assert_status(response, 302)
@@ -236,14 +238,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
236
238
 
237
239
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=True)
238
240
  @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
239
- def test_authorization_accept_wildcard(self, client, oauth):
241
+ def test_authorization_accept_wildcard(self, oauth):
240
242
  """Should redirect to the redirect_uri on authorization accepted
241
243
  with wildcard enabled and used in config"""
242
- client.login()
244
+ self.login()
243
245
 
244
246
  redirect_uri = "https://subdomain.test.org/callback"
245
247
 
246
- response = client.post(
248
+ response = self.post(
247
249
  url_for(
248
250
  "oauth.authorize",
249
251
  response_type="code",
@@ -254,6 +256,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
254
256
  "scope": "default",
255
257
  "accept": "",
256
258
  },
259
+ json=False,
257
260
  )
258
261
 
259
262
  assert_status(response, 302)
@@ -263,14 +266,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
263
266
 
264
267
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=False)
265
268
  @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
266
- def test_authorization_accept_no_wildcard(self, client, oauth):
269
+ def test_authorization_accept_no_wildcard(self, oauth):
267
270
  """Should not redirect to the redirect_uri on authorization accepted
268
271
  without wildcard enabled while used in config"""
269
- client.login()
272
+ self.login()
270
273
 
271
274
  redirect_uri = "https://subdomain.test.org/callback"
272
275
 
273
- response = client.post(
276
+ response = self.post(
274
277
  url_for(
275
278
  "oauth.authorize",
276
279
  response_type="code",
@@ -281,6 +284,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
281
284
  "scope": "default",
282
285
  "accept": "",
283
286
  },
287
+ json=False,
284
288
  )
285
289
 
286
290
  assert_status(response, 400)
@@ -289,14 +293,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
289
293
 
290
294
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=True)
291
295
  @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
292
- def test_authorization_accept_wrong_wildcard(self, client, oauth):
296
+ def test_authorization_accept_wrong_wildcard(self, oauth):
293
297
  """Should not redirect to the redirect_uri on authorization accepted
294
298
  with wildcard enabled but mismatched from config"""
295
- client.login()
299
+ self.login()
296
300
 
297
301
  redirect_uri = "https://subdomain.example.com/callback"
298
302
 
299
- response = client.post(
303
+ response = self.post(
300
304
  url_for(
301
305
  "oauth.authorize",
302
306
  response_type="code",
@@ -307,16 +311,17 @@ class APIAuthTest(PytestOnlyAPITestCase):
307
311
  "scope": "default",
308
312
  "accept": "",
309
313
  },
314
+ json=False,
310
315
  )
311
316
 
312
317
  assert_status(response, 400)
313
318
  assert "error" in response.json
314
319
  assert "Redirect URI" in response.json["error_description"]
315
320
 
316
- def test_authorization_grant_token(self, client, oauth):
317
- client.login()
321
+ def test_authorization_grant_token(self, oauth):
322
+ self.login()
318
323
 
319
- response = client.post(
324
+ response = self.post(
320
325
  url_for(
321
326
  "oauth.authorize",
322
327
  response_type="code",
@@ -326,19 +331,21 @@ class APIAuthTest(PytestOnlyAPITestCase):
326
331
  "scope": "default",
327
332
  "accept": "",
328
333
  },
334
+ json=False,
329
335
  )
330
336
 
331
337
  uri, params = response.location.split("?")
332
338
  code = parse_qs(params)["code"][0]
333
339
 
334
- client.logout()
335
- response = client.post(
340
+ self.logout()
341
+ response = self.post(
336
342
  url_for("oauth.token"),
337
343
  {
338
344
  "grant_type": "authorization_code",
339
345
  "code": code,
340
346
  },
341
347
  headers=basic_header(oauth),
348
+ json=False,
342
349
  )
343
350
 
344
351
  assert200(response)
@@ -347,13 +354,13 @@ class APIAuthTest(PytestOnlyAPITestCase):
347
354
  tokens = OAuth2Token.objects(access_token=response.json["access_token"])
348
355
  assert len(tokens) == 1 # A token has been created and saved.
349
356
 
350
- def test_s256_code_challenge_success_client_secret_basic(self, client, oauth):
357
+ def test_s256_code_challenge_success_client_secret_basic(self, oauth):
351
358
  code_verifier = generate_token(48)
352
359
  code_challenge = create_s256_code_challenge(code_verifier)
353
360
 
354
- client.login()
361
+ self.login()
355
362
 
356
- response = client.post(
363
+ response = self.post(
357
364
  url_for(
358
365
  "oauth.authorize",
359
366
  response_type="code",
@@ -365,16 +372,18 @@ class APIAuthTest(PytestOnlyAPITestCase):
365
372
  "scope": "default",
366
373
  "accept": "",
367
374
  },
375
+ json=False,
368
376
  )
369
377
  assert "code=" in response.location
370
378
 
371
379
  params = dict(url_decode(urlparse.urlparse(response.location).query))
372
380
  code = params["code"]
373
381
 
374
- response = client.post(
382
+ response = self.post(
375
383
  url_for("oauth.token"),
376
384
  {"grant_type": "authorization_code", "code": code, "code_verifier": code_verifier},
377
385
  headers=basic_header(oauth),
386
+ json=False,
378
387
  )
379
388
 
380
389
  assert200(response)
@@ -383,7 +392,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
383
392
 
384
393
  token = response.json["access_token"]
385
394
 
386
- response = client.post(
395
+ response = self.post(
387
396
  url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token])}
388
397
  )
389
398
 
@@ -391,13 +400,13 @@ class APIAuthTest(PytestOnlyAPITestCase):
391
400
  assert response.content_type == "application/json"
392
401
  assert response.json == {"success": True}
393
402
 
394
- def test_s256_code_challenge_success_client_secret_post(self, client, oauth):
403
+ def test_s256_code_challenge_success_client_secret_post(self, oauth):
395
404
  code_verifier = generate_token(48)
396
405
  code_challenge = create_s256_code_challenge(code_verifier)
397
406
 
398
- client.login()
407
+ self.login()
399
408
 
400
- response = client.post(
409
+ response = self.post(
401
410
  url_for(
402
411
  "oauth.authorize",
403
412
  response_type="code",
@@ -409,13 +418,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
409
418
  "scope": "default",
410
419
  "accept": "",
411
420
  },
421
+ json=False,
412
422
  )
413
423
  assert "code=" in response.location
414
424
 
415
425
  params = dict(url_decode(urlparse.urlparse(response.location).query))
416
426
  code = params["code"]
417
427
 
418
- response = client.post(
428
+ response = self.post(
419
429
  url_for("oauth.token"),
420
430
  {
421
431
  "grant_type": "authorization_code",
@@ -424,6 +434,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
424
434
  "client_id": oauth.client_id,
425
435
  "client_secret": oauth.secret,
426
436
  },
437
+ json=False,
427
438
  )
428
439
 
429
440
  assert200(response)
@@ -432,7 +443,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
432
443
 
433
444
  token = response.json["access_token"]
434
445
 
435
- response = client.post(
446
+ response = self.post(
436
447
  url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token])}
437
448
  )
438
449
 
@@ -441,14 +452,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
441
452
  assert response.json == {"success": True}
442
453
 
443
454
  @pytest.mark.oauth(secret=None)
444
- def test_s256_code_challenge_success_no_client_secret(self, client, oauth):
455
+ def test_s256_code_challenge_success_no_client_secret(self, oauth):
445
456
  """Authenticate through an OAuth client that has no secret associated (public client)"""
446
457
  code_verifier = generate_token(48)
447
458
  code_challenge = create_s256_code_challenge(code_verifier)
448
459
 
449
- client.login()
460
+ self.login()
450
461
 
451
- response = client.post(
462
+ response = self.post(
452
463
  url_for(
453
464
  "oauth.authorize",
454
465
  response_type="code",
@@ -460,13 +471,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
460
471
  "scope": "default",
461
472
  "accept": "",
462
473
  },
474
+ json=False,
463
475
  )
464
476
  assert "code=" in response.location
465
477
 
466
478
  params = dict(url_decode(urlparse.urlparse(response.location).query))
467
479
  code = params["code"]
468
480
 
469
- response = client.post(
481
+ response = self.post(
470
482
  url_for("oauth.token"),
471
483
  {
472
484
  "grant_type": "authorization_code",
@@ -474,6 +486,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
474
486
  "code_verifier": code_verifier,
475
487
  "client_id": oauth.client_id,
476
488
  },
489
+ json=False,
477
490
  )
478
491
 
479
492
  assert200(response)
@@ -482,7 +495,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
482
495
 
483
496
  token = response.json["access_token"]
484
497
 
485
- response = client.post(
498
+ response = self.post(
486
499
  url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token])}
487
500
  )
488
501
 
@@ -490,14 +503,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
490
503
  assert response.content_type == "application/json"
491
504
  assert response.json == {"success": True}
492
505
 
493
- def test_s256_code_challenge_missing_client_secret(self, client, oauth):
506
+ def test_s256_code_challenge_missing_client_secret(self, oauth):
494
507
  """Fail authentication through an OAuth client with missing secret"""
495
508
  code_verifier = generate_token(48)
496
509
  code_challenge = create_s256_code_challenge(code_verifier)
497
510
 
498
- client.login()
511
+ self.login()
499
512
 
500
- response = client.post(
513
+ response = self.post(
501
514
  url_for(
502
515
  "oauth.authorize",
503
516
  response_type="code",
@@ -509,13 +522,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
509
522
  "scope": "default",
510
523
  "accept": "",
511
524
  },
525
+ json=False,
512
526
  )
513
527
  assert "code=" in response.location
514
528
 
515
529
  params = dict(url_decode(urlparse.urlparse(response.location).query))
516
530
  code = params["code"]
517
531
 
518
- response = client.post(
532
+ response = self.post(
519
533
  url_for("oauth.token"),
520
534
  {
521
535
  "grant_type": "authorization_code",
@@ -523,14 +537,15 @@ class APIAuthTest(PytestOnlyAPITestCase):
523
537
  "code_verifier": code_verifier,
524
538
  "client_id": oauth.client_id,
525
539
  },
540
+ json=False,
526
541
  )
527
542
 
528
543
  assert401(response)
529
544
 
530
- def test_authorization_multiple_grant_token(self, client, oauth):
545
+ def test_authorization_multiple_grant_token(self, oauth):
531
546
  for i in range(3):
532
- client.login()
533
- response = client.post(
547
+ self.login()
548
+ response = self.post(
534
549
  url_for(
535
550
  "oauth.authorize",
536
551
  response_type="code",
@@ -540,29 +555,31 @@ class APIAuthTest(PytestOnlyAPITestCase):
540
555
  "scope": "default",
541
556
  "accept": "",
542
557
  },
558
+ json=False,
543
559
  )
544
560
 
545
561
  uri, params = response.location.split("?")
546
562
  code = parse_qs(params)["code"][0]
547
563
 
548
- client.logout()
549
- response = client.post(
564
+ self.logout()
565
+ response = self.post(
550
566
  url_for("oauth.token"),
551
567
  {
552
568
  "grant_type": "authorization_code",
553
569
  "code": code,
554
570
  },
555
571
  headers=basic_header(oauth),
572
+ json=False,
556
573
  )
557
574
 
558
575
  assert200(response)
559
576
  assert response.content_type == "application/json"
560
577
  assert "access_token" in response.json
561
578
 
562
- def test_authorization_grant_token_body_credentials(self, client, oauth):
563
- client.login()
579
+ def test_authorization_grant_token_body_credentials(self, oauth):
580
+ self.login()
564
581
 
565
- response = client.post(
582
+ response = self.post(
566
583
  url_for(
567
584
  "oauth.authorize",
568
585
  response_type="code",
@@ -572,13 +589,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
572
589
  "scope": "default",
573
590
  "accept": "",
574
591
  },
592
+ json=False,
575
593
  )
576
594
 
577
595
  uri, params = response.location.split("?")
578
596
  code = parse_qs(params)["code"][0]
579
597
 
580
- client.logout()
581
- response = client.post(
598
+ self.logout()
599
+ response = self.post(
582
600
  url_for("oauth.token"),
583
601
  {
584
602
  "grant_type": "authorization_code",
@@ -586,6 +604,7 @@ class APIAuthTest(PytestOnlyAPITestCase):
586
604
  "client_id": oauth.client_id,
587
605
  "client_secret": oauth.secret,
588
606
  },
607
+ json=False,
589
608
  )
590
609
 
591
610
  assert200(response)
@@ -593,10 +612,10 @@ class APIAuthTest(PytestOnlyAPITestCase):
593
612
  assert "access_token" in response.json
594
613
 
595
614
  @pytest.mark.oauth(internal=True)
596
- def test_authorization_redirects_for_internal_clients(self, client, oauth):
597
- client.login()
615
+ def test_authorization_redirects_for_internal_clients(self, oauth):
616
+ self.login()
598
617
 
599
- response = client.get(
618
+ response = self.get(
600
619
  url_for(
601
620
  "oauth.authorize",
602
621
  response_type="code",
@@ -611,23 +630,24 @@ class APIAuthTest(PytestOnlyAPITestCase):
611
630
  assert uri == oauth.default_redirect_uri
612
631
  assert "code" in parse_qs(params)
613
632
 
614
- def test_client_credentials_grant_token(self, client, oauth):
615
- response = client.post(
633
+ def test_client_credentials_grant_token(self, oauth):
634
+ response = self.post(
616
635
  url_for("oauth.token"),
617
636
  {
618
637
  "grant_type": "client_credentials",
619
638
  },
620
639
  headers=basic_header(oauth),
640
+ json=False,
621
641
  )
622
642
 
623
643
  assert200(response)
624
644
  assert response.content_type == "application/json"
625
645
  assert "access_token" in response.json
626
646
 
627
- def test_password_grant_token(self, client, oauth):
647
+ def test_password_grant_token(self, oauth):
628
648
  user = UserFactory(password="password")
629
649
 
630
- response = client.post(
650
+ response = self.post(
631
651
  url_for("oauth.token"),
632
652
  {
633
653
  "grant_type": "password",
@@ -635,15 +655,16 @@ class APIAuthTest(PytestOnlyAPITestCase):
635
655
  "password": "password",
636
656
  },
637
657
  headers=basic_header(oauth),
658
+ json=False,
638
659
  )
639
660
 
640
661
  assert200(response)
641
662
  assert response.content_type == "application/json"
642
663
  assert "access_token" in response.json
643
664
 
644
- def test_invalid_implicit_grant_token(self, client, oauth):
645
- client.login()
646
- response = client.post(
665
+ def test_invalid_implicit_grant_token(self, oauth):
666
+ self.login()
667
+ response = self.post(
647
668
  url_for(
648
669
  "oauth.authorize",
649
670
  response_type="token",
@@ -652,13 +673,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
652
673
  {
653
674
  "accept": "",
654
675
  },
676
+ json=False,
655
677
  )
656
678
 
657
679
  assert_status(response, 400)
658
680
  assert response.json["error"] == "unsupported_response_type"
659
681
 
660
682
  @pytest.mark.oauth(confidential=True)
661
- def test_refresh_token(self, client, oauth):
683
+ def test_refresh_token(self, oauth):
662
684
  user = UserFactory()
663
685
  token_to_be_refreshed = OAuth2Token.objects.create(
664
686
  client=oauth,
@@ -680,13 +702,14 @@ class APIAuthTest(PytestOnlyAPITestCase):
680
702
  )
681
703
  tokens_count = OAuth2Token.objects.count()
682
704
 
683
- response = client.post(
705
+ response = self.post(
684
706
  url_for("oauth.token"),
685
707
  {
686
708
  "grant_type": "refresh_token",
687
709
  "refresh_token": token_to_be_refreshed.refresh_token,
688
710
  },
689
711
  headers=basic_header(oauth),
712
+ json=False,
690
713
  )
691
714
 
692
715
  assert200(response)
@@ -722,12 +745,13 @@ class APIAuthTest(PytestOnlyAPITestCase):
722
745
  access_token="access-token",
723
746
  refresh_token="refresh-token",
724
747
  )
725
- response = client.post(
748
+ response = self.post(
726
749
  url_for("oauth.revoke_token"),
727
750
  {
728
751
  "token": getattr(token, token_type),
729
752
  },
730
753
  headers=basic_header(oauth),
754
+ json=False,
731
755
  )
732
756
 
733
757
  assert200(response)
@@ -744,17 +768,18 @@ class APIAuthTest(PytestOnlyAPITestCase):
744
768
  access_token="access-token",
745
769
  refresh_token="refresh-token",
746
770
  )
747
- response = client.post(
771
+ response = self.post(
748
772
  url_for("oauth.revoke_token"),
749
773
  {"token": getattr(token, token_type), "token_type_hint": token_type},
750
774
  headers=basic_header(oauth),
775
+ json=False,
751
776
  )
752
777
  assert200(response)
753
778
 
754
779
  tok = OAuth2Token.objects(pk=token.pk).first()
755
780
  assert tok.revoked is True
756
781
 
757
- def test_revoke_token_with_bad_hint(self, client, oauth):
782
+ def test_revoke_token_with_bad_hint(self, oauth):
758
783
  user = UserFactory()
759
784
  token = OAuth2Token.objects.create(
760
785
  client=oauth,
@@ -763,37 +788,38 @@ class APIAuthTest(PytestOnlyAPITestCase):
763
788
  refresh_token="refresh-token",
764
789
  )
765
790
 
766
- response = client.post(
791
+ response = self.post(
767
792
  url_for("oauth.revoke_token"),
768
793
  {
769
794
  "token": token.access_token,
770
795
  "token_type_hint": "refresh_token",
771
796
  },
772
797
  headers=basic_header(oauth),
798
+ json=False,
773
799
  )
774
800
  assert200(response)
775
801
 
776
802
  tok = OAuth2Token.objects(pk=token.pk).first()
777
803
  assert tok.revoked is False
778
804
 
779
- def test_value_error(self, api):
805
+ def test_value_error(self):
780
806
  @ns.route("/exception", endpoint="exception")
781
807
  class ExceptionAPI(API):
782
808
  def get(self):
783
809
  raise ValueError("Not working")
784
810
 
785
- response = api.get(url_for("api.exception"))
811
+ response = self.get(url_for("api.exception"))
786
812
 
787
813
  assert400(response)
788
814
  assert response.json["message"] == "Not working"
789
815
 
790
- def test_permission_denied(self, api):
816
+ def test_permission_denied(self):
791
817
  @ns.route("/exception", endpoint="exception")
792
818
  class ExceptionAPI(API):
793
819
  def get(self):
794
820
  raise PermissionDenied("Permission denied")
795
821
 
796
- response = api.get(url_for("api.exception"))
822
+ response = self.get(url_for("api.exception"))
797
823
 
798
824
  assert403(response)
799
825
  assert "message" in response.json