c2cgeoportal-geoportal 2.6.0__py2.py3-none-any.whl → 2.8.1.180__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.
Files changed (225) hide show
  1. c2cgeoportal_geoportal/__init__.py +245 -95
  2. c2cgeoportal_geoportal/lib/__init__.py +67 -43
  3. c2cgeoportal_geoportal/lib/authentication.py +50 -26
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
  6. c2cgeoportal_geoportal/lib/caching.py +65 -193
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +67 -65
  9. c2cgeoportal_geoportal/lib/common_headers.py +167 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +61 -46
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +126 -88
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
  13. c2cgeoportal_geoportal/lib/functionality.py +20 -17
  14. c2cgeoportal_geoportal/lib/headers.py +14 -5
  15. c2cgeoportal_geoportal/lib/i18n.py +4 -4
  16. c2cgeoportal_geoportal/lib/layers.py +30 -11
  17. c2cgeoportal_geoportal/lib/lingua_extractor.py +363 -240
  18. c2cgeoportal_geoportal/lib/loader.py +11 -16
  19. c2cgeoportal_geoportal/lib/metrics.py +28 -17
  20. c2cgeoportal_geoportal/lib/oauth2.py +392 -206
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +105 -84
  22. c2cgeoportal_geoportal/lib/xsd.py +26 -16
  23. c2cgeoportal_geoportal/resources.py +15 -9
  24. c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
  25. c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
  26. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
  27. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
  28. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
  29. c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +22 -15
  30. c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
  31. c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
  32. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +100 -0
  33. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
  34. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  35. c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +77 -0
  38. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +29 -28
  39. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +4 -7
  40. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
  41. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
  42. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  43. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  46. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  47. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js} +1 -2
  48. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js} +4 -4
  49. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
  50. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  51. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -27
  52. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  53. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  54. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
  55. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +57 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  57. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +76 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  64. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +186 -0
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +26 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +165 -22
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +23 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
  72. c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +20 -15
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +101 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +126 -36
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +3 -3
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +9 -18
  81. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +13 -8
  82. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +13 -8
  83. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +13 -8
  84. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +13 -8
  85. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +11 -4
  86. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  87. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  88. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  89. c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +4 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  91. c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +3 -5
  92. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +38 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  99. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  100. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +61 -0
  101. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +273 -0
  102. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
  103. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +64 -17
  104. c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +396 -19
  105. c2cgeoportal_geoportal/scripts/__init__.py +16 -30
  106. c2cgeoportal_geoportal/scripts/c2cupgrade.py +272 -234
  107. c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
  108. c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
  109. c2cgeoportal_geoportal/scripts/pcreate.py +310 -0
  110. c2cgeoportal_geoportal/scripts/theme2fts.py +128 -24
  111. c2cgeoportal_geoportal/scripts/urllogin.py +19 -11
  112. c2cgeoportal_geoportal/templates/login.html +88 -84
  113. c2cgeoportal_geoportal/templates/notlogin.html +59 -59
  114. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  115. c2cgeoportal_geoportal/views/__init__.py +23 -6
  116. c2cgeoportal_geoportal/views/dev.py +9 -7
  117. c2cgeoportal_geoportal/views/dynamic.py +56 -19
  118. c2cgeoportal_geoportal/views/entry.py +85 -24
  119. c2cgeoportal_geoportal/views/fulltextsearch.py +29 -23
  120. c2cgeoportal_geoportal/views/geometry_processing.py +17 -9
  121. c2cgeoportal_geoportal/views/i18n.py +91 -9
  122. c2cgeoportal_geoportal/views/layers.py +166 -133
  123. c2cgeoportal_geoportal/views/login.py +161 -93
  124. c2cgeoportal_geoportal/views/mapserverproxy.py +47 -31
  125. c2cgeoportal_geoportal/views/memory.py +12 -12
  126. c2cgeoportal_geoportal/views/ogcproxy.py +52 -30
  127. c2cgeoportal_geoportal/views/pdfreport.py +30 -26
  128. c2cgeoportal_geoportal/views/printproxy.py +60 -52
  129. c2cgeoportal_geoportal/views/profile.py +24 -23
  130. c2cgeoportal_geoportal/views/proxy.py +88 -72
  131. c2cgeoportal_geoportal/views/raster.py +37 -26
  132. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  133. c2cgeoportal_geoportal/views/shortener.py +27 -25
  134. c2cgeoportal_geoportal/views/theme.py +472 -332
  135. c2cgeoportal_geoportal/views/tinyowsproxy.py +42 -44
  136. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  137. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/METADATA +19 -8
  138. c2cgeoportal_geoportal-2.8.1.180.dist-info/RECORD +191 -0
  139. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/WHEEL +1 -1
  140. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/entry_points.txt +3 -0
  141. tests/__init__.py +10 -5
  142. tests/test_cachebuster.py +3 -5
  143. tests/test_caching.py +18 -26
  144. tests/test_checker.py +1 -3
  145. tests/test_decimaljson.py +5 -5
  146. tests/test_headerstween.py +1 -3
  147. tests/test_i18n.py +2 -2
  148. tests/test_init.py +16 -20
  149. tests/test_locale_negociator.py +4 -6
  150. tests/test_mapserverproxy_route_predicate.py +1 -4
  151. tests/test_raster.py +15 -17
  152. tests/test_wmstparsing.py +10 -12
  153. tests/xmlstr.py +1 -3
  154. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
  155. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
  156. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
  157. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
  158. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
  159. c2cgeoportal_geoportal/scaffolds/create/Dockerfile_tmpl +0 -67
  160. c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
  161. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
  162. c2cgeoportal_geoportal/scaffolds/create/ci/config.yaml_tmpl +0 -23
  163. c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
  164. c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
  165. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
  166. c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -67
  167. c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -48
  168. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
  169. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
  170. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -58
  171. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  172. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  173. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  174. c2cgeoportal_geoportal/scaffolds/create/geoportal/tools/extract-messages.js +0 -39
  175. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  176. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
  177. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  178. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  179. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
  180. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
  181. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  182. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
  183. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  184. c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -181
  185. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
  186. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  187. c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
  188. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  189. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  190. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
  191. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  192. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  193. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/requirements.txt +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  195. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/locale/en/LC_MESSAGES/+package+_geoportal-client.po → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/locale/en/LC_MESSAGES/{{cookiecutter.package}}_geoportal-client.po} +0 -0
  196. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  198. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  199. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  212. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  213. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  214. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  215. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  216. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  217. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  218. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  219. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  220. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  221. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
  222. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  223. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  224. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  225. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2021, Camptocamp SA
1
+ # Copyright (c) 2021-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -29,24 +27,29 @@
29
27
 
30
28
  import logging
31
29
  from datetime import datetime, timedelta
32
- from typing import Any, Dict, List, Union
30
+ from typing import Any, Dict, List, Optional, Union
33
31
 
32
+ import basicauth
34
33
  import oauthlib.common
35
34
  import oauthlib.oauth2
36
35
  import pyramid.threadlocal
36
+ from pyramid.httpexceptions import HTTPBadRequest
37
37
 
38
+ import c2cgeoportal_commons
38
39
  from c2cgeoportal_geoportal.lib.caching import get_region
39
40
 
40
41
  LOG = logging.getLogger(__name__)
41
42
  OBJECT_CACHE_REGION = get_region("obj")
42
43
 
43
44
 
44
- class RequestValidator(oauthlib.oauth2.RequestValidator):
45
- def __init__(self, authorization_expires_in):
45
+ class RequestValidator(oauthlib.oauth2.RequestValidator): # type: ignore
46
+ """The oauth2 request validator implementation."""
47
+
48
+ def __init__(self, authorization_expires_in: int) -> None:
46
49
  # in minutes
47
50
  self.authorization_expires_in = authorization_expires_in
48
51
 
49
- def authenticate_client( # pylint: disable=no-self-use,useless-suppression
52
+ def authenticate_client(
50
53
  self,
51
54
  request: oauthlib.common.Request,
52
55
  *args: Any,
@@ -63,8 +66,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
63
66
  both body and query can be obtained by direct attribute access, i.e.
64
67
  request.client_id for client_id in the URL query.
65
68
 
66
- :param request: oauthlib.common.Request
67
- :rtype: True or False
69
+ Keyword Arguments:
70
+
71
+ request: oauthlib.common.Request
72
+
73
+ Returns: True or False
68
74
 
69
75
  Method is used by:
70
76
  - Authorization Code Grant
@@ -72,25 +78,15 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
72
78
  - Client Credentials Grant
73
79
  - Refresh Token Grant
74
80
 
75
- .. _`HTTP Basic Authentication Scheme`: http://tools.ietf.org/html/rfc1945#section-11.1
81
+ .. _`HTTP Basic Authentication Scheme`: https://tools.ietf.org/html/rfc1945#section-11.1
76
82
  """
77
83
  del args, kwargs
78
84
 
79
- LOG.debug("authenticate_client")
80
-
81
- from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
82
-
83
- params = dict(request.decoded_body)
84
-
85
- request.client = (
86
- DBSession.query(static.OAuth2Client)
87
- .filter(static.OAuth2Client.client_id == params["client_id"])
88
- .one_or_none()
89
- )
85
+ LOG.debug("authenticate_client => unimplemented")
90
86
 
91
- return request.client is not None
87
+ raise NotImplementedError("Not implemented, the method `authenticate_client_id` should be used.")
92
88
 
93
- def authenticate_client_id( # pylint: disable=no-self-use,useless-suppression
89
+ def authenticate_client_id(
94
90
  self,
95
91
  client_id: str,
96
92
  request: oauthlib.common.Request,
@@ -107,9 +103,6 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
107
103
  to set request.client to the client object associated with the
108
104
  given client_id.
109
105
 
110
- :param request: oauthlib.common.Request
111
- :rtype: True or False
112
-
113
106
  Method is used by:
114
107
  - Authorization Code Grant
115
108
  """
@@ -121,16 +114,27 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
121
114
 
122
115
  params = dict(request.decoded_body)
123
116
 
117
+ if "client_secret" in params:
118
+ client_secret = params["client_secret"]
119
+ elif "Authorization" in request.headers:
120
+ username, password = basicauth.decode(request.headers["Authorization"])
121
+ assert client_id == username
122
+ client_secret = password
123
+ else:
124
+ # Unable to get the client secret
125
+ return False
126
+
124
127
  request.client = (
125
128
  DBSession.query(static.OAuth2Client)
126
129
  .filter(static.OAuth2Client.client_id == client_id)
127
- .filter(static.OAuth2Client.secret == params["client_secret"])
130
+ .filter(static.OAuth2Client.secret == client_secret)
128
131
  .one_or_none()
129
132
  )
130
133
 
134
+ LOG.debug("authenticate_client_id => %s", request.client is not None)
131
135
  return request.client is not None
132
136
 
133
- def client_authentication_required( # pylint: disable=no-self-use,useless-suppression
137
+ def client_authentication_required(
134
138
  self,
135
139
  request: oauthlib.common.Request,
136
140
  *args: Any,
@@ -150,25 +154,28 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
150
154
  client credentials or whenever Client provided client authentication, see
151
155
  `Section 6`_
152
156
 
153
- :param request: oauthlib.common.Request
154
- :rtype: True or False
157
+ Keyword Arguments:
158
+
159
+ request: oauthlib.common.Request
160
+
161
+ Returns: True or False
155
162
 
156
163
  Method is used by:
157
164
  - Authorization Code Grant
158
165
  - Resource Owner Password Credentials Grant
159
166
  - Refresh Token Grant
160
167
 
161
- .. _`Section 4.3.2`: http://tools.ietf.org/html/rfc6749#section-4.3.2
162
- .. _`Section 4.1.3`: http://tools.ietf.org/html/rfc6749#section-4.1.3
163
- .. _`Section 6`: http://tools.ietf.org/html/rfc6749#section-6
168
+ .. _`Section 4.3.2`: https://tools.ietf.org/html/rfc6749#section-4.3.2
169
+ .. _`Section 4.1.3`: https://tools.ietf.org/html/rfc6749#section-4.1.3
170
+ .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6
164
171
  """
165
172
  del request, args, kwargs
166
173
 
167
- LOG.debug("client_authentication_required")
174
+ LOG.debug("client_authentication_required => False")
168
175
 
169
- return True
176
+ return False
170
177
 
171
- def confirm_redirect_uri( # pylint: disable=no-self-use,useless-suppression
178
+ def confirm_redirect_uri(
172
179
  self,
173
180
  client_id: str,
174
181
  code: str,
@@ -178,8 +185,10 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
178
185
  **kwargs: Any,
179
186
  ) -> bool:
180
187
  """
181
- Ensure that the authorization process represented by this authorization
182
- code began with this 'redirect_uri'.
188
+ Ensure that the authorization process is correct.
189
+
190
+ Ensure that the authorization process represented by this authorization code began with this
191
+ ``redirect_uri``.
183
192
 
184
193
  If the client specifies a redirect_uri when obtaining code then that
185
194
  redirect URI must be bound to the code and verified equal in this
@@ -187,12 +196,15 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
187
196
  the client's allowed redirect URIs, but against the URI used when the
188
197
  code was saved.
189
198
 
190
- :param client_id: Unicode client identifier
191
- :param code: Unicode authorization_code.
192
- :param redirect_uri: Unicode absolute URI
193
- :param client: Client object set by you, see authenticate_client.
194
- :param request: The HTTP Request (oauthlib.common.Request)
195
- :rtype: True or False
199
+ Keyword Arguments:
200
+
201
+ client_id: Unicode client identifier
202
+ code: Unicode authorization_code.
203
+ redirect_uri: Unicode absolute URI
204
+ client: Client object set by you, see authenticate_client.
205
+ request: The HTTP Request
206
+
207
+ Returns: True or False
196
208
 
197
209
  Method is used by:
198
210
  - Authorization Code Grant (during token request)
@@ -212,30 +224,10 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
212
224
  .filter(static.OAuth2AuthorizationCode.expire_at > datetime.now())
213
225
  .one_or_none()
214
226
  )
227
+ LOG.debug("confirm_redirect_uri => %s", authorization_code is not None)
215
228
  return authorization_code is not None
216
229
 
217
- def get_code_challenge_method( # pylint: disable=no-self-use,useless-suppression
218
- self, code: str, request: oauthlib.common.Request
219
- ) -> None:
220
- """Is called during the "token" request processing, when a
221
- ``code_verifier`` and a ``code_challenge`` has been provided.
222
- See ``.get_code_challenge``.
223
- Must return ``plain`` or ``S256``. You can return a custom value if you have
224
- implemented your own ``AuthorizationCodeGrant`` class.
225
- :param code: Authorization code.
226
- :param request: OAuthlib request.
227
- :type request: oauthlib.common.Request
228
- :rtype: code_challenge_method string
229
- Method is used by:
230
- - Authorization Code Grant - when PKCE is active
231
- """
232
- del code, request
233
-
234
- LOG.debug("get_code_challenge_method")
235
-
236
- raise NotImplementedError("Not implemented.")
237
-
238
- def get_default_redirect_uri( # pylint: disable=no-self-use,useless-suppression
230
+ def get_default_redirect_uri(
239
231
  self,
240
232
  client_id: str,
241
233
  request: oauthlib.common.Request,
@@ -245,9 +237,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
245
237
  """
246
238
  Get the default redirect URI for the client.
247
239
 
248
- :param client_id: Unicode client identifier
249
- :param request: The HTTP Request (oauthlib.common.Request)
250
- :rtype: The default redirect URI for the client
240
+ Keyword Arguments:
241
+
242
+ client_id: Unicode client identifier
243
+ request: The HTTP Request
244
+
245
+ Returns: The default redirect URI for the client
251
246
 
252
247
  Method is used by:
253
248
  - Authorization Code Grant
@@ -259,7 +254,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
259
254
 
260
255
  raise NotImplementedError("Not implemented.")
261
256
 
262
- def get_default_scopes( # pylint: disable=no-self-use,useless-suppression
257
+ def get_default_scopes(
263
258
  self,
264
259
  client_id: str,
265
260
  request: oauthlib.common.Request,
@@ -269,9 +264,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
269
264
  """
270
265
  Get the default scopes for the client.
271
266
 
272
- :param client_id: Unicode client identifier
273
- :param request: The HTTP Request (oauthlib.common.Request)
274
- :rtype: List of default scopes
267
+ Keyword Arguments:
268
+
269
+ client_id: Unicode client identifier
270
+ request: The HTTP Request
271
+
272
+ Returns: List of default scopes
275
273
 
276
274
  Method is used by all core grant types:
277
275
  - Authorization Code Grant
@@ -285,7 +283,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
285
283
 
286
284
  return ["geomapfish"]
287
285
 
288
- def get_original_scopes( # pylint: disable=no-self-use,useless-suppression
286
+ def get_original_scopes(
289
287
  self,
290
288
  refresh_token: str,
291
289
  request: oauthlib.common.Request,
@@ -295,9 +293,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
295
293
  """
296
294
  Get the list of scopes associated with the refresh token.
297
295
 
298
- :param refresh_token: Unicode refresh token
299
- :param request: The HTTP Request (oauthlib.common.Request)
300
- :rtype: List of scopes.
296
+ Keyword Arguments:
297
+
298
+ refresh_token: Unicode refresh token
299
+ request: The HTTP Request
300
+
301
+ Returns: List of scopes.
301
302
 
302
303
  Method is used by:
303
304
  - Refresh token grant
@@ -308,7 +309,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
308
309
 
309
310
  return []
310
311
 
311
- def introspect_token( # pylint: disable=no-self-use,useless-suppression
312
+ def introspect_token(
312
313
  self,
313
314
  token: str,
314
315
  token_type_hint: str,
@@ -316,11 +317,13 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
316
317
  *args: Any,
317
318
  **kwargs: Any,
318
319
  ) -> None:
319
- """Introspect an access or refresh token.
320
- Called once the introspect request is validated. This method should
321
- verify the *token* and either return a dictionary with the list of
322
- claims associated, or `None` in case the token is unknown.
323
- Below the list of registered claims you should be interested in:
320
+ """
321
+ Introspect an access or refresh token.
322
+
323
+ Called once the introspect request is validated. This method
324
+ should verify the *token* and either return a dictionary with the list of claims associated, or `None`
325
+ in case the token is unknown. Below the list of registered claims you should be interested in:
326
+
324
327
  - scope : space-separated list of scopes
325
328
  - client_id : client identifier
326
329
  - username : human-readable identifier for the resource owner
@@ -337,12 +340,16 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
337
340
  The implementation can use *token_type_hint* to improve lookup
338
341
  efficiency, but must fallback to other types to be compliant with RFC.
339
342
  The dict of claims is added to request.token after this method.
340
- :param token: The token string.
341
- :param token_type_hint: access_token or refresh_token.
342
- :param request: OAuthlib request.
343
- :type request: oauthlib.common.Request
343
+
344
+ Keyword Arguments:
345
+
346
+ token: The token string.
347
+ token_type_hint: access_token or refresh_token.
348
+ request: OAuthlib request.
349
+
344
350
  Method is used by:
345
351
  - Introspect Endpoint (all grants are compatible)
352
+
346
353
  .. _`Introspect Claims`: https://tools.ietf.org/html/rfc7662#section-2.2
347
354
  .. _`JWT Claims`: https://tools.ietf.org/html/rfc7519#section-4
348
355
  """
@@ -352,7 +359,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
352
359
 
353
360
  raise NotImplementedError("Not implemented.")
354
361
 
355
- def invalidate_authorization_code( # pylint: disable=no-self-use,useless-suppression
362
+ def invalidate_authorization_code(
356
363
  self,
357
364
  client_id: str,
358
365
  code: str,
@@ -363,9 +370,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
363
370
  """
364
371
  Invalidate an authorization code after use.
365
372
 
366
- :param client_id: Unicode client identifier
367
- :param code: The authorization code grant (request.code).
368
- :param request: The HTTP Request (oauthlib.common.Request)
373
+ Keyword Arguments:
374
+
375
+ client_id: Unicode client identifier
376
+ code: The authorization code grant (request.code).
377
+ request: The HTTP Request
369
378
 
370
379
  Method is used by:
371
380
  - Authorization Code Grant
@@ -385,7 +394,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
385
394
  .one()
386
395
  )
387
396
 
388
- def is_within_original_scope( # pylint: disable=no-self-use,useless-suppression
397
+ def is_within_original_scope(
389
398
  self,
390
399
  request_scopes: List[str],
391
400
  refresh_token: str,
@@ -404,10 +413,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
404
413
  used in situations where returning all valid scopes from the
405
414
  get_original_scopes is not practical.
406
415
 
407
- :param request_scopes: A list of scopes that were requested by client
408
- :param refresh_token: Unicode refresh_token
409
- :param request: The HTTP Request (oauthlib.common.Request)
410
- :rtype: True or False
416
+ Keyword Arguments:
417
+
418
+ request_scopes: A list of scopes that were requested by client
419
+ refresh_token: Unicode refresh_token
420
+ request: The HTTP Request
411
421
 
412
422
  Method is used by:
413
423
  - Refresh token grant
@@ -418,7 +428,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
418
428
 
419
429
  return False
420
430
 
421
- def revoke_token( # pylint: disable=no-self-use,useless-suppression
431
+ def revoke_token(
422
432
  self,
423
433
  token: str,
424
434
  token_type_hint: str,
@@ -429,9 +439,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
429
439
  """
430
440
  Revoke an access or refresh token.
431
441
 
432
- :param token: The token string.
433
- :param token_type_hint: access_token or refresh_token.
434
- :param request: The HTTP Request (oauthlib.common.Request)
442
+ Keyword Arguments:
443
+
444
+ token: The token string.
445
+ token_type_hint: access_token or refresh_token.
446
+ request: The HTTP Request
435
447
 
436
448
  Method is used by:
437
449
  - Revocation Endpoint
@@ -442,9 +454,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
442
454
 
443
455
  raise NotImplementedError("Not implemented.")
444
456
 
445
- def rotate_refresh_token( # pylint: disable=no-self-use,useless-suppression
446
- self, request: oauthlib.common.Request
447
- ) -> bool:
457
+ def rotate_refresh_token(self, request: oauthlib.common.Request) -> bool:
448
458
  """
449
459
  Determine whether to rotate the refresh token. Default, yes.
450
460
 
@@ -452,8 +462,9 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
452
462
  or replaced with a new one (rotated). Return True to rotate and
453
463
  and False for keeping original.
454
464
 
455
- :param request: oauthlib.common.Request
456
- :rtype: True or False
465
+ Keyword Arguments:
466
+
467
+ request: oauthlib.common.Request
457
468
 
458
469
  Method is used by:
459
470
  - Refresh Token Grant
@@ -464,7 +475,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
464
475
 
465
476
  return True
466
477
 
467
- def save_authorization_code( # pylint: disable=no-self-use,useless-suppression
478
+ def save_authorization_code(
468
479
  self,
469
480
  client_id: str,
470
481
  code: Dict[str, str],
@@ -485,18 +496,25 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
485
496
  The 'code' argument is actually a dictionary, containing at least a
486
497
  'code' key with the actual authorization code:
487
498
 
488
- {'code': 'sdf345jsdf0934f'}
499
+ {'code': '<secret>'}
489
500
 
490
501
  It may also have a 'state' key containing a nonce for the client, if it
491
502
  chose to send one. That value should be saved and used in
492
503
  'validate_code'.
493
504
 
494
- :param client_id: Unicode client identifier
495
- :param code: A dict of the authorization code grant and, optionally, state.
496
- :param request: The HTTP Request (oauthlib.common.Request)
505
+ Keyword Arguments:
506
+
507
+ client_id: Unicode client identifier
508
+ code: A dict of the authorization code grant and, optionally, state.
509
+ request: The HTTP Request
497
510
 
498
511
  Method is used by:
499
512
  - Authorization Code Grant
513
+
514
+ To support PKCE, you MUST associate the code with:
515
+
516
+ Code Challenge (request.code_challenge) and
517
+ Code Challenge Method (request.code_challenge_method)
500
518
  """
501
519
  del args, kwargs
502
520
 
@@ -504,7 +522,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
504
522
 
505
523
  from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
506
524
 
507
- user = pyramid.threadlocal.get_current_request().user_
525
+ user = pyramid.threadlocal.get_current_request().user
508
526
 
509
527
  # Don't allows to have two authentications for the same user and the same client
510
528
  authorization_code = (
@@ -515,20 +533,32 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
515
533
  )
516
534
 
517
535
  if authorization_code is not None:
518
- authorization_code.code = code["code"]
519
536
  authorization_code.expire_at = datetime.now() + timedelta(minutes=self.authorization_expires_in)
520
- authorization_code.redirect_uri = request.redirect_uri
521
537
  else:
522
538
  authorization_code = static.OAuth2AuthorizationCode()
523
539
  authorization_code.client_id = request.client.id
524
- authorization_code.code = code["code"]
525
540
  authorization_code.user_id = user.id
526
541
  authorization_code.expire_at = datetime.now() + timedelta(minutes=self.authorization_expires_in)
527
- authorization_code.redirect_uri = request.redirect_uri
542
+ authorization_code.state = code.get("state")
543
+
544
+ authorization_code.code = code["code"]
545
+ authorization_code.redirect_uri = request.redirect_uri
546
+
547
+ client = (
548
+ DBSession.query(static.OAuth2Client)
549
+ .filter(static.OAuth2Client.client_id == client_id)
550
+ .one_or_none()
551
+ )
552
+ if client and client.state_required and not code.get("state"):
553
+ raise HTTPBadRequest("Client is missing the state parameter.")
554
+
555
+ if client and client.pkce_required:
556
+ authorization_code.challenge = request.code_challenge
557
+ authorization_code.challenge_method = request.code_challenge_method
528
558
 
529
559
  DBSession.add(authorization_code)
530
560
 
531
- def save_bearer_token( # pylint: disable=no-self-use,useless-suppression
561
+ def save_bearer_token(
532
562
  self,
533
563
  token: Dict[str, Union[str, int]],
534
564
  request: oauthlib.common.Request,
@@ -549,20 +579,23 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
549
579
 
550
580
  {
551
581
  'token_type': 'Bearer',
552
- 'access_token': 'askfjh234as9sd8',
582
+ 'access_token': '<secret>',
553
583
  'expires_in': 3600,
554
584
  'scope': 'string of space separated authorized scopes',
555
- 'refresh_token': '23sdf876234', # if issued
585
+ 'refresh_token': '<secret>', # if issued
556
586
  'state': 'given_by_client', # if supplied by client
557
587
  }
558
588
 
559
589
  Note that while "scope" is a string-separated list of authorized scopes,
560
590
  the original list is still available in request.scopes
561
591
 
562
- :param client_id: Unicode client identifier
563
- :param token: A Bearer token dict
564
- :param request: The HTTP Request (oauthlib.common.Request)
565
- :rtype: The default redirect URI for the client
592
+ Keyword Arguments:
593
+
594
+ client_id: Unicode client identifier
595
+ token: A Bearer token dict
596
+ request: The HTTP Request
597
+
598
+ Returns: The default redirect URI for the client
566
599
 
567
600
  Method is used by all core grant types issuing Bearer tokens:
568
601
  - Authorization Code Grant
@@ -584,21 +617,18 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
584
617
  .one_or_none()
585
618
  )
586
619
 
587
- if bearer_token is not None:
588
- bearer_token.access_token = token["access_token"]
589
- bearer_token.refresh_token = token["refresh_token"]
590
- bearer_token.expire_at = datetime.now() + timedelta(seconds=float(token["expires_in"]))
591
- else:
620
+ if bearer_token is None:
592
621
  bearer_token = static.OAuth2BearerToken()
593
622
  bearer_token.client_id = request.client.id
594
623
  bearer_token.user_id = request.user.id
595
- bearer_token.access_token = token["access_token"]
596
- bearer_token.refresh_token = token["refresh_token"]
597
- bearer_token.expire_at = datetime.now() + timedelta(seconds=float(token["expires_in"]))
598
-
599
624
  DBSession.add(bearer_token)
600
625
 
601
- def validate_bearer_token( # pylint: disable=no-self-use,useless-suppression
626
+ bearer_token.access_token = token["access_token"]
627
+ bearer_token.refresh_token = token["refresh_token"]
628
+ bearer_token.expire_at = datetime.now() + timedelta(seconds=float(token["expires_in"]))
629
+ bearer_token.state = token.get("state")
630
+
631
+ def validate_bearer_token(
602
632
  self,
603
633
  token: str,
604
634
  scopes: List[str],
@@ -607,9 +637,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
607
637
  """
608
638
  Ensure the Bearer token is valid and authorized access to scopes.
609
639
 
610
- :param token: A string of random characters.
611
- :param scopes: A list of scopes associated with the protected resource.
612
- :param request: The HTTP Request (oauthlib.common.Request)
640
+ Keyword Arguments:
641
+
642
+ token: A string of random characters.
643
+ scopes: A list of scopes associated with the protected resource.
644
+ request: The HTTP Request
613
645
 
614
646
  A key to OAuth 2 security and restricting impact of leaked tokens is
615
647
  the short expiration time of tokens, *always ensure the token has not
@@ -641,10 +673,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
641
673
  one provided for django these attributes will be made available
642
674
  in all protected views as keyword arguments.
643
675
 
644
- :param token: Unicode Bearer token
645
- :param scopes: List of scopes (defined by you)
646
- :param request: The HTTP Request (oauthlib.common.Request)
647
- :rtype: True or False
676
+ Keyword Arguments:
677
+
678
+ token: Unicode Bearer token
679
+ scopes: List of scopes (defined by you)
680
+ request: The HTTP Request
648
681
 
649
682
  Method is indirectly used by all core Bearer token issuing grant types:
650
683
  - Authorization Code Grant
@@ -659,6 +692,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
659
692
 
660
693
  bearer_token = (
661
694
  DBSession.query(static.OAuth2BearerToken)
695
+ .join(static.User)
662
696
  .filter(static.OAuth2BearerToken.access_token == token)
663
697
  .filter(static.OAuth2BearerToken.expire_at > datetime.now())
664
698
  ).one_or_none()
@@ -666,9 +700,10 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
666
700
  if bearer_token is not None:
667
701
  request.user = bearer_token.user
668
702
 
703
+ LOG.debug("validate_bearer_token => %s", bearer_token is not None)
669
704
  return bearer_token is not None
670
705
 
671
- def validate_client_id( # pylint: disable=no-self-use,useless-suppression
706
+ def validate_client_id(
672
707
  self,
673
708
  client_id: str,
674
709
  request: oauthlib.common.Request,
@@ -682,8 +717,10 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
682
717
  to set request.client to the client object associated with the
683
718
  given client_id.
684
719
 
685
- :param request: oauthlib.common.Request
686
- :rtype: True or False
720
+ Keyword Arguments:
721
+
722
+ client_id: Unicode client identifier
723
+ request: oauthlib.common.Request
687
724
 
688
725
  Method is used by:
689
726
  - Authorization Code Grant
@@ -704,7 +741,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
704
741
  request.client = client
705
742
  return client is not None
706
743
 
707
- def validate_code( # pylint: disable=no-self-use,useless-suppression
744
+ def validate_code(
708
745
  self,
709
746
  client_id: str,
710
747
  code: str,
@@ -714,8 +751,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
714
751
  **kwargs: Any,
715
752
  ) -> bool:
716
753
  """
717
- Verify that the authorization_code is valid and assigned to the given
718
- client.
754
+ Verify that the authorization_code is valid and assigned to the given client.
719
755
 
720
756
  Before returning true, set the following based on the information stored
721
757
  with the code in 'save_authorization_code':
@@ -727,11 +763,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
727
763
  associated with this authorization code. Similarly request.scopes
728
764
  must also be set.
729
765
 
730
- :param client_id: Unicode client identifier
731
- :param code: Unicode authorization code
732
- :param client: Client object set by you, see authenticate_client.
733
- :param request: The HTTP Request (oauthlib.common.Request)
734
- :rtype: True or False
766
+ Keyword Arguments:
767
+
768
+ client_id: Unicode client identifier
769
+ code: Unicode authorization code
770
+ client: Client object set by you, see authenticate_client.
771
+ request: The HTTP Request
735
772
 
736
773
  Method is used by:
737
774
  - Authorization Code Grant
@@ -742,23 +779,36 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
742
779
 
743
780
  from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
744
781
 
745
- authorization_code = (
782
+ authorization_code_query = (
746
783
  DBSession.query(static.OAuth2AuthorizationCode)
747
784
  .join(static.OAuth2AuthorizationCode.client)
748
785
  .filter(static.OAuth2AuthorizationCode.code == code)
749
- .filter(static.OAuth2Client.client_id == client_id)
786
+ .filter(static.OAuth2AuthorizationCode.client_id == client.id)
750
787
  .filter(static.OAuth2AuthorizationCode.expire_at > datetime.now())
751
- .one_or_none()
752
788
  )
753
- if authorization_code is not None:
754
- request.user = authorization_code.user
755
- return authorization_code is not None
789
+ if client.state_required:
790
+ authorization_code_query = authorization_code_query.filter(
791
+ static.OAuth2AuthorizationCode.state == request.state
792
+ )
793
+
794
+ authorization_code = authorization_code_query.one_or_none()
795
+ if authorization_code is None:
796
+ LOG.debug("validate_code => KO, no authorization_code found")
797
+ return False
798
+
799
+ if authorization_code.client.pkce_required:
800
+ request.code_challenge = authorization_code.challenge
801
+ request.code_challenge_method = authorization_code.challenge_method
802
+
803
+ request.user = authorization_code.user
804
+ LOG.debug("validate_code => OK")
805
+ return True
756
806
 
757
- def validate_grant_type( # pylint: disable=no-self-use,useless-suppression
807
+ def validate_grant_type(
758
808
  self,
759
809
  client_id: str,
760
810
  grant_type: str,
761
- client,
811
+ client: "c2cgeoportal_commons.models.static.OAuth2Client",
762
812
  request: oauthlib.common.Request,
763
813
  *args: Any,
764
814
  **kwargs: Any,
@@ -766,11 +816,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
766
816
  """
767
817
  Ensure client is authorized to use the grant_type requested.
768
818
 
769
- :param client_id: Unicode client identifier
770
- :param grant_type: Unicode grant type, i.e. authorization_code, password.
771
- :param client: Client object set by you, see authenticate_client.
772
- :param request: The HTTP Request (oauthlib.common.Request)
773
- :rtype: True or False
819
+ Keyword Arguments:
820
+
821
+ client_id: Unicode client identifier
822
+ grant_type: Unicode grant type, i.e. authorization_code, password.
823
+ client: Client object set by you, see authenticate_client.
824
+ request: The HTTP Request
774
825
 
775
826
  Method is used by:
776
827
  - Authorization Code Grant
@@ -778,13 +829,18 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
778
829
  - Client Credentials Grant
779
830
  - Refresh Token Grant
780
831
  """
781
- del request, args, kwargs
832
+ del client, request, args, kwargs
782
833
 
783
- LOG.debug("validate_grant_type %s %s", client_id, grant_type)
834
+ LOG.debug(
835
+ "validate_grant_type %s %s => %s",
836
+ client_id,
837
+ grant_type,
838
+ grant_type in ("authorization_code", "refresh_token"),
839
+ )
784
840
 
785
841
  return grant_type in ("authorization_code", "refresh_token")
786
842
 
787
- def validate_redirect_uri( # pylint: disable=no-self-use,useless-suppression
843
+ def validate_redirect_uri(
788
844
  self,
789
845
  client_id: str,
790
846
  redirect_uri: str,
@@ -798,10 +854,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
798
854
  All clients should register the absolute URIs of all URIs they intend
799
855
  to redirect to. The registration is outside of the scope of oauthlib.
800
856
 
801
- :param client_id: Unicode client identifier
802
- :param redirect_uri: Unicode absolute URI
803
- :param request: The HTTP Request (oauthlib.common.Request)
804
- :rtype: True or False
857
+ Keyword Arguments:
858
+
859
+ client_id: Unicode client identifier
860
+ redirect_uri: Unicode absolute URI
861
+ request: The HTTP Request
805
862
 
806
863
  Method is used by:
807
864
  - Authorization Code Grant
@@ -822,7 +879,7 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
822
879
  LOG.debug("validate_redirect_uri %s", client is not None)
823
880
  return client is not None
824
881
 
825
- def validate_refresh_token( # pylint: disable=no-self-use,useless-suppression
882
+ def validate_refresh_token(
826
883
  self,
827
884
  refresh_token: str,
828
885
  client: "c2cgeoportal_commons.models.static.OAuth2Client", # noqa: F821
@@ -836,10 +893,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
836
893
  OBS! The request.user attribute should be set to the resource owner
837
894
  associated with this refresh token.
838
895
 
839
- :param refresh_token: Unicode refresh token
840
- :param client: Client object set by you, see authenticate_client.
841
- :param request: The HTTP Request (oauthlib.common.Request)
842
- :rtype: True or False
896
+ Keyword Arguments:
897
+
898
+ refresh_token: Unicode refresh token
899
+ client: Client object set by you, see authenticate_client.
900
+ request: The HTTP Request
843
901
 
844
902
  Method is used by:
845
903
  - Authorization Code Grant (indirectly by issuing refresh tokens)
@@ -856,7 +914,6 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
856
914
  DBSession.query(static.OAuth2BearerToken)
857
915
  .filter(static.OAuth2BearerToken.refresh_token == refresh_token)
858
916
  .filter(static.OAuth2BearerToken.client_id == request.client.id)
859
- .filter(static.OAuth2BearerToken.expire_at > datetime.now())
860
917
  .one_or_none()
861
918
  )
862
919
 
@@ -865,11 +922,11 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
865
922
 
866
923
  return bearer_token is not None
867
924
 
868
- def validate_response_type( # pylint: disable=no-self-use,useless-suppression
925
+ def validate_response_type(
869
926
  self,
870
927
  client_id: str,
871
928
  response_type: str,
872
- client,
929
+ client: "c2cgeoportal_commons.models.static.OAuth2Client",
873
930
  request: oauthlib.common.Request,
874
931
  *args: Any,
875
932
  **kwargs: Any,
@@ -877,27 +934,28 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
877
934
  """
878
935
  Ensure client is authorized to use the response_type requested.
879
936
 
880
- :param client_id: Unicode client identifier
881
- :param response_type: Unicode response type, i.e. code, token.
882
- :param client: Client object set by you, see authenticate_client.
883
- :param request: The HTTP Request (oauthlib.common.Request)
884
- :rtype: True or False
937
+ Keyword Arguments:
938
+
939
+ client_id: Unicode client identifier
940
+ response_type: Unicode response type, i.e. code, token.
941
+ client: Client object set by you, see authenticate_client.
942
+ request: The HTTP Request
885
943
 
886
944
  Method is used by:
887
945
  - Authorization Code Grant
888
946
  - Implicit Grant
889
947
  """
890
- del request, args, kwargs
948
+ del client, request, args, kwargs
891
949
 
892
950
  LOG.debug("validate_response_type %s %s", client_id, response_type)
893
951
 
894
952
  return response_type == "code"
895
953
 
896
- def validate_scopes( # pylint: disable=no-self-use,useless-suppression
954
+ def validate_scopes(
897
955
  self,
898
956
  client_id: str,
899
957
  scopes: List[str],
900
- client,
958
+ client: "c2cgeoportal_commons.models.static.OAuth2Client",
901
959
  request: oauthlib.common.Request,
902
960
  *args: Any,
903
961
  **kwargs: Any,
@@ -905,11 +963,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
905
963
  """
906
964
  Ensure the client is authorized access to requested scopes.
907
965
 
908
- :param client_id: Unicode client identifier
909
- :param scopes: List of scopes (defined by you)
910
- :param client: Client object set by you, see authenticate_client.
911
- :param request: The HTTP Request (oauthlib.common.Request)
912
- :rtype: True or False
966
+ Keyword Arguments:
967
+
968
+ client_id: Unicode client identifier
969
+ scopes: List of scopes (defined by you)
970
+ client: Client object set by you, see authenticate_client.
971
+ request: The HTTP Request
913
972
 
914
973
  Method is used by all core grant types:
915
974
  - Authorization Code Grant
@@ -917,16 +976,16 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
917
976
  - Resource Owner Password Credentials Grant
918
977
  - Client Credentials Grant
919
978
  """
920
- del request, args, kwargs
979
+ del client, request, args, kwargs
921
980
 
922
981
  LOG.debug("validate_scopes %s %s", client_id, scopes)
923
982
 
924
983
  return True
925
984
 
926
- def validate_user( # pylint: disable=no-self-use,useless-suppression
985
+ def validate_user(
927
986
  self,
928
- username,
929
- password,
987
+ username: str,
988
+ password: str,
930
989
  client: "c2cgeoportal_commons.models.static.OAuth2Client", # noqa: F821
931
990
  request: oauthlib.common.Request,
932
991
  *args: Any,
@@ -940,11 +999,12 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
940
999
  not set you will be unable to associate a token with a user in the
941
1000
  persistence method used (commonly, save_bearer_token).
942
1001
 
943
- :param username: Unicode username
944
- :param password: Unicode password
945
- :param client: Client object set by you, see authenticate_client.
946
- :param request: The HTTP Request (oauthlib.common.Request)
947
- :rtype: True or False
1002
+ Keyword Arguments:
1003
+
1004
+ username: Unicode username
1005
+ password: Unicode password
1006
+ client: Client object set by you, see authenticate_client.
1007
+ request: The HTTP Request
948
1008
 
949
1009
  Method is used by:
950
1010
  - Resource Owner Password Credentials Grant
@@ -955,13 +1015,139 @@ class RequestValidator(oauthlib.oauth2.RequestValidator):
955
1015
 
956
1016
  raise NotImplementedError("Not implemented.")
957
1017
 
1018
+ def is_pkce_required(self, client_id: int, request: oauthlib.common.Request) -> bool:
1019
+ """
1020
+ Determine if current request requires PKCE.
1021
+
1022
+ Default, False. This is called for both “authorization” and “token” requests.
1023
+
1024
+ Override this method by return True to enable PKCE for everyone. You might want to enable it only
1025
+ for public clients. Note that PKCE can also be used in addition of a client authentication.
1026
+
1027
+ OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to
1028
+ the authorization code interception attack. This specification describes the attack as well as
1029
+ a technique to mitigate against the threat through the use of Proof Key for Code Exchange
1030
+ (PKCE, pronounced “pixy”). See RFC7636.
1031
+
1032
+ Keyword Arguments:
1033
+
1034
+ client_id: Client identifier.
1035
+ request (oauthlib.common.Request): OAuthlib request.
1036
+
1037
+
1038
+ Method is used by:
1039
+
1040
+ Authorization Code Grant
1041
+
1042
+
1043
+ """
1044
+ from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
1045
+
1046
+ client = (
1047
+ DBSession.query(static.OAuth2Client)
1048
+ .filter(static.OAuth2Client.client_id == client_id)
1049
+ .one_or_none()
1050
+ )
1051
+
1052
+ return client and client.pkce_required # type: ignore
1053
+
1054
+ def get_code_challenge(self, code: str, request: oauthlib.common.Request) -> Optional[str]:
1055
+ """
1056
+ Is called for every “token” requests.
1057
+
1058
+ When the server issues the authorization code in the authorization response, it MUST associate the
1059
+ code_challenge and code_challenge_method values with the authorization code so it can be
1060
+ verified later.
1061
+
1062
+ Typically, the code_challenge and code_challenge_method values are stored in encrypted form in
1063
+ the code itself but could alternatively be stored on the server associated with the code.
1064
+ The server MUST NOT include the code_challenge value in client requests in a form that other
1065
+ entities can extract.
1066
+
1067
+ Return the code_challenge associated to the code. If None is returned, code is considered to not
1068
+ be associated to any challenges.
1069
+
1070
+ Keyword Arguments:
1071
+
1072
+ code: Authorization code.
1073
+ request: OAuthlib request.
1074
+
1075
+ Return:
958
1076
 
959
- @OBJECT_CACHE_REGION.cache_on_arguments()
960
- def get_oauth_client(settings):
1077
+ code_challenge string
1078
+
1079
+ Method is used by:
1080
+
1081
+ Authorization Code Grant - when PKCE is active
1082
+ """
1083
+ from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
1084
+
1085
+ LOG.debug("get_code_challenge")
1086
+
1087
+ authorization_code = (
1088
+ DBSession.query(static.OAuth2AuthorizationCode)
1089
+ .filter(static.OAuth2AuthorizationCode.code == code)
1090
+ .one_or_none()
1091
+ )
1092
+ if authorization_code:
1093
+ return authorization_code.challenge # type: ignore
1094
+ LOG.debug("get_code_challenge authorization_code not found")
1095
+ return None
1096
+
1097
+ def get_code_challenge_method(self, code: str, request: oauthlib.common.Request) -> Optional[str]:
1098
+ """
1099
+ Is called during the “token” request processing.
1100
+
1101
+ When a code_verifier and a code_challenge has been provided.
1102
+
1103
+ See .get_code_challenge.
1104
+
1105
+ Must return plain or S256. You can return a custom value if you have implemented your own
1106
+ AuthorizationCodeGrant class.
1107
+
1108
+ Keyword Arguments:
1109
+
1110
+ code: Authorization code.
1111
+ request: OAuthlib request.
1112
+
1113
+ Return type:
1114
+
1115
+ code_challenge_method string
1116
+
1117
+ Method is used by:
1118
+
1119
+ Authorization Code Grant - when PKCE is active
1120
+ """
1121
+ from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel
1122
+
1123
+ LOG.debug("get_code_challenge_method")
1124
+
1125
+ authorization_code = (
1126
+ DBSession.query(static.OAuth2AuthorizationCode)
1127
+ .filter(static.OAuth2AuthorizationCode.code == code)
1128
+ .one_or_none()
1129
+ )
1130
+ if authorization_code:
1131
+ return authorization_code.challenge_method # type: ignore
1132
+ LOG.debug("get_code_challenge_method authorization_code not found")
1133
+ return None
1134
+
1135
+
1136
+ def get_oauth_client(settings: Dict[str, Any]) -> oauthlib.oauth2.WebApplicationServer:
1137
+ """Get the oauth2 client, with a cache."""
961
1138
  authentication_settings = settings.get("authentication", {})
1139
+ return _get_oauth_client_cache(
1140
+ authentication_settings.get("oauth2_authorization_expire_minutes", 10),
1141
+ authentication_settings.get("oauth2_token_expire_minutes", 60),
1142
+ )
1143
+
1144
+
1145
+ @OBJECT_CACHE_REGION.cache_on_arguments() # type: ignore
1146
+ def _get_oauth_client_cache(
1147
+ authorization_expire_minutes: int, token_expire_minutes: int
1148
+ ) -> oauthlib.oauth2.WebApplicationServer:
1149
+ """Get the oauth2 client, with a cache."""
962
1150
  return oauthlib.oauth2.WebApplicationServer(
963
- RequestValidator(
964
- authorization_expires_in=authentication_settings.get("oauth2_authorization_expire_minutes", 10)
965
- ),
966
- token_expires_in=authentication_settings.get("oauth2_token_expire_minutes", 60) * 60,
1151
+ RequestValidator(authorization_expires_in=authorization_expire_minutes),
1152
+ token_expires_in=token_expire_minutes * 60,
967
1153
  )