c2cgeoportal-geoportal 2.5.0.100__py2.py3-none-any.whl → 2.7.1.156__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 (224) hide show
  1. c2cgeoportal_geoportal/__init__.py +261 -130
  2. c2cgeoportal_geoportal/lib/__init__.py +74 -120
  3. c2cgeoportal_geoportal/lib/authentication.py +170 -21
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +19 -11
  6. c2cgeoportal_geoportal/lib/caching.py +66 -160
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +62 -64
  9. c2cgeoportal_geoportal/lib/common_headers.py +170 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +70 -31
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +127 -97
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
  13. c2cgeoportal_geoportal/lib/functionality.py +36 -21
  14. c2cgeoportal_geoportal/lib/headers.py +14 -5
  15. c2cgeoportal_geoportal/lib/i18n.py +39 -0
  16. c2cgeoportal_geoportal/lib/layers.py +29 -10
  17. c2cgeoportal_geoportal/lib/lingua_extractor.py +408 -211
  18. c2cgeoportal_geoportal/lib/loader.py +18 -16
  19. c2cgeoportal_geoportal/lib/metrics.py +29 -18
  20. c2cgeoportal_geoportal/lib/oauth2.py +1036 -0
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
  22. c2cgeoportal_geoportal/lib/xsd.py +29 -19
  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/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -4
  29. c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +24 -15
  30. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +1 -0
  31. c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
  32. c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
  33. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +102 -0
  34. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
  35. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
  38. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/tools/extract-messages.js +8 -6
  39. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  40. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +75 -0
  41. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +31 -28
  42. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +3 -7
  43. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +4 -4
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +47 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  46. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  47. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  48. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  49. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  50. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
  51. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
  52. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +3 -6
  53. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  54. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +32 -20
  55. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  57. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +4 -8
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +43 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +65 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  64. c2cgeoportal_geoportal/scaffolds/create/{Dockerfile_tmpl → {{cookiecutter.project}}/Dockerfile} +34 -24
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +14 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +162 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +25 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +474 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +67 -0
  72. c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +43 -18
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +82 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +60 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +396 -0
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +0 -0
  81. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +1 -1
  82. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  83. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +7 -15
  84. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +17 -9
  85. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +17 -9
  86. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +17 -9
  87. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +17 -9
  88. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +30 -27
  89. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  91. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  92. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +3 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/setup.cfg +7 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +3 -0
  99. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  100. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  101. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +191 -0
  102. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1160 -0
  103. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +99 -47
  104. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1410 -0
  105. c2cgeoportal_geoportal/scripts/__init__.py +18 -32
  106. c2cgeoportal_geoportal/scripts/c2cupgrade.py +295 -200
  107. c2cgeoportal_geoportal/scripts/create_demo_theme.py +5 -6
  108. c2cgeoportal_geoportal/scripts/manage_users.py +34 -37
  109. c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
  110. c2cgeoportal_geoportal/scripts/theme2fts.py +92 -25
  111. c2cgeoportal_geoportal/scripts/urllogin.py +23 -17
  112. c2cgeoportal_geoportal/templates/login.html +88 -84
  113. c2cgeoportal_geoportal/templates/notlogin.html +62 -0
  114. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  115. c2cgeoportal_geoportal/views/__init__.py +23 -4
  116. c2cgeoportal_geoportal/views/dev.py +9 -7
  117. c2cgeoportal_geoportal/views/dynamic.py +71 -40
  118. c2cgeoportal_geoportal/views/entry.py +93 -24
  119. c2cgeoportal_geoportal/views/fulltextsearch.py +36 -29
  120. c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
  121. c2cgeoportal_geoportal/views/i18n.py +91 -9
  122. c2cgeoportal_geoportal/views/layers.py +173 -134
  123. c2cgeoportal_geoportal/views/login.py +206 -87
  124. c2cgeoportal_geoportal/views/mapserverproxy.py +59 -35
  125. c2cgeoportal_geoportal/views/memory.py +13 -13
  126. c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
  127. c2cgeoportal_geoportal/views/pdfreport.py +31 -27
  128. c2cgeoportal_geoportal/views/printproxy.py +70 -54
  129. c2cgeoportal_geoportal/views/profile.py +25 -24
  130. c2cgeoportal_geoportal/views/proxy.py +100 -70
  131. c2cgeoportal_geoportal/views/raster.py +47 -29
  132. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  133. c2cgeoportal_geoportal/views/shortener.py +31 -24
  134. c2cgeoportal_geoportal/views/theme.py +475 -365
  135. c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
  136. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  137. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/METADATA +17 -11
  138. c2cgeoportal_geoportal-2.7.1.156.dist-info/RECORD +185 -0
  139. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/WHEEL +1 -1
  140. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/entry_points.txt +3 -1
  141. tests/__init__.py +24 -3
  142. tests/test_cachebuster.py +1 -3
  143. tests/test_caching.py +19 -26
  144. tests/test_checker.py +2 -3
  145. tests/test_decimaljson.py +4 -4
  146. tests/test_headerstween.py +0 -3
  147. tests/test_i18n.py +31 -0
  148. tests/test_init.py +12 -27
  149. tests/test_locale_negociator.py +6 -6
  150. tests/test_mapserverproxy_route_predicate.py +0 -2
  151. tests/test_raster.py +18 -5
  152. tests/test_wmstparsing.py +7 -8
  153. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -226
  154. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -11
  155. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/ci.yaml_tmpl +0 -56
  156. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -12
  157. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -144
  158. c2cgeoportal_geoportal/scaffolds/create/docker-compose-lib.yaml_tmpl +0 -302
  159. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml_tmpl +0 -54
  160. c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -49
  161. c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -39
  162. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -5
  163. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -19
  164. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -48
  165. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  166. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/images/favicon.ico +0 -0
  167. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  168. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl +0 -37
  169. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl +0 -22
  170. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  171. c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
  172. c2cgeoportal_geoportal/scaffolds/create/geoportal/setup.py_tmpl +0 -18
  173. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  174. c2cgeoportal_geoportal/scaffolds/create/geoportal/vars.yaml_tmpl +0 -224
  175. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -71
  176. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  177. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  178. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -166
  179. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/legend.jrxml +0 -27
  180. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -29
  181. c2cgeoportal_geoportal/scaffolds/create/scripts/publish-docker +0 -124
  182. c2cgeoportal_geoportal/scaffolds/create/setup.cfg_tmpl +0 -3
  183. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  184. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -169
  185. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  186. c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -171
  187. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -64
  188. c2cgeoportal_geoportal/scaffolds/update/geoportal/CONST_vars.yaml_tmpl +0 -783
  189. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  190. c2cgeoportal_geoportal-2.5.0.100.dist-info/RECORD +0 -162
  191. tests/test_get_url.py +0 -96
  192. tests/test_lib.py +0 -77
  193. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  195. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  196. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  198. /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
  199. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  212. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  213. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  214. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  215. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  216. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  217. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  218. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  219. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  220. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  221. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  222. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  223. /c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
  224. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2011-2020, Camptocamp SA
1
+ # Copyright (c) 2011-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -32,14 +30,17 @@ import datetime
32
30
  import ipaddress
33
31
  import json
34
32
  import logging
33
+ import re
35
34
  from string import Formatter
36
- from typing import Any, Dict, List, Optional, Set, Union
37
- import urllib.parse
35
+ from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union, cast
38
36
 
39
37
  import dateutil
38
+ import pyramid.request
39
+ import pyramid.response
40
40
  from pyramid.interfaces import IRoutePregenerator
41
41
  from zope.interface import implementer
42
42
 
43
+ from c2cgeoportal_commons.lib.url import get_url2
43
44
  from c2cgeoportal_geoportal.lib.cacheversion import get_cache_version
44
45
  from c2cgeoportal_geoportal.lib.caching import get_region
45
46
 
@@ -48,72 +49,25 @@ CACHE_REGION = get_region("std")
48
49
  CACHE_REGION_OBJ = get_region("obj")
49
50
 
50
51
 
51
- def get_types_map(types_array):
52
+ def get_types_map(types_array: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
53
+ """Get the type name of a metadata or a functionality."""
52
54
  return {type_["name"]: type_ for type_ in types_array}
53
55
 
54
56
 
55
- def get_url2(name, url, request, errors) -> Optional[str]:
56
- url_split = urllib.parse.urlsplit(url)
57
- if url_split.scheme == "":
58
- if url_split.netloc == "" and url_split.path not in ("", "/"):
59
- # Relative URL like: /dummy/static/url or dummy/static/url
60
- return urllib.parse.urlunsplit(url_split)
61
- errors.add("{}='{}' is not an URL.".format(name, url))
62
- return None
63
- if url_split.scheme in ("http", "https"):
64
- if url_split.netloc == "":
65
- errors.add("{}='{}' is not a valid URL.".format(name, url))
66
- return None
67
- return urllib.parse.urlunsplit(url_split)
68
- if url_split.scheme == "static":
69
- if url_split.path in ("", "/"):
70
- errors.add("{}='{}' cannot have an empty path.".format(name, url))
71
- return None
72
- proj = url_split.netloc
73
- package = request.registry.settings["package"]
74
- if proj in ("", "static"):
75
- proj = "/etc/geomapfish/static"
76
- elif ":" not in proj:
77
- if proj == "static-ngeo":
78
- errors.add(
79
- "{}='{}' static-ngeo shouldn't be used out of webpack because it don't has "
80
- "cache bustering.".format(name, url)
81
- )
82
- proj = "{}_geoportal:{}".format(package, proj)
83
- return request.static_url("{}{}".format(proj, url_split.path))
84
- if url_split.scheme == "config":
85
- if url_split.netloc == "":
86
- errors.add("{}='{}' cannot have an empty netloc.".format(name, url))
87
- return None
88
- server = request.registry.settings.get("servers", {}).get(url_split.netloc)
89
- if server is None:
90
- errors.add(
91
- "{}: The server '{}' ({}) is not found in the config: [{}]".format(
92
- name,
93
- url_split.netloc,
94
- url,
95
- ", ".join(request.registry.settings.get("servers", {}).keys()),
96
- )
97
- )
98
- return None
99
- if url_split.path != "":
100
- if server[-1] != "/":
101
- server += "/"
102
- url = urllib.parse.urljoin(server, url_split.path[1:])
103
- else:
104
- url = server
105
- return url if not url_split.query else "{}?{}".format(url, url_split.query)
106
- return None
107
-
108
-
109
57
  def get_typed(
110
- name, value, types, request, errors, layer_name=None
58
+ name: str,
59
+ value: str,
60
+ types: Dict[str, Any],
61
+ request: pyramid.request.Request,
62
+ errors: Set[str],
63
+ layer_name: Optional[str] = None,
111
64
  ) -> Union[str, int, float, bool, None, List[Any], Dict[str, Any]]:
112
- prefix = "Layer '{}': ".format(layer_name) if layer_name is not None else ""
65
+ """Get the typed (parsed) value of a metadata or a functionality."""
66
+ prefix = f"Layer '{layer_name}': " if layer_name is not None else ""
113
67
  type_ = {"type": "not init"}
114
68
  try:
115
69
  if name not in types:
116
- errors.add("{}Type '{}' not defined.".format(prefix, name))
70
+ errors.add(f"{prefix}Type '{name}' not defined.")
117
71
  return None
118
72
  type_ = types[name]
119
73
  if type_.get("type", "string") == "string":
@@ -127,8 +81,8 @@ def get_typed(
127
81
  if value in ["no", "n", "off", "0", "false"]:
128
82
  return False
129
83
  errors.add(
130
- "{}The boolean attribute '{}'='{}' is not in "
131
- "[yes, y, on, 1, true, no, n, off, 0, false].".format(prefix, name, value.lower())
84
+ f"{prefix}The boolean attribute '{name}'='{value.lower()}' is not in "
85
+ "[yes, y, on, 1, true, no, n, off, 0, false]."
132
86
  )
133
87
  elif type_["type"] == "integer":
134
88
  return int(value)
@@ -137,59 +91,47 @@ def get_typed(
137
91
  elif type_["type"] == "date":
138
92
  date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore
139
93
  if date.time() != datetime.time(0, 0, 0):
140
- errors.add(
141
- "{}The date attribute '{}'='{}' should not have any time".format(prefix, name, value)
142
- )
94
+ errors.add(f"{prefix}The date attribute '{name}'='{value}' should not have any time")
143
95
  else:
144
96
  return datetime.date.strftime(date.date(), "%Y-%m-%d")
145
97
  elif type_["type"] == "time":
146
98
  date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore
147
99
  if date.date() != datetime.date(1, 1, 1):
148
- errors.add(
149
- "{}The time attribute '{}'='{}' should not have any date".format(prefix, name, value)
150
- )
100
+ errors.add(f"{prefix}The time attribute '{name}'='{value}' should not have any date")
151
101
  else:
152
102
  return datetime.time.strftime(date.time(), "%H:%M:%S")
153
103
  elif type_["type"] == "datetime":
154
104
  date = dateutil.parser.parse(value, default=datetime.datetime(1, 1, 1, 0, 0, 0)) # type: ignore
155
105
  return datetime.datetime.strftime(date, "%Y-%m-%dT%H:%M:%S")
156
106
  elif type_["type"] == "url":
157
- return get_url2("{}The attribute '{}'".format(prefix, name), value, request, errors)
107
+ url = get_url2(f"{prefix}The attribute '{name}'", value, request, errors)
108
+ return url.url() if url else ""
158
109
  elif type_["type"] == "json":
159
110
  try:
160
- return json.loads(value)
111
+ return cast(Dict[str, Any], json.loads(value))
161
112
  except Exception as e:
162
- errors.append(
163
- "{}The attribute '{}'='{}' has an error: {}".format(prefix, name, value, str(e))
113
+ errors.add(f"{prefix}The attribute '{name}'='{value}' has an error: {str(e)}")
114
+ elif type_["type"] == "regex":
115
+ pattern = type_["regex"]
116
+ if re.match(pattern, value) is None:
117
+ errors.add(
118
+ f"{prefix}The regex attribute '{name}'='{value}' "
119
+ f"does not match expected pattern '{pattern}'."
164
120
  )
121
+ else:
122
+ return value
165
123
  else:
166
- errors.add("{}Unknown type '{}'.".format(prefix, type_["type"]))
124
+ errors.add(f"{prefix}Unknown type '{type_['type']}'.")
167
125
  except Exception as e:
168
126
  errors.add(
169
- "{}Unable to parse the attribute '{}'='{}' with the type '{}', error:\n{}".format(
170
- prefix, name, value, type_.get("type", "string"), str(e)
171
- )
127
+ f"{prefix}Unable to parse the attribute '{name}'='{value}' with the type "
128
+ f"'{type_.get('type', 'string')}', error:\n{e!s}"
172
129
  )
173
130
  return None
174
131
 
175
132
 
176
- def add_url_params(url: str, params: Optional[Dict[str, str]]) -> str:
177
- if not params:
178
- return url
179
- return add_spliturl_params(urllib.parse.urlsplit(url), params)
180
-
181
-
182
- def add_spliturl_params(spliturl, params):
183
- query = {k: v[-1] for k, v in list(urllib.parse.parse_qs(spliturl.query).items())}
184
- for key, value in list(params.items()):
185
- query[key] = value
186
-
187
- return urllib.parse.urlunsplit(
188
- (spliturl.scheme, spliturl.netloc, spliturl.path, urllib.parse.urlencode(query), spliturl.fragment)
189
- )
190
-
191
-
192
- def get_setting(settings, path, default=None):
133
+ def get_setting(settings: Any, path: Iterable[str], default: Any = None) -> Any:
134
+ """Get the settings."""
193
135
  value = settings
194
136
  for p in path:
195
137
  if value and p in value:
@@ -199,42 +141,48 @@ def get_setting(settings, path, default=None):
199
141
  return value if value else default
200
142
 
201
143
 
202
- @CACHE_REGION_OBJ.cache_on_arguments()
203
- def get_ogc_server_wms_url_ids(request):
144
+ @CACHE_REGION_OBJ.cache_on_arguments() # type: ignore
145
+ def get_ogc_server_wms_url_ids(request: pyramid.request.Request, host: str) -> Dict[str, List[int]]:
146
+ """Get the OGCServer ids mapped on the WMS URL."""
204
147
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
205
148
  from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel
206
149
 
150
+ del host # used for cache
207
151
  errors: Set[str] = set()
208
152
  servers: Dict[str, List[int]] = {}
209
153
  for ogc_server in DBSession.query(OGCServer).all():
210
154
  url = get_url2(ogc_server.name, ogc_server.url, request, errors)
211
155
  if url is not None:
212
- servers.setdefault(url, []).append(ogc_server.id)
156
+ servers.setdefault(url.url(), []).append(ogc_server.id)
213
157
  return servers
214
158
 
215
159
 
216
- @CACHE_REGION_OBJ.cache_on_arguments()
217
- def get_ogc_server_wfs_url_ids(request):
160
+ @CACHE_REGION_OBJ.cache_on_arguments() # type: ignore
161
+ def get_ogc_server_wfs_url_ids(request: pyramid.request.Request, host: str) -> Dict[str, List[int]]:
162
+ """Get the OGCServer ids mapped on the WFS URL."""
218
163
  from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
219
164
  from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel
220
165
 
166
+ del host # used for cache
221
167
  errors: Set[str] = set()
222
168
  servers: Dict[str, List[int]] = {}
223
169
  for ogc_server in DBSession.query(OGCServer).all():
224
170
  url = get_url2(ogc_server.name, ogc_server.url_wfs or ogc_server.url, request, errors)
225
171
  if url is not None:
226
- servers.setdefault(url, []).append(ogc_server.id)
172
+ servers.setdefault(url.url(), []).append(ogc_server.id)
227
173
  return servers
228
174
 
229
175
 
230
176
  @implementer(IRoutePregenerator)
231
- class C2CPregenerator: # pragma: no cover
232
- def __init__(self, version=True, role=False):
177
+ class C2CPregenerator:
178
+ """The custom pyramid pregenerator that manage the cache version."""
179
+
180
+ def __init__(self, version: bool = True, role: bool = False):
233
181
  self.version = version
234
182
  self.role = role
235
183
 
236
- def __call__(self, request, elements, kw):
237
- query = kw.get("_query", {})
184
+ def __call__(self, request: pyramid.request.Request, elements: Any, kw: Any) -> Tuple[Any, Any]:
185
+ query = {**kw.get("_query", {})}
238
186
 
239
187
  if self.version:
240
188
  query["cache_version"] = get_cache_version()
@@ -252,42 +200,48 @@ class C2CPregenerator: # pragma: no cover
252
200
  _formatter = Formatter()
253
201
 
254
202
 
255
- @CACHE_REGION_OBJ.cache_on_arguments()
256
- def _get_intranet_networks(request):
203
+ @CACHE_REGION_OBJ.cache_on_arguments() # type: ignore
204
+ def _get_intranet_networks(
205
+ request: pyramid.request.Request,
206
+ ) -> List[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]]:
257
207
  return [
258
208
  ipaddress.ip_network(network, strict=False)
259
209
  for network in request.registry.settings.get("intranet", {}).get("networks", [])
260
210
  ]
261
211
 
262
212
 
263
- @CACHE_REGION.cache_on_arguments()
264
- def get_role_id(name):
213
+ @CACHE_REGION.cache_on_arguments() # type: ignore
214
+ def get_role_id(name: str) -> int:
215
+ """Get the role ID."""
265
216
  from c2cgeoportal_commons.models import DBSession, main # pylint: disable=import-outside-toplevel
266
217
 
267
- return DBSession.query(main.Role.id).filter(main.Role.name == name).one()[0]
218
+ return cast(int, DBSession.query(main.Role.id).filter(main.Role.name == name).one()[0])
268
219
 
269
220
 
270
- def get_roles_id(request):
271
- result = [get_role_id("anonymous")]
221
+ def get_roles_id(request: pyramid.request.Request) -> List[int]:
222
+ """Get the user roles ID."""
223
+ result = [get_role_id(request.get_organization_role("anonymous"))]
272
224
  if is_intranet(request):
273
- result.append(get_role_id("intranet"))
225
+ result.append(get_role_id(request.get_organization_role("intranet")))
274
226
  if request.user is not None:
275
- result.append(get_role_id("registered"))
227
+ result.append(get_role_id(request.get_organization_role("registered")))
276
228
  result.extend([r.id for r in request.user.roles])
277
229
  return result
278
230
 
279
231
 
280
- def get_roles_name(request):
281
- result = ["anonymous"]
232
+ def get_roles_name(request: pyramid.request.Request) -> pyramid.response.Response:
233
+ """Get the user roles name."""
234
+ result = [request.get_organization_role("anonymous")]
282
235
  if is_intranet(request):
283
- result.append("intranet")
236
+ result.append(request.get_organization_role("intranet"))
284
237
  if request.user is not None:
285
- result.append("registered")
238
+ result.append(request.get_organization_role("registered"))
286
239
  result.extend([r.name for r in request.user.roles])
287
240
  return result
288
241
 
289
242
 
290
- def is_intranet(request):
243
+ def is_intranet(request: pyramid.request.Request) -> bool:
244
+ """Get if it's an intranet user."""
291
245
  address = ipaddress.ip_address(request.client_addr)
292
246
  for network in _get_intranet_networks(request):
293
247
  if address in network:
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2014-2020, Camptocamp SA
1
+ # Copyright (c) 2014-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,17 +26,143 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ import binascii
30
+ import json
31
31
  import logging
32
+ import os
33
+ import time
34
+ from typing import Any, Callable, Dict, List, Optional, cast
32
35
 
33
- from pyramid.authentication import AuthTktAuthenticationPolicy, BasicAuthAuthenticationPolicy
36
+ import pyramid.request
37
+ from Crypto.Cipher import AES # nosec
38
+ from pyramid.authentication import (
39
+ AuthTktAuthenticationPolicy,
40
+ BasicAuthAuthenticationPolicy,
41
+ CallbackAuthenticationPolicy,
42
+ )
43
+ from pyramid.interfaces import IAuthenticationPolicy
44
+ from pyramid.security import remember
34
45
  from pyramid_multiauth import MultiAuthenticationPolicy
46
+ from zope.interface import implementer
35
47
 
48
+ from c2cgeoportal_geoportal.lib import oauth2
36
49
  from c2cgeoportal_geoportal.resources import defaultgroupsfinder
37
50
 
38
51
  LOG = logging.getLogger(__name__)
39
52
 
40
53
 
41
- def create_authentication(settings):
54
+ @implementer(IAuthenticationPolicy)
55
+ class UrlAuthenticationPolicy(CallbackAuthenticationPolicy): # type: ignore
56
+ """An authentication policy based on information given in the URL."""
57
+
58
+ def __init__(
59
+ self, aes_key: str, callback: Optional[Callable[[str, Any], List[str]]] = None, debug: bool = False
60
+ ):
61
+ self.aeskey = aes_key
62
+ self.callback = callback
63
+ self.debug = debug
64
+
65
+ def unauthenticated_userid(self, request: pyramid.request.Request) -> Optional[str]:
66
+ if not request.method == "GET" or "auth" not in request.params:
67
+ return None
68
+ auth_enc = request.params.get("auth")
69
+ if auth_enc is None:
70
+ return None
71
+ try:
72
+ if self.aeskey is None: # pragma: nocover
73
+ raise Exception("urllogin is not configured")
74
+ now = int(time.time())
75
+ data = binascii.unhexlify(auth_enc.encode("ascii"))
76
+ nonce = data[0:16]
77
+ tag = data[16:32]
78
+ ciphertext = data[32:]
79
+ cipher = AES.new(self.aeskey.encode("ascii"), AES.MODE_EAX, nonce)
80
+ auth = json.loads(cipher.decrypt_and_verify(ciphertext, tag).decode("utf-8")) # type: ignore
81
+
82
+ if "t" in auth and "u" in auth and "p" in auth:
83
+ timestamp = int(auth["t"])
84
+
85
+ if now < timestamp and request.registry.validate_user(request, auth["u"], auth["p"]):
86
+ headers = remember(request, auth["u"])
87
+ request.response.headerlist.extend(headers)
88
+ return cast(str, auth["u"])
89
+
90
+ except Exception as e:
91
+ LOG.error("URL login error: %s.", e, exc_info=True)
92
+
93
+ return None
94
+
95
+ def remember( # pylint: disable=no-self-use
96
+ self, request: pyramid.request.Request, userid: str, **kw: Any
97
+ ) -> List[Dict[str, str]]:
98
+ """Do no-op."""
99
+ del request, userid, kw
100
+ return []
101
+
102
+ def forget(self, request: pyramid.request.Request) -> List[Dict[str, str]]: # pylint: disable=no-self-use
103
+ """Do no-op."""
104
+ del request
105
+ return []
106
+
107
+
108
+ @implementer(IAuthenticationPolicy)
109
+ class OAuth2AuthenticationPolicy(CallbackAuthenticationPolicy): # type: ignore
110
+ """The oauth2 authentication policy."""
111
+
112
+ @staticmethod
113
+ def unauthenticated_userid(request: pyramid.request.Request) -> Optional[str]:
114
+ route_url = ""
115
+ try:
116
+ route_url = request.current_route_url(_query={**request.GET})
117
+ except ValueError:
118
+ route_url = request.route_url("base", _query={**request.GET})
119
+
120
+ LOG.debug(
121
+ "Call OAuth verify_request with:\nurl: %s\nmethod: %s\nbody:\n%s",
122
+ route_url,
123
+ request.method,
124
+ request.body,
125
+ )
126
+ valid, oauth2_request = oauth2.get_oauth_client(request.registry.settings).verify_request(
127
+ route_url,
128
+ request.method,
129
+ request.body,
130
+ request.headers,
131
+ [],
132
+ )
133
+ LOG.debug("OAuth verify_request: %s", valid)
134
+ if valid:
135
+ request.user_ = oauth2_request.user
136
+
137
+ return cast(str, request.user_.username)
138
+ return None
139
+
140
+ def remember( # pylint: disable=no-self-use
141
+ self, request: pyramid.request.Request, userid: str, **kw: Any
142
+ ) -> List[Dict[str, str]]:
143
+ """Do no-op."""
144
+ del request, userid, kw
145
+ return []
146
+
147
+ def forget(self, request: pyramid.request.Request) -> List[Dict[str, str]]: # pylint: disable=no-self-use
148
+ """Do no-op."""
149
+ del request
150
+ return []
151
+
152
+
153
+ @implementer(IAuthenticationPolicy)
154
+ class DevAuthenticationPolicy(CallbackAuthenticationPolicy): # type: ignore
155
+ """An authentication policy for the dev base on an environment variable."""
156
+
157
+ @staticmethod
158
+ def unauthenticated_userid(request: pyramid.request.Request) -> Optional[str]:
159
+ """Get the user name from the environment variable."""
160
+ del request
161
+ return os.environ["DEV_LOGINNAME"]
162
+
163
+
164
+ def create_authentication(settings: Dict[str, Any]) -> MultiAuthenticationPolicy:
165
+ """Create all the authentication policies."""
42
166
  timeout = settings.get("authtkt_timeout")
43
167
  timeout = None if timeout is None or timeout.lower() == "none" else int(timeout)
44
168
  reissue_time = settings.get("authtkt_reissue_time")
@@ -58,26 +182,51 @@ def create_authentication(settings):
58
182
  "See https://docs.pylonsproject.org/projects/pyramid/en/latest/api/session.html"
59
183
  )
60
184
 
61
- cookie_authentication_policy = AuthTktAuthenticationPolicy(
62
- secret,
63
- callback=defaultgroupsfinder,
64
- cookie_name=settings["authtkt_cookie_name"],
65
- samesite=None if samesite == "" else samesite,
66
- timeout=timeout,
67
- max_age=max_age,
68
- reissue_time=reissue_time,
69
- hashalg="sha512",
70
- http_only=http_only,
71
- secure=secure,
185
+ policies = []
186
+
187
+ policies.append(
188
+ UrlAuthenticationPolicy(
189
+ settings.get("urllogin", {}).get("aes_key"),
190
+ defaultgroupsfinder,
191
+ )
192
+ )
193
+
194
+ policies.append(
195
+ AuthTktAuthenticationPolicy(
196
+ secret,
197
+ callback=defaultgroupsfinder,
198
+ cookie_name=settings["authtkt_cookie_name"],
199
+ samesite=None if samesite == "" else samesite,
200
+ timeout=timeout,
201
+ max_age=max_age,
202
+ reissue_time=reissue_time,
203
+ hashalg="sha512",
204
+ http_only=http_only,
205
+ secure=secure,
206
+ )
72
207
  )
73
- if not basicauth:
74
- return cookie_authentication_policy
75
- basic_authentication_policy = BasicAuthAuthenticationPolicy(c2cgeoportal_check)
76
- policies = [cookie_authentication_policy, basic_authentication_policy]
208
+
209
+ policies.append(OAuth2AuthenticationPolicy())
210
+
211
+ if basicauth:
212
+ if settings["authentication"].get("two_factor", False):
213
+ LOG.warning(
214
+ "Basic auth and tow factor auth should not be enable toogether, "
215
+ "you should use OAuth2 instead of Basic auth"
216
+ )
217
+
218
+ basic_authentication_policy = BasicAuthAuthenticationPolicy(c2cgeoportal_check)
219
+ policies.append(basic_authentication_policy)
220
+
221
+ # Consider empty string as not configured
222
+ if "DEV_LOGINNAME" in os.environ and os.environ["DEV_LOGINNAME"]:
223
+ policies.append(DevAuthenticationPolicy())
224
+
77
225
  return MultiAuthenticationPolicy(policies)
78
226
 
79
227
 
80
- def c2cgeoportal_check(username, password, request): # pragma: no cover
228
+ def c2cgeoportal_check(username: str, password: str, request: pyramid.request.Request) -> Optional[List[str]]:
229
+ """Check the user authentication."""
81
230
  if request.registry.validate_user(request, username, password):
82
231
  return defaultgroupsfinder(username, request)
83
232
  return None
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2013-2020, Camptocamp SA
1
+ # Copyright (c) 2013-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,16 +25,22 @@
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
28
+ from enum import Enum
29
+
30
+
31
+ class Color(Enum):
32
+ """The available colors."""
30
33
 
31
- BLACK = 0
32
- RED = 1
33
- GREEN = 2
34
- YELLOW = 3
35
- BLUE = 4
36
- MAGENTA = 5
37
- CYAN = 6
38
- WHITE = 7
34
+ BLACK = 0
35
+ RED = 1
36
+ GREEN = 2
37
+ YELLOW = 3
38
+ BLUE = 4
39
+ MAGENTA = 5
40
+ CYAN = 6
41
+ WHITE = 7
39
42
 
40
43
 
41
- def colorize(text, color): # pragma: no cover
42
- return "\x1b[01;3{0:d}m{1!s}\x1b[0m".format(color, text)
44
+ def colorize(text: str, color: Color) -> str:
45
+ """Colorize a text for the bash."""
46
+ return f"\x1b[01;3{color.value}m{text}\x1b[0m"
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2011-2019, Camptocamp SA
1
+ # Copyright (c) 2011-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,35 +26,45 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
31
- from urllib.parse import urljoin
32
29
  import uuid
30
+ from typing import Any, Callable, Dict, Tuple
31
+ from urllib.parse import urljoin
33
32
 
34
33
  import pyramid.registry
34
+ import pyramid.request
35
+ import pyramid.response
35
36
 
36
37
  from c2cgeoportal_geoportal.lib.caching import get_region
37
38
 
38
39
  CACHE_REGION = get_region("std")
39
40
 
40
41
 
41
- @CACHE_REGION.cache_on_arguments()
42
- def get_cache_version():
43
- """Return a cache version that is regenerate after each cache invalidation"""
42
+ @CACHE_REGION.cache_on_arguments() # type: ignore
43
+ def get_cache_version() -> str:
44
+ """Return a cache version that is regenerate after each cache invalidation."""
44
45
  return uuid.uuid4().hex
45
46
 
46
47
 
47
- def version_cache_buster(request, subpath, kw): # pragma: no cover
48
+ def version_cache_buster(
49
+ request: pyramid.request.Request, subpath: str, kw: Dict[str, Any]
50
+ ) -> Tuple[str, Dict[str, Any]]:
51
+ """Join the cash buster version with the sub path."""
48
52
  del request # unused
49
53
  return urljoin(get_cache_version() + "/", subpath), kw
50
54
 
51
55
 
52
56
  class CachebusterTween:
53
- """ Get back the cachebuster URL. """
57
+ """Get back the cachebuster URL."""
54
58
 
55
- def __init__(self, handler, registry: pyramid.registry.Registry):
59
+ def __init__(
60
+ self,
61
+ handler: Callable[[pyramid.request.Request], pyramid.response.Response],
62
+ registry: pyramid.registry.Registry,
63
+ ):
56
64
  self.handler = handler
57
65
  self.cache_path = registry.settings["cache_path"]
58
66
 
59
- def __call__(self, request):
67
+ def __call__(self, request: pyramid.request.Request) -> pyramid.response.Response:
60
68
  path = request.path_info.split("/")
61
69
  if len(path) > 1 and path[1] in self.cache_path:
62
70
  # remove the cache buster