udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30382__py2.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 (425) hide show
  1. tasks/__init__.py +109 -107
  2. tasks/helpers.py +18 -18
  3. udata/__init__.py +4 -4
  4. udata/admin/views.py +5 -5
  5. udata/api/__init__.py +135 -124
  6. udata/api/commands.py +45 -37
  7. udata/api/errors.py +5 -4
  8. udata/api/fields.py +23 -21
  9. udata/api/oauth2.py +55 -74
  10. udata/api/parsers.py +15 -15
  11. udata/api/signals.py +1 -1
  12. udata/api_fields.py +137 -89
  13. udata/app.py +56 -54
  14. udata/assets.py +5 -5
  15. udata/auth/__init__.py +37 -26
  16. udata/auth/forms.py +23 -15
  17. udata/auth/helpers.py +1 -1
  18. udata/auth/mails.py +3 -3
  19. udata/auth/password_validation.py +19 -15
  20. udata/auth/views.py +94 -68
  21. udata/commands/__init__.py +71 -69
  22. udata/commands/cache.py +7 -7
  23. udata/commands/db.py +201 -140
  24. udata/commands/dcat.py +36 -30
  25. udata/commands/fixtures.py +100 -84
  26. udata/commands/images.py +21 -20
  27. udata/commands/info.py +17 -20
  28. udata/commands/init.py +10 -10
  29. udata/commands/purge.py +12 -13
  30. udata/commands/serve.py +41 -29
  31. udata/commands/static.py +16 -18
  32. udata/commands/test.py +20 -20
  33. udata/commands/tests/fixtures.py +26 -24
  34. udata/commands/worker.py +31 -33
  35. udata/core/__init__.py +12 -12
  36. udata/core/activity/__init__.py +0 -1
  37. udata/core/activity/api.py +59 -49
  38. udata/core/activity/models.py +28 -26
  39. udata/core/activity/signals.py +1 -1
  40. udata/core/activity/tasks.py +16 -10
  41. udata/core/badges/api.py +6 -6
  42. udata/core/badges/commands.py +14 -13
  43. udata/core/badges/fields.py +8 -5
  44. udata/core/badges/forms.py +7 -4
  45. udata/core/badges/models.py +16 -31
  46. udata/core/badges/permissions.py +1 -3
  47. udata/core/badges/signals.py +2 -2
  48. udata/core/badges/tasks.py +3 -2
  49. udata/core/badges/tests/test_commands.py +10 -10
  50. udata/core/badges/tests/test_model.py +24 -31
  51. udata/core/contact_point/api.py +19 -18
  52. udata/core/contact_point/api_fields.py +21 -14
  53. udata/core/contact_point/factories.py +2 -2
  54. udata/core/contact_point/forms.py +7 -6
  55. udata/core/contact_point/models.py +3 -5
  56. udata/core/dataservices/api.py +26 -21
  57. udata/core/dataservices/factories.py +13 -11
  58. udata/core/dataservices/models.py +35 -40
  59. udata/core/dataservices/permissions.py +4 -4
  60. udata/core/dataservices/rdf.py +40 -17
  61. udata/core/dataservices/tasks.py +4 -3
  62. udata/core/dataset/actions.py +10 -10
  63. udata/core/dataset/activities.py +21 -23
  64. udata/core/dataset/api.py +321 -298
  65. udata/core/dataset/api_fields.py +443 -271
  66. udata/core/dataset/apiv2.py +305 -229
  67. udata/core/dataset/commands.py +38 -36
  68. udata/core/dataset/constants.py +61 -54
  69. udata/core/dataset/csv.py +70 -74
  70. udata/core/dataset/events.py +39 -32
  71. udata/core/dataset/exceptions.py +8 -4
  72. udata/core/dataset/factories.py +57 -65
  73. udata/core/dataset/forms.py +87 -63
  74. udata/core/dataset/models.py +336 -280
  75. udata/core/dataset/permissions.py +9 -6
  76. udata/core/dataset/preview.py +15 -17
  77. udata/core/dataset/rdf.py +156 -122
  78. udata/core/dataset/search.py +92 -77
  79. udata/core/dataset/signals.py +1 -1
  80. udata/core/dataset/tasks.py +63 -54
  81. udata/core/discussions/actions.py +5 -5
  82. udata/core/discussions/api.py +124 -120
  83. udata/core/discussions/factories.py +2 -2
  84. udata/core/discussions/forms.py +9 -7
  85. udata/core/discussions/metrics.py +1 -3
  86. udata/core/discussions/models.py +25 -24
  87. udata/core/discussions/notifications.py +18 -14
  88. udata/core/discussions/permissions.py +3 -3
  89. udata/core/discussions/signals.py +4 -4
  90. udata/core/discussions/tasks.py +24 -28
  91. udata/core/followers/api.py +32 -33
  92. udata/core/followers/models.py +9 -9
  93. udata/core/followers/signals.py +3 -3
  94. udata/core/jobs/actions.py +7 -7
  95. udata/core/jobs/api.py +99 -92
  96. udata/core/jobs/commands.py +48 -49
  97. udata/core/jobs/forms.py +11 -11
  98. udata/core/jobs/models.py +6 -6
  99. udata/core/metrics/__init__.py +2 -2
  100. udata/core/metrics/commands.py +34 -30
  101. udata/core/metrics/models.py +2 -4
  102. udata/core/metrics/signals.py +1 -1
  103. udata/core/metrics/tasks.py +3 -3
  104. udata/core/organization/activities.py +12 -15
  105. udata/core/organization/api.py +167 -174
  106. udata/core/organization/api_fields.py +183 -124
  107. udata/core/organization/apiv2.py +32 -32
  108. udata/core/organization/commands.py +20 -22
  109. udata/core/organization/constants.py +11 -11
  110. udata/core/organization/csv.py +17 -15
  111. udata/core/organization/factories.py +8 -11
  112. udata/core/organization/forms.py +32 -26
  113. udata/core/organization/metrics.py +2 -1
  114. udata/core/organization/models.py +87 -67
  115. udata/core/organization/notifications.py +18 -14
  116. udata/core/organization/permissions.py +10 -11
  117. udata/core/organization/rdf.py +14 -14
  118. udata/core/organization/search.py +30 -28
  119. udata/core/organization/signals.py +7 -7
  120. udata/core/organization/tasks.py +42 -61
  121. udata/core/owned.py +38 -27
  122. udata/core/post/api.py +82 -81
  123. udata/core/post/constants.py +8 -5
  124. udata/core/post/factories.py +4 -4
  125. udata/core/post/forms.py +13 -14
  126. udata/core/post/models.py +20 -22
  127. udata/core/post/tests/test_api.py +30 -32
  128. udata/core/reports/api.py +8 -7
  129. udata/core/reports/constants.py +1 -3
  130. udata/core/reports/models.py +10 -10
  131. udata/core/reuse/activities.py +15 -19
  132. udata/core/reuse/api.py +123 -126
  133. udata/core/reuse/api_fields.py +120 -85
  134. udata/core/reuse/apiv2.py +11 -10
  135. udata/core/reuse/constants.py +23 -23
  136. udata/core/reuse/csv.py +18 -18
  137. udata/core/reuse/factories.py +5 -9
  138. udata/core/reuse/forms.py +24 -21
  139. udata/core/reuse/models.py +55 -51
  140. udata/core/reuse/permissions.py +2 -2
  141. udata/core/reuse/search.py +49 -46
  142. udata/core/reuse/signals.py +1 -1
  143. udata/core/reuse/tasks.py +4 -5
  144. udata/core/site/api.py +47 -50
  145. udata/core/site/factories.py +2 -2
  146. udata/core/site/forms.py +4 -5
  147. udata/core/site/models.py +94 -63
  148. udata/core/site/rdf.py +14 -14
  149. udata/core/spam/api.py +16 -9
  150. udata/core/spam/constants.py +4 -4
  151. udata/core/spam/fields.py +13 -7
  152. udata/core/spam/models.py +27 -20
  153. udata/core/spam/signals.py +1 -1
  154. udata/core/spam/tests/test_spam.py +6 -5
  155. udata/core/spatial/api.py +72 -80
  156. udata/core/spatial/api_fields.py +73 -58
  157. udata/core/spatial/commands.py +67 -64
  158. udata/core/spatial/constants.py +3 -3
  159. udata/core/spatial/factories.py +37 -54
  160. udata/core/spatial/forms.py +27 -26
  161. udata/core/spatial/geoids.py +17 -17
  162. udata/core/spatial/models.py +43 -47
  163. udata/core/spatial/tasks.py +2 -1
  164. udata/core/spatial/tests/test_api.py +115 -130
  165. udata/core/spatial/tests/test_fields.py +74 -77
  166. udata/core/spatial/tests/test_geoid.py +22 -22
  167. udata/core/spatial/tests/test_models.py +5 -7
  168. udata/core/spatial/translations.py +16 -16
  169. udata/core/storages/__init__.py +16 -18
  170. udata/core/storages/api.py +66 -64
  171. udata/core/storages/tasks.py +7 -7
  172. udata/core/storages/utils.py +15 -15
  173. udata/core/storages/views.py +5 -6
  174. udata/core/tags/api.py +17 -14
  175. udata/core/tags/csv.py +4 -4
  176. udata/core/tags/models.py +8 -5
  177. udata/core/tags/tasks.py +11 -13
  178. udata/core/tags/views.py +4 -4
  179. udata/core/topic/api.py +84 -73
  180. udata/core/topic/apiv2.py +157 -127
  181. udata/core/topic/factories.py +3 -4
  182. udata/core/topic/forms.py +12 -14
  183. udata/core/topic/models.py +14 -19
  184. udata/core/topic/parsers.py +26 -26
  185. udata/core/user/activities.py +30 -29
  186. udata/core/user/api.py +151 -152
  187. udata/core/user/api_fields.py +132 -100
  188. udata/core/user/apiv2.py +7 -7
  189. udata/core/user/commands.py +38 -38
  190. udata/core/user/factories.py +8 -9
  191. udata/core/user/forms.py +14 -11
  192. udata/core/user/metrics.py +2 -2
  193. udata/core/user/models.py +68 -69
  194. udata/core/user/permissions.py +4 -5
  195. udata/core/user/rdf.py +7 -8
  196. udata/core/user/tasks.py +2 -2
  197. udata/core/user/tests/test_user_model.py +24 -16
  198. udata/db/tasks.py +2 -1
  199. udata/entrypoints.py +35 -31
  200. udata/errors.py +2 -1
  201. udata/event/values.py +6 -6
  202. udata/factories.py +2 -2
  203. udata/features/identicon/api.py +5 -6
  204. udata/features/identicon/backends.py +48 -55
  205. udata/features/identicon/tests/test_backends.py +4 -5
  206. udata/features/notifications/__init__.py +0 -1
  207. udata/features/notifications/actions.py +9 -9
  208. udata/features/notifications/api.py +17 -13
  209. udata/features/territories/__init__.py +12 -10
  210. udata/features/territories/api.py +14 -15
  211. udata/features/territories/models.py +23 -28
  212. udata/features/transfer/actions.py +8 -11
  213. udata/features/transfer/api.py +84 -77
  214. udata/features/transfer/factories.py +2 -1
  215. udata/features/transfer/models.py +11 -12
  216. udata/features/transfer/notifications.py +19 -15
  217. udata/features/transfer/permissions.py +5 -5
  218. udata/forms/__init__.py +5 -2
  219. udata/forms/fields.py +164 -172
  220. udata/forms/validators.py +19 -22
  221. udata/forms/widgets.py +9 -13
  222. udata/frontend/__init__.py +31 -26
  223. udata/frontend/csv.py +68 -58
  224. udata/frontend/markdown.py +40 -44
  225. udata/harvest/actions.py +89 -77
  226. udata/harvest/api.py +294 -238
  227. udata/harvest/backends/__init__.py +4 -4
  228. udata/harvest/backends/base.py +128 -111
  229. udata/harvest/backends/dcat.py +80 -66
  230. udata/harvest/commands.py +56 -60
  231. udata/harvest/csv.py +8 -8
  232. udata/harvest/exceptions.py +6 -3
  233. udata/harvest/filters.py +24 -23
  234. udata/harvest/forms.py +27 -28
  235. udata/harvest/models.py +88 -80
  236. udata/harvest/notifications.py +15 -10
  237. udata/harvest/signals.py +13 -13
  238. udata/harvest/tasks.py +11 -10
  239. udata/harvest/tests/factories.py +23 -24
  240. udata/harvest/tests/test_actions.py +136 -166
  241. udata/harvest/tests/test_api.py +220 -214
  242. udata/harvest/tests/test_base_backend.py +117 -112
  243. udata/harvest/tests/test_dcat_backend.py +380 -308
  244. udata/harvest/tests/test_filters.py +33 -22
  245. udata/harvest/tests/test_models.py +11 -14
  246. udata/harvest/tests/test_notifications.py +6 -7
  247. udata/harvest/tests/test_tasks.py +7 -6
  248. udata/i18n.py +237 -78
  249. udata/linkchecker/backends.py +5 -11
  250. udata/linkchecker/checker.py +23 -22
  251. udata/linkchecker/commands.py +4 -6
  252. udata/linkchecker/models.py +6 -6
  253. udata/linkchecker/tasks.py +18 -20
  254. udata/mail.py +21 -21
  255. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  256. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  257. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  258. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  259. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  260. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  261. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  262. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  263. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  264. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  265. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  266. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  267. udata/migrations/2021-08-17-users-integrity.py +9 -8
  268. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  269. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  270. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  271. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  272. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  273. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  274. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  275. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  276. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  277. udata/migrations/__init__.py +123 -105
  278. udata/models/__init__.py +4 -4
  279. udata/mongo/__init__.py +13 -11
  280. udata/mongo/badges_field.py +3 -2
  281. udata/mongo/datetime_fields.py +13 -12
  282. udata/mongo/document.py +17 -16
  283. udata/mongo/engine.py +15 -16
  284. udata/mongo/errors.py +2 -1
  285. udata/mongo/extras_fields.py +30 -20
  286. udata/mongo/queryset.py +12 -12
  287. udata/mongo/slug_fields.py +38 -28
  288. udata/mongo/taglist_field.py +1 -2
  289. udata/mongo/url_field.py +5 -5
  290. udata/mongo/uuid_fields.py +4 -3
  291. udata/notifications/__init__.py +1 -1
  292. udata/notifications/mattermost.py +10 -9
  293. udata/rdf.py +167 -188
  294. udata/routing.py +40 -45
  295. udata/search/__init__.py +18 -19
  296. udata/search/adapter.py +17 -16
  297. udata/search/commands.py +44 -51
  298. udata/search/fields.py +13 -20
  299. udata/search/query.py +23 -18
  300. udata/search/result.py +9 -10
  301. udata/sentry.py +21 -19
  302. udata/settings.py +262 -198
  303. udata/sitemap.py +8 -6
  304. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.52e531c19f8de80c00cf.js} +3 -3
  305. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.52e531c19f8de80c00cf.js.map} +1 -1
  306. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.c3343a7f1070061c0e10.js} +2 -2
  307. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.c3343a7f1070061c0e10.js.map} +1 -1
  308. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.8fa42440ad75ca172e6d.js} +2 -2
  309. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.8fa42440ad75ca172e6d.js.map} +1 -1
  310. udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.9c6c8412729cd6d59cfa.js} +3 -3
  311. udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.9c6c8412729cd6d59cfa.js.map} +1 -1
  312. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.71d15c2e4f21feee2a9a.js} +3 -3
  313. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.71d15c2e4f21feee2a9a.js.map} +1 -1
  314. udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.9139dc098b8ea640b890.js} +3 -3
  315. udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.9139dc098b8ea640b890.js.map} +1 -1
  316. udata/static/common.js +1 -1
  317. udata/static/common.js.map +1 -1
  318. udata/storage/s3.py +20 -13
  319. udata/tags.py +4 -5
  320. udata/tasks.py +43 -42
  321. udata/tests/__init__.py +9 -6
  322. udata/tests/api/__init__.py +5 -6
  323. udata/tests/api/test_auth_api.py +395 -321
  324. udata/tests/api/test_base_api.py +31 -33
  325. udata/tests/api/test_contact_points.py +7 -9
  326. udata/tests/api/test_dataservices_api.py +211 -158
  327. udata/tests/api/test_datasets_api.py +823 -812
  328. udata/tests/api/test_follow_api.py +13 -15
  329. udata/tests/api/test_me_api.py +95 -112
  330. udata/tests/api/test_organizations_api.py +301 -339
  331. udata/tests/api/test_reports_api.py +35 -25
  332. udata/tests/api/test_reuses_api.py +134 -139
  333. udata/tests/api/test_swagger.py +5 -5
  334. udata/tests/api/test_tags_api.py +18 -25
  335. udata/tests/api/test_topics_api.py +94 -94
  336. udata/tests/api/test_transfer_api.py +53 -48
  337. udata/tests/api/test_user_api.py +128 -141
  338. udata/tests/apiv2/test_datasets.py +290 -198
  339. udata/tests/apiv2/test_me_api.py +10 -11
  340. udata/tests/apiv2/test_organizations.py +56 -74
  341. udata/tests/apiv2/test_swagger.py +5 -5
  342. udata/tests/apiv2/test_topics.py +69 -87
  343. udata/tests/cli/test_cli_base.py +8 -8
  344. udata/tests/cli/test_db_cli.py +21 -19
  345. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  346. udata/tests/dataset/test_csv_adapter.py +44 -35
  347. udata/tests/dataset/test_dataset_actions.py +2 -3
  348. udata/tests/dataset/test_dataset_commands.py +7 -8
  349. udata/tests/dataset/test_dataset_events.py +36 -29
  350. udata/tests/dataset/test_dataset_model.py +224 -217
  351. udata/tests/dataset/test_dataset_rdf.py +142 -131
  352. udata/tests/dataset/test_dataset_tasks.py +15 -15
  353. udata/tests/dataset/test_resource_preview.py +10 -13
  354. udata/tests/features/territories/__init__.py +9 -13
  355. udata/tests/features/territories/test_territories_api.py +71 -91
  356. udata/tests/forms/test_basic_fields.py +7 -7
  357. udata/tests/forms/test_current_user_field.py +39 -66
  358. udata/tests/forms/test_daterange_field.py +31 -39
  359. udata/tests/forms/test_dict_field.py +28 -26
  360. udata/tests/forms/test_extras_fields.py +102 -76
  361. udata/tests/forms/test_form_field.py +8 -8
  362. udata/tests/forms/test_image_field.py +33 -26
  363. udata/tests/forms/test_model_field.py +134 -123
  364. udata/tests/forms/test_model_list_field.py +7 -7
  365. udata/tests/forms/test_nested_model_list_field.py +117 -79
  366. udata/tests/forms/test_publish_as_field.py +36 -65
  367. udata/tests/forms/test_reference_field.py +34 -53
  368. udata/tests/forms/test_user_forms.py +23 -21
  369. udata/tests/forms/test_uuid_field.py +6 -10
  370. udata/tests/frontend/__init__.py +9 -6
  371. udata/tests/frontend/test_auth.py +7 -6
  372. udata/tests/frontend/test_csv.py +81 -96
  373. udata/tests/frontend/test_hooks.py +43 -43
  374. udata/tests/frontend/test_markdown.py +211 -191
  375. udata/tests/helpers.py +32 -37
  376. udata/tests/models.py +2 -2
  377. udata/tests/organization/test_csv_adapter.py +21 -16
  378. udata/tests/organization/test_notifications.py +11 -18
  379. udata/tests/organization/test_organization_model.py +13 -13
  380. udata/tests/organization/test_organization_rdf.py +29 -22
  381. udata/tests/organization/test_organization_tasks.py +16 -17
  382. udata/tests/plugin.py +76 -73
  383. udata/tests/reuse/test_reuse_model.py +21 -21
  384. udata/tests/reuse/test_reuse_task.py +11 -13
  385. udata/tests/search/__init__.py +11 -12
  386. udata/tests/search/test_adapter.py +60 -70
  387. udata/tests/search/test_query.py +16 -16
  388. udata/tests/search/test_results.py +10 -7
  389. udata/tests/site/test_site_api.py +11 -16
  390. udata/tests/site/test_site_metrics.py +20 -30
  391. udata/tests/site/test_site_model.py +4 -5
  392. udata/tests/site/test_site_rdf.py +94 -78
  393. udata/tests/test_activity.py +17 -17
  394. udata/tests/test_discussions.py +292 -299
  395. udata/tests/test_i18n.py +37 -40
  396. udata/tests/test_linkchecker.py +91 -85
  397. udata/tests/test_mail.py +13 -17
  398. udata/tests/test_migrations.py +219 -180
  399. udata/tests/test_model.py +164 -157
  400. udata/tests/test_notifications.py +17 -17
  401. udata/tests/test_owned.py +14 -14
  402. udata/tests/test_rdf.py +25 -23
  403. udata/tests/test_routing.py +89 -93
  404. udata/tests/test_storages.py +137 -128
  405. udata/tests/test_tags.py +44 -46
  406. udata/tests/test_topics.py +7 -7
  407. udata/tests/test_transfer.py +42 -49
  408. udata/tests/test_uris.py +160 -161
  409. udata/tests/test_utils.py +79 -71
  410. udata/tests/user/test_user_rdf.py +5 -9
  411. udata/tests/workers/test_jobs_commands.py +57 -58
  412. udata/tests/workers/test_tasks_routing.py +23 -29
  413. udata/tests/workers/test_workers_api.py +125 -131
  414. udata/tests/workers/test_workers_helpers.py +6 -6
  415. udata/tracking.py +4 -6
  416. udata/uris.py +45 -46
  417. udata/utils.py +68 -66
  418. udata/wsgi.py +1 -1
  419. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/METADATA +3 -2
  420. udata-9.1.2.dev30382.dist-info/RECORD +704 -0
  421. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  422. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/LICENSE +0 -0
  423. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/WHEEL +0 -0
  424. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/entry_points.txt +0 -0
  425. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/top_level.txt +0 -0
@@ -1,338 +1,365 @@
1
- import pytest
2
-
3
1
  from base64 import b64encode
4
2
  from urllib.parse import parse_qs
5
3
 
6
- from flask import url_for
4
+ import pytest
7
5
  from authlib.common.security import generate_token
8
- from authlib.common.urls import urlparse, url_decode
6
+ from authlib.common.urls import url_decode, urlparse
9
7
  from authlib.oauth2.rfc7636 import (
10
8
  create_s256_code_challenge,
11
9
  )
10
+ from flask import url_for
12
11
 
13
- from udata.api import api, API
12
+ from udata.api import API, api
14
13
  from udata.api.oauth2 import OAuth2Client, OAuth2Token
15
14
  from udata.auth import PermissionDenied
16
15
  from udata.core.user.factories import UserFactory
17
16
  from udata.forms import Form, fields, validators
18
17
  from udata.tests.helpers import (
19
- assert200, assert400, assert401, assert403, assert_status
18
+ assert200,
19
+ assert400,
20
+ assert401,
21
+ assert403,
22
+ assert_status,
20
23
  )
21
24
 
22
- ns = api.namespace('fake', 'A Fake namespace')
25
+ ns = api.namespace("fake", "A Fake namespace")
23
26
 
24
27
 
25
28
  class FakeForm(Form):
26
29
  required = fields.StringField(validators=[validators.DataRequired()])
27
- choices = fields.SelectField(choices=(('first', ''), ('second', '')))
30
+ choices = fields.SelectField(choices=(("first", ""), ("second", "")))
28
31
  email = fields.StringField(validators=[validators.Email()])
29
32
 
30
33
 
31
- @ns.route('/', endpoint='fake')
34
+ @ns.route("/", endpoint="fake")
32
35
  class FakeAPI(API):
33
36
  @api.secure
34
37
  def post(self):
35
- return {'success': True}
38
+ return {"success": True}
36
39
 
37
40
  def get(self):
38
- return {'success': True}
41
+ return {"success": True}
39
42
 
40
43
  def put(self):
41
44
  api.validate(FakeForm)
42
- return {'success': True}
45
+ return {"success": True}
43
46
 
44
47
 
45
48
  def basic_header(client):
46
- payload = ':'.join((client.client_id, client.secret))
47
- token = b64encode(payload.encode('utf-8')).decode('utf8')
48
- return {'Authorization': 'Basic {}'.format(token)}
49
+ payload = ":".join((client.client_id, client.secret))
50
+ token = b64encode(payload.encode("utf-8")).decode("utf8")
51
+ return {"Authorization": "Basic {}".format(token)}
49
52
 
50
53
 
51
54
  @pytest.fixture
52
55
  def oauth(app, request):
53
- marker = request.node.get_closest_marker('oauth')
56
+ marker = request.node.get_closest_marker("oauth")
54
57
  custom_kwargs = marker.kwargs if marker else {}
55
58
  kwargs = dict(
56
- name='test-client',
59
+ name="test-client",
57
60
  owner=UserFactory(),
58
- redirect_uris=['https://test.org/callback'],
61
+ redirect_uris=["https://test.org/callback"],
59
62
  )
60
63
  kwargs.update(custom_kwargs)
61
64
  return OAuth2Client.objects.create(**kwargs)
62
65
 
63
66
 
64
- @pytest.mark.usefixtures('clean_db')
67
+ @pytest.mark.usefixtures("clean_db")
65
68
  class APIAuthTest:
66
69
  modules = []
67
70
 
68
71
  def test_no_auth(self, api):
69
- '''Should not return a content type if there is no content on delete'''
70
- response = api.get(url_for('api.fake'))
72
+ """Should not return a content type if there is no content on delete"""
73
+ response = api.get(url_for("api.fake"))
71
74
 
72
75
  assert200(response)
73
- assert response.content_type == 'application/json'
74
- assert response.json == {'success': True}
76
+ assert response.content_type == "application/json"
77
+ assert response.json == {"success": True}
75
78
 
76
79
  def test_session_auth(self, api):
77
- '''Should handle session authentication'''
80
+ """Should handle session authentication"""
78
81
  api.client.login() # Session auth
79
82
 
80
- response = api.post(url_for('api.fake'))
83
+ response = api.post(url_for("api.fake"))
81
84
 
82
85
  assert200(response)
83
- assert response.content_type == 'application/json'
84
- assert response.json == {'success': True}
86
+ assert response.content_type == "application/json"
87
+ assert response.json == {"success": True}
85
88
 
86
89
  def test_header_auth(self, api):
87
- '''Should handle header API Key authentication'''
90
+ """Should handle header API Key authentication"""
88
91
  with api.user() as user: # API Key auth
89
- response = api.post(url_for('api.fake'),
90
- headers={'X-API-KEY': user.apikey})
92
+ response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
91
93
 
92
94
  assert200(response)
93
- assert response.content_type == 'application/json'
94
- assert response.json == {'success': True}
95
+ assert response.content_type == "application/json"
96
+ assert response.json == {"success": True}
95
97
 
96
98
  def test_oauth_auth(self, api, oauth):
97
- '''Should handle OAuth header authentication'''
99
+ """Should handle OAuth header authentication"""
98
100
  user = UserFactory()
99
101
  token = OAuth2Token.objects.create(
100
102
  client=oauth,
101
103
  user=user,
102
- access_token='access-token',
103
- refresh_token='refresh-token',
104
+ access_token="access-token",
105
+ refresh_token="refresh-token",
104
106
  )
105
107
 
106
- response = api.post(url_for('api.fake'), headers={
107
- 'Authorization': ' '.join(['Bearer', token.access_token])
108
- })
108
+ response = api.post(
109
+ url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token.access_token])}
110
+ )
109
111
 
110
112
  assert200(response)
111
- assert response.content_type == 'application/json'
112
- assert response.json == {'success': True}
113
+ assert response.content_type == "application/json"
114
+ assert response.json == {"success": True}
113
115
 
114
116
  def test_bad_oauth_auth(self, api, oauth):
115
- '''Should handle wrong OAuth header authentication'''
117
+ """Should handle wrong OAuth header authentication"""
116
118
  user = UserFactory()
117
119
  OAuth2Token.objects.create(
118
120
  client=oauth,
119
121
  user=user,
120
- access_token='access-token',
121
- refresh_token='refresh-token',
122
+ access_token="access-token",
123
+ refresh_token="refresh-token",
122
124
  )
123
125
 
124
- response = api.post(url_for('api.fake'), headers={
125
- 'Authorization': ' '.join(['Bearer', 'not-my-token'])
126
- })
126
+ response = api.post(
127
+ url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", "not-my-token"])}
128
+ )
127
129
 
128
130
  assert401(response)
129
- assert response.content_type == 'application/json'
131
+ assert response.content_type == "application/json"
130
132
 
131
133
  def test_no_apikey(self, api):
132
- '''Should raise a HTTP 401 if no API Key is provided'''
133
- response = api.post(url_for('api.fake'))
134
+ """Should raise a HTTP 401 if no API Key is provided"""
135
+ response = api.post(url_for("api.fake"))
134
136
 
135
137
  assert401(response)
136
- assert response.content_type == 'application/json'
137
- assert 'message' in response.json
138
+ assert response.content_type == "application/json"
139
+ assert "message" in response.json
138
140
 
139
141
  def test_invalid_apikey(self, api):
140
- '''Should raise a HTTP 401 if an invalid API Key is provided'''
141
- response = api.post(url_for('api.fake'), headers={'X-API-KEY': 'fake'})
142
+ """Should raise a HTTP 401 if an invalid API Key is provided"""
143
+ response = api.post(url_for("api.fake"), headers={"X-API-KEY": "fake"})
142
144
 
143
145
  assert401(response)
144
- assert response.content_type == 'application/json'
145
- assert 'message' in response.json
146
+ assert response.content_type == "application/json"
147
+ assert "message" in response.json
146
148
 
147
149
  def test_inactive_user(self, api):
148
- '''Should raise a HTTP 401 if the user is inactive'''
150
+ """Should raise a HTTP 401 if the user is inactive"""
149
151
  user = UserFactory(active=False)
150
152
  with api.user(user) as user:
151
- response = api.post(url_for('api.fake'),
152
- headers={'X-API-KEY': user.apikey})
153
+ response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
153
154
 
154
155
  assert401(response)
155
- assert response.content_type == 'application/json'
156
- assert 'message' in response.json
156
+ assert response.content_type == "application/json"
157
+ assert "message" in response.json
157
158
 
158
159
  def test_deleted_user(self, api):
159
- '''Should raise a HTTP 401 if the user is deleted'''
160
+ """Should raise a HTTP 401 if the user is deleted"""
160
161
  user = UserFactory()
161
162
  user.mark_as_deleted()
162
163
  with api.user(user) as user:
163
- response = api.post(url_for('api.fake'),
164
- headers={'X-API-KEY': user.apikey})
164
+ response = api.post(url_for("api.fake"), headers={"X-API-KEY": user.apikey})
165
165
 
166
166
  assert401(response)
167
- assert response.content_type == 'application/json'
168
- assert 'message' in response.json
167
+ assert response.content_type == "application/json"
168
+ assert "message" in response.json
169
169
 
170
170
  def test_validation_errors(self, api):
171
- '''Should raise a HTTP 400 and returns errors on validation error'''
172
- response = api.put(url_for('api.fake'), {'email': 'wrong'})
171
+ """Should raise a HTTP 400 and returns errors on validation error"""
172
+ response = api.put(url_for("api.fake"), {"email": "wrong"})
173
173
 
174
174
  assert400(response)
175
- assert response.content_type == 'application/json'
175
+ assert response.content_type == "application/json"
176
176
 
177
- for field in 'required', 'email', 'choices':
178
- assert field in response.json['errors']
179
- assert isinstance(response.json['errors'][field], list)
177
+ for field in "required", "email", "choices":
178
+ assert field in response.json["errors"]
179
+ assert isinstance(response.json["errors"][field], list)
180
180
 
181
181
  def test_no_validation_error(self, api):
182
- '''Should pass if no validation error'''
183
- response = api.put(url_for('api.fake'), {
184
- 'required': 'value',
185
- 'email': 'coucou@cmoi.fr',
186
- 'choices': 'first',
187
- })
182
+ """Should pass if no validation error"""
183
+ response = api.put(
184
+ url_for("api.fake"),
185
+ {
186
+ "required": "value",
187
+ "email": "coucou@cmoi.fr",
188
+ "choices": "first",
189
+ },
190
+ )
188
191
 
189
192
  assert200(response)
190
- assert response.json == {'success': True}
193
+ assert response.json == {"success": True}
191
194
 
192
195
  def test_authorization_display(self, client, oauth):
193
- '''Should display the OAuth authorization page'''
196
+ """Should display the OAuth authorization page"""
194
197
  client.login()
195
198
 
196
- response = client.get(url_for(
197
- 'oauth.authorize',
198
- response_type='code',
199
- client_id=oauth.client_id,
200
- redirect_uri=oauth.default_redirect_uri
201
- ))
199
+ response = client.get(
200
+ url_for(
201
+ "oauth.authorize",
202
+ response_type="code",
203
+ client_id=oauth.client_id,
204
+ redirect_uri=oauth.default_redirect_uri,
205
+ )
206
+ )
202
207
 
203
208
  assert200(response)
204
209
 
205
210
  def test_authorization_decline(self, client, oauth):
206
- '''Should redirect to the redirect_uri on authorization denied'''
211
+ """Should redirect to the redirect_uri on authorization denied"""
207
212
  client.login()
208
213
 
209
- response = client.post(url_for(
210
- 'oauth.authorize',
211
- response_type='code',
212
- client_id=oauth.client_id,
213
- redirect_uri=oauth.default_redirect_uri
214
- ), {
215
- 'scope': 'default',
216
- 'refuse': '',
217
- })
214
+ response = client.post(
215
+ url_for(
216
+ "oauth.authorize",
217
+ response_type="code",
218
+ client_id=oauth.client_id,
219
+ redirect_uri=oauth.default_redirect_uri,
220
+ ),
221
+ {
222
+ "scope": "default",
223
+ "refuse": "",
224
+ },
225
+ )
218
226
 
219
227
  assert_status(response, 302)
220
- uri, params = response.location.split('?')
228
+ uri, params = response.location.split("?")
221
229
  assert uri == oauth.default_redirect_uri
222
230
 
223
231
  def test_authorization_accept(self, client, oauth):
224
- '''Should redirect to the redirect_uri on authorization accepted'''
232
+ """Should redirect to the redirect_uri on authorization accepted"""
225
233
  client.login()
226
234
 
227
- response = client.post(url_for(
228
- 'oauth.authorize',
229
- response_type='code',
230
- client_id=oauth.client_id,
231
- redirect_uri=oauth.default_redirect_uri
232
- ), {
233
- 'scope': 'default',
234
- 'accept': '',
235
- })
235
+ response = client.post(
236
+ url_for(
237
+ "oauth.authorize",
238
+ response_type="code",
239
+ client_id=oauth.client_id,
240
+ redirect_uri=oauth.default_redirect_uri,
241
+ ),
242
+ {
243
+ "scope": "default",
244
+ "accept": "",
245
+ },
246
+ )
236
247
 
237
248
  assert_status(response, 302)
238
- uri, params = response.location.split('?')
249
+ uri, params = response.location.split("?")
239
250
 
240
251
  assert uri == oauth.default_redirect_uri
241
252
 
242
253
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=True)
243
- @pytest.mark.oauth(redirect_uris=['https://*.test.org/callback'])
254
+ @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
244
255
  def test_authorization_accept_wildcard(self, client, oauth):
245
- '''Should redirect to the redirect_uri on authorization accepted
246
- with wildcard enabled and used in config'''
256
+ """Should redirect to the redirect_uri on authorization accepted
257
+ with wildcard enabled and used in config"""
247
258
  client.login()
248
259
 
249
- redirect_uri = 'https://subdomain.test.org/callback'
260
+ redirect_uri = "https://subdomain.test.org/callback"
250
261
 
251
- response = client.post(url_for(
252
- 'oauth.authorize',
253
- response_type='code',
254
- client_id=oauth.client_id,
255
- redirect_uri=redirect_uri,
256
- ), {
257
- 'scope': 'default',
258
- 'accept': '',
259
- })
262
+ response = client.post(
263
+ url_for(
264
+ "oauth.authorize",
265
+ response_type="code",
266
+ client_id=oauth.client_id,
267
+ redirect_uri=redirect_uri,
268
+ ),
269
+ {
270
+ "scope": "default",
271
+ "accept": "",
272
+ },
273
+ )
260
274
 
261
275
  assert_status(response, 302)
262
- uri, _ = response.location.split('?')
276
+ uri, _ = response.location.split("?")
263
277
 
264
278
  assert uri == redirect_uri
265
279
 
266
280
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=False)
267
- @pytest.mark.oauth(redirect_uris=['https://*.test.org/callback'])
281
+ @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
268
282
  def test_authorization_accept_no_wildcard(self, client, oauth):
269
- '''Should not redirect to the redirect_uri on authorization accepted
270
- without wildcard enabled while used in config'''
283
+ """Should not redirect to the redirect_uri on authorization accepted
284
+ without wildcard enabled while used in config"""
271
285
  client.login()
272
286
 
273
- redirect_uri = 'https://subdomain.test.org/callback'
287
+ redirect_uri = "https://subdomain.test.org/callback"
274
288
 
275
- response = client.post(url_for(
276
- 'oauth.authorize',
277
- response_type='code',
278
- client_id=oauth.client_id,
279
- redirect_uri=redirect_uri,
280
- ), {
281
- 'scope': 'default',
282
- 'accept': '',
283
- })
289
+ response = client.post(
290
+ url_for(
291
+ "oauth.authorize",
292
+ response_type="code",
293
+ client_id=oauth.client_id,
294
+ redirect_uri=redirect_uri,
295
+ ),
296
+ {
297
+ "scope": "default",
298
+ "accept": "",
299
+ },
300
+ )
284
301
 
285
302
  assert_status(response, 400)
286
- assert 'error' in response.json
287
- assert 'redirect_uri' in response.json['error_description']
303
+ assert "error" in response.json
304
+ assert "redirect_uri" in response.json["error_description"]
288
305
 
289
306
  @pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=True)
290
- @pytest.mark.oauth(redirect_uris=['https://*.test.org/callback'])
307
+ @pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
291
308
  def test_authorization_accept_wrong_wildcard(self, client, oauth):
292
- '''Should not redirect to the redirect_uri on authorization accepted
293
- with wildcard enabled but mismatched from config'''
309
+ """Should not redirect to the redirect_uri on authorization accepted
310
+ with wildcard enabled but mismatched from config"""
294
311
  client.login()
295
312
 
296
- redirect_uri = 'https://subdomain.example.com/callback'
313
+ redirect_uri = "https://subdomain.example.com/callback"
297
314
 
298
- response = client.post(url_for(
299
- 'oauth.authorize',
300
- response_type='code',
301
- client_id=oauth.client_id,
302
- redirect_uri=redirect_uri,
303
- ), {
304
- 'scope': 'default',
305
- 'accept': '',
306
- })
315
+ response = client.post(
316
+ url_for(
317
+ "oauth.authorize",
318
+ response_type="code",
319
+ client_id=oauth.client_id,
320
+ redirect_uri=redirect_uri,
321
+ ),
322
+ {
323
+ "scope": "default",
324
+ "accept": "",
325
+ },
326
+ )
307
327
 
308
328
  assert_status(response, 400)
309
- assert 'error' in response.json
310
- assert 'redirect_uri' in response.json['error_description']
329
+ assert "error" in response.json
330
+ assert "redirect_uri" in response.json["error_description"]
311
331
 
312
332
  def test_authorization_grant_token(self, client, oauth):
313
333
  client.login()
314
334
 
315
- response = client.post(url_for(
316
- 'oauth.authorize',
317
- response_type='code',
318
- client_id=oauth.client_id,
319
- ), {
320
- 'scope': 'default',
321
- 'accept': '',
322
- })
335
+ response = client.post(
336
+ url_for(
337
+ "oauth.authorize",
338
+ response_type="code",
339
+ client_id=oauth.client_id,
340
+ ),
341
+ {
342
+ "scope": "default",
343
+ "accept": "",
344
+ },
345
+ )
323
346
 
324
- uri, params = response.location.split('?')
325
- code = parse_qs(params)['code'][0]
347
+ uri, params = response.location.split("?")
348
+ code = parse_qs(params)["code"][0]
326
349
 
327
350
  client.logout()
328
- response = client.post(url_for('oauth.token'), {
329
- 'grant_type': 'authorization_code',
330
- 'code': code,
331
- }, headers=basic_header(oauth))
351
+ response = client.post(
352
+ url_for("oauth.token"),
353
+ {
354
+ "grant_type": "authorization_code",
355
+ "code": code,
356
+ },
357
+ headers=basic_header(oauth),
358
+ )
332
359
 
333
360
  assert200(response)
334
- assert response.content_type == 'application/json'
335
- assert 'access_token' in response.json
361
+ assert response.content_type == "application/json"
362
+ assert "access_token" in response.json
336
363
 
337
364
  def test_s256_code_challenge_success_client_secret_basic(self, client, oauth):
338
365
  code_verifier = generate_token(48)
@@ -340,40 +367,43 @@ class APIAuthTest:
340
367
 
341
368
  client.login()
342
369
 
343
- response = client.post(url_for(
344
- 'oauth.authorize',
345
- response_type='code',
346
- client_id=oauth.client_id,
347
- code_challenge=code_challenge,
348
- code_challenge_method='S256'
349
- ), {
350
- 'scope': 'default',
351
- 'accept': '',
352
- })
353
- assert 'code=' in response.location
370
+ response = client.post(
371
+ url_for(
372
+ "oauth.authorize",
373
+ response_type="code",
374
+ client_id=oauth.client_id,
375
+ code_challenge=code_challenge,
376
+ code_challenge_method="S256",
377
+ ),
378
+ {
379
+ "scope": "default",
380
+ "accept": "",
381
+ },
382
+ )
383
+ assert "code=" in response.location
354
384
 
355
385
  params = dict(url_decode(urlparse.urlparse(response.location).query))
356
- code = params['code']
386
+ code = params["code"]
357
387
 
358
- response = client.post(url_for('oauth.token'), {
359
- 'grant_type': 'authorization_code',
360
- 'code': code,
361
- 'code_verifier': code_verifier
362
- }, headers=basic_header(oauth))
388
+ response = client.post(
389
+ url_for("oauth.token"),
390
+ {"grant_type": "authorization_code", "code": code, "code_verifier": code_verifier},
391
+ headers=basic_header(oauth),
392
+ )
363
393
 
364
394
  assert200(response)
365
- assert response.content_type == 'application/json'
366
- assert 'access_token' in response.json
395
+ assert response.content_type == "application/json"
396
+ assert "access_token" in response.json
367
397
 
368
- token = response.json['access_token']
398
+ token = response.json["access_token"]
369
399
 
370
- response = client.post(url_for('api.fake'), headers={
371
- 'Authorization': ' '.join(['Bearer', token])
372
- })
400
+ response = client.post(
401
+ url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token])}
402
+ )
373
403
 
374
404
  assert200(response)
375
- assert response.content_type == 'application/json'
376
- assert response.json == {'success': True}
405
+ assert response.content_type == "application/json"
406
+ assert response.json == {"success": True}
377
407
 
378
408
  def test_s256_code_challenge_success_client_secret_post(self, client, oauth):
379
409
  code_verifier = generate_token(48)
@@ -381,147 +411,178 @@ class APIAuthTest:
381
411
 
382
412
  client.login()
383
413
 
384
- response = client.post(url_for(
385
- 'oauth.authorize',
386
- response_type='code',
387
- client_id=oauth.client_id,
388
- code_challenge=code_challenge,
389
- code_challenge_method='S256'
390
- ), {
391
- 'scope': 'default',
392
- 'accept': '',
393
- })
394
- assert 'code=' in response.location
414
+ response = client.post(
415
+ url_for(
416
+ "oauth.authorize",
417
+ response_type="code",
418
+ client_id=oauth.client_id,
419
+ code_challenge=code_challenge,
420
+ code_challenge_method="S256",
421
+ ),
422
+ {
423
+ "scope": "default",
424
+ "accept": "",
425
+ },
426
+ )
427
+ assert "code=" in response.location
395
428
 
396
429
  params = dict(url_decode(urlparse.urlparse(response.location).query))
397
- code = params['code']
398
-
399
- response = client.post(url_for('oauth.token'), {
400
- 'grant_type': 'authorization_code',
401
- 'code': code,
402
- 'code_verifier': code_verifier,
403
- 'client_id': oauth.client_id,
404
- 'client_secret': oauth.secret
405
- })
430
+ code = params["code"]
431
+
432
+ response = client.post(
433
+ url_for("oauth.token"),
434
+ {
435
+ "grant_type": "authorization_code",
436
+ "code": code,
437
+ "code_verifier": code_verifier,
438
+ "client_id": oauth.client_id,
439
+ "client_secret": oauth.secret,
440
+ },
441
+ )
406
442
 
407
443
  assert200(response)
408
- assert response.content_type == 'application/json'
409
- assert 'access_token' in response.json
444
+ assert response.content_type == "application/json"
445
+ assert "access_token" in response.json
410
446
 
411
- token = response.json['access_token']
447
+ token = response.json["access_token"]
412
448
 
413
- response = client.post(url_for('api.fake'), headers={
414
- 'Authorization': ' '.join(['Bearer', token])
415
- })
449
+ response = client.post(
450
+ url_for("api.fake"), headers={"Authorization": " ".join(["Bearer", token])}
451
+ )
416
452
 
417
453
  assert200(response)
418
- assert response.content_type == 'application/json'
419
- assert response.json == {'success': True}
454
+ assert response.content_type == "application/json"
455
+ assert response.json == {"success": True}
420
456
 
421
457
  def test_authorization_multiple_grant_token(self, client, oauth):
422
-
423
458
  for i in range(3):
424
459
  client.login()
425
- response = client.post(url_for(
426
- 'oauth.authorize',
427
- response_type='code',
428
- client_id=oauth.client_id,
429
- ), {
430
- 'scope': 'default',
431
- 'accept': '',
432
- })
433
-
434
- uri, params = response.location.split('?')
435
- code = parse_qs(params)['code'][0]
460
+ response = client.post(
461
+ url_for(
462
+ "oauth.authorize",
463
+ response_type="code",
464
+ client_id=oauth.client_id,
465
+ ),
466
+ {
467
+ "scope": "default",
468
+ "accept": "",
469
+ },
470
+ )
471
+
472
+ uri, params = response.location.split("?")
473
+ code = parse_qs(params)["code"][0]
436
474
 
437
475
  client.logout()
438
- response = client.post(url_for('oauth.token'), {
439
- 'grant_type': 'authorization_code',
440
- 'code': code,
441
- }, headers=basic_header(oauth))
476
+ response = client.post(
477
+ url_for("oauth.token"),
478
+ {
479
+ "grant_type": "authorization_code",
480
+ "code": code,
481
+ },
482
+ headers=basic_header(oauth),
483
+ )
442
484
 
443
485
  assert200(response)
444
- assert response.content_type == 'application/json'
445
- assert 'access_token' in response.json
486
+ assert response.content_type == "application/json"
487
+ assert "access_token" in response.json
446
488
 
447
489
  def test_authorization_grant_token_body_credentials(self, client, oauth):
448
490
  client.login()
449
491
 
450
- response = client.post(url_for(
451
- 'oauth.authorize',
452
- response_type='code',
453
- client_id=oauth.client_id,
454
- ), {
455
- 'scope': 'default',
456
- 'accept': '',
457
- })
492
+ response = client.post(
493
+ url_for(
494
+ "oauth.authorize",
495
+ response_type="code",
496
+ client_id=oauth.client_id,
497
+ ),
498
+ {
499
+ "scope": "default",
500
+ "accept": "",
501
+ },
502
+ )
458
503
 
459
- uri, params = response.location.split('?')
460
- code = parse_qs(params)['code'][0]
504
+ uri, params = response.location.split("?")
505
+ code = parse_qs(params)["code"][0]
461
506
 
462
507
  client.logout()
463
- response = client.post(url_for('oauth.token'), {
464
- 'grant_type': 'authorization_code',
465
- 'code': code,
466
- 'client_id': oauth.client_id,
467
- 'client_secret': oauth.secret,
468
- })
508
+ response = client.post(
509
+ url_for("oauth.token"),
510
+ {
511
+ "grant_type": "authorization_code",
512
+ "code": code,
513
+ "client_id": oauth.client_id,
514
+ "client_secret": oauth.secret,
515
+ },
516
+ )
469
517
 
470
518
  assert200(response)
471
- assert response.content_type == 'application/json'
472
- assert 'access_token' in response.json
519
+ assert response.content_type == "application/json"
520
+ assert "access_token" in response.json
473
521
 
474
522
  @pytest.mark.oauth(internal=True)
475
523
  def test_authorization_redirects_for_internal_clients(self, client, oauth):
476
524
  client.login()
477
525
 
478
- response = client.get(url_for(
479
- 'oauth.authorize',
480
- response_type='code',
481
- client_id=oauth.client_id,
482
- redirect_uri=oauth.default_redirect_uri
483
- ))
526
+ response = client.get(
527
+ url_for(
528
+ "oauth.authorize",
529
+ response_type="code",
530
+ client_id=oauth.client_id,
531
+ redirect_uri=oauth.default_redirect_uri,
532
+ )
533
+ )
484
534
 
485
535
  assert_status(response, 302)
486
- uri, params = response.location.split('?')
536
+ uri, params = response.location.split("?")
487
537
 
488
538
  assert uri == oauth.default_redirect_uri
489
- assert 'code' in parse_qs(params)
539
+ assert "code" in parse_qs(params)
490
540
 
491
541
  def test_client_credentials_grant_token(self, client, oauth):
492
- response = client.post(url_for('oauth.token'), {
493
- 'grant_type': 'client_credentials',
494
- }, headers=basic_header(oauth))
542
+ response = client.post(
543
+ url_for("oauth.token"),
544
+ {
545
+ "grant_type": "client_credentials",
546
+ },
547
+ headers=basic_header(oauth),
548
+ )
495
549
 
496
550
  assert200(response)
497
- assert response.content_type == 'application/json'
498
- assert 'access_token' in response.json
551
+ assert response.content_type == "application/json"
552
+ assert "access_token" in response.json
499
553
 
500
554
  def test_password_grant_token(self, client, oauth):
501
- user = UserFactory(password='password')
502
-
503
- response = client.post(url_for('oauth.token'), {
504
- 'grant_type': 'password',
505
- 'username': user.email,
506
- 'password': 'password',
507
- }, headers=basic_header(oauth))
555
+ user = UserFactory(password="password")
556
+
557
+ response = client.post(
558
+ url_for("oauth.token"),
559
+ {
560
+ "grant_type": "password",
561
+ "username": user.email,
562
+ "password": "password",
563
+ },
564
+ headers=basic_header(oauth),
565
+ )
508
566
 
509
567
  assert200(response)
510
- assert response.content_type == 'application/json'
511
- assert 'access_token' in response.json
568
+ assert response.content_type == "application/json"
569
+ assert "access_token" in response.json
512
570
 
513
571
  def test_invalid_implicit_grant_token(self, client, oauth):
514
572
  client.login()
515
- response = client.post(url_for(
516
- 'oauth.authorize',
517
- response_type='token',
518
- client_id=oauth.client_id,
519
- ), {
520
- 'accept': '',
521
- })
573
+ response = client.post(
574
+ url_for(
575
+ "oauth.authorize",
576
+ response_type="token",
577
+ client_id=oauth.client_id,
578
+ ),
579
+ {
580
+ "accept": "",
581
+ },
582
+ )
522
583
 
523
584
  assert_status(response, 400)
524
- assert response.json['error'] == 'invalid_grant'
585
+ assert response.json["error"] == "invalid_grant"
525
586
 
526
587
  @pytest.mark.oauth(confidential=True)
527
588
  def test_refresh_token(self, client, oauth):
@@ -529,50 +590,59 @@ class APIAuthTest:
529
590
  token = OAuth2Token.objects.create(
530
591
  client=oauth,
531
592
  user=user,
532
- access_token='access-token',
533
- refresh_token='refresh-token',
593
+ access_token="access-token",
594
+ refresh_token="refresh-token",
534
595
  )
535
596
 
536
- response = client.post(url_for('oauth.token'), {
537
- 'grant_type': 'refresh_token',
538
- 'refresh_token': token.refresh_token,
539
- }, headers=basic_header(oauth))
597
+ response = client.post(
598
+ url_for("oauth.token"),
599
+ {
600
+ "grant_type": "refresh_token",
601
+ "refresh_token": token.refresh_token,
602
+ },
603
+ headers=basic_header(oauth),
604
+ )
540
605
 
541
606
  assert200(response)
542
- assert response.content_type == 'application/json'
543
- assert 'access_token' in response.json
607
+ assert response.content_type == "application/json"
608
+ assert "access_token" in response.json
544
609
 
545
- @pytest.mark.parametrize('token_type', ['access_token', 'refresh_token'])
610
+ @pytest.mark.parametrize("token_type", ["access_token", "refresh_token"])
546
611
  def test_revoke_token(self, client, oauth, token_type):
547
612
  user = UserFactory()
548
613
  token = OAuth2Token.objects.create(
549
614
  client=oauth,
550
615
  user=user,
551
- access_token='access-token',
552
- refresh_token='refresh-token',
616
+ access_token="access-token",
617
+ refresh_token="refresh-token",
618
+ )
619
+ response = client.post(
620
+ url_for("oauth.revoke_token"),
621
+ {
622
+ "token": getattr(token, token_type),
623
+ },
624
+ headers=basic_header(oauth),
553
625
  )
554
- response = client.post(url_for('oauth.revoke_token'), {
555
- 'token': getattr(token, token_type),
556
- }, headers=basic_header(oauth))
557
626
 
558
627
  assert200(response)
559
628
 
560
629
  tok = OAuth2Token.objects(pk=token.pk).first()
561
630
  assert tok.revoked is True
562
631
 
563
- @pytest.mark.parametrize('token_type', ['access_token', 'refresh_token'])
632
+ @pytest.mark.parametrize("token_type", ["access_token", "refresh_token"])
564
633
  def test_revoke_token_with_hint(self, client, oauth, token_type):
565
634
  user = UserFactory()
566
635
  token = OAuth2Token.objects.create(
567
636
  client=oauth,
568
637
  user=user,
569
- access_token='access-token',
570
- refresh_token='refresh-token',
638
+ access_token="access-token",
639
+ refresh_token="refresh-token",
640
+ )
641
+ response = client.post(
642
+ url_for("oauth.revoke_token"),
643
+ {"token": getattr(token, token_type), "token_type_hint": token_type},
644
+ headers=basic_header(oauth),
571
645
  )
572
- response = client.post(url_for('oauth.revoke_token'), {
573
- 'token': getattr(token, token_type),
574
- 'token_type_hint': token_type
575
- }, headers=basic_header(oauth))
576
646
  assert200(response)
577
647
 
578
648
  tok = OAuth2Token.objects(pk=token.pk).first()
@@ -583,37 +653,41 @@ class APIAuthTest:
583
653
  token = OAuth2Token.objects.create(
584
654
  client=oauth,
585
655
  user=user,
586
- access_token='access-token',
587
- refresh_token='refresh-token',
656
+ access_token="access-token",
657
+ refresh_token="refresh-token",
588
658
  )
589
659
 
590
- response = client.post(url_for('oauth.revoke_token'), {
591
- 'token': token.access_token,
592
- 'token_type_hint': 'refresh_token',
593
- }, headers=basic_header(oauth))
660
+ response = client.post(
661
+ url_for("oauth.revoke_token"),
662
+ {
663
+ "token": token.access_token,
664
+ "token_type_hint": "refresh_token",
665
+ },
666
+ headers=basic_header(oauth),
667
+ )
594
668
  assert200(response)
595
669
 
596
670
  tok = OAuth2Token.objects(pk=token.pk).first()
597
671
  assert tok.revoked is False
598
672
 
599
673
  def test_value_error(self, api):
600
- @ns.route('/exception', endpoint='exception')
674
+ @ns.route("/exception", endpoint="exception")
601
675
  class ExceptionAPI(API):
602
676
  def get(self):
603
- raise ValueError('Not working')
677
+ raise ValueError("Not working")
604
678
 
605
- response = api.get(url_for('api.exception'))
679
+ response = api.get(url_for("api.exception"))
606
680
 
607
681
  assert400(response)
608
- assert response.json['message'] == 'Not working'
682
+ assert response.json["message"] == "Not working"
609
683
 
610
684
  def test_permission_denied(self, api):
611
- @ns.route('/exception', endpoint='exception')
685
+ @ns.route("/exception", endpoint="exception")
612
686
  class ExceptionAPI(API):
613
687
  def get(self):
614
- raise PermissionDenied('Permission denied')
688
+ raise PermissionDenied("Permission denied")
615
689
 
616
- response = api.get(url_for('api.exception'))
690
+ response = api.get(url_for("api.exception"))
617
691
 
618
692
  assert403(response)
619
- assert 'message' in response.json
693
+ assert "message" in response.json