c2cgeoportal-geoportal 2.6.0__py2.py3-none-any.whl → 2.7.1.157__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.
- c2cgeoportal_geoportal/__init__.py +224 -84
- c2cgeoportal_geoportal/lib/__init__.py +67 -43
- c2cgeoportal_geoportal/lib/authentication.py +50 -22
- c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
- c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
- c2cgeoportal_geoportal/lib/caching.py +61 -191
- c2cgeoportal_geoportal/lib/check_collector.py +17 -10
- c2cgeoportal_geoportal/lib/checker.py +61 -63
- c2cgeoportal_geoportal/lib/common_headers.py +170 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +54 -39
- c2cgeoportal_geoportal/lib/filter_capabilities.py +122 -88
- c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
- c2cgeoportal_geoportal/lib/functionality.py +20 -17
- c2cgeoportal_geoportal/lib/headers.py +14 -5
- c2cgeoportal_geoportal/lib/i18n.py +4 -4
- c2cgeoportal_geoportal/lib/layers.py +30 -11
- c2cgeoportal_geoportal/lib/lingua_extractor.py +361 -237
- c2cgeoportal_geoportal/lib/loader.py +10 -15
- c2cgeoportal_geoportal/lib/metrics.py +28 -17
- c2cgeoportal_geoportal/lib/oauth2.py +214 -145
- c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
- c2cgeoportal_geoportal/lib/xsd.py +26 -16
- c2cgeoportal_geoportal/resources.py +15 -9
- c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +18 -9
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +102 -0
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/tools/extract-messages.js +8 -6
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +75 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +31 -28
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +3 -7
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/__init__.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py} +11 -22
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
- 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
- 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
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -7
- c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +43 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +65 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
- c2cgeoportal_geoportal/scaffolds/create/{Dockerfile_tmpl → {{cookiecutter.project}}/Dockerfile} +20 -11
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +162 -0
- c2cgeoportal_geoportal/scaffolds/create/{ci/config.yaml_tmpl → {{cookiecutter.project}}/ci/config.yaml} +7 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +133 -17
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +67 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +17 -12
- c2cgeoportal_geoportal/scaffolds/create/{env.default_tmpl → {{cookiecutter.project}}/env.default} +29 -14
- c2cgeoportal_geoportal/scaffolds/create/{env.project_tmpl → {{cookiecutter.project}}/env.project} +16 -4
- c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +93 -27
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +1 -1
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +7 -15
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +8 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +5 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
- c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
- c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +3 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
- c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/update/{+dot+upgrade.yaml_tmpl → {{cookiecutter.project}}/.upgrade.yaml} +49 -39
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1160 -0
- c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +47 -2
- c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +350 -17
- c2cgeoportal_geoportal/scripts/__init__.py +16 -30
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +271 -232
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
- c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
- c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +72 -23
- c2cgeoportal_geoportal/scripts/urllogin.py +19 -11
- c2cgeoportal_geoportal/templates/login.html +88 -84
- c2cgeoportal_geoportal/templates/notlogin.html +59 -59
- c2cgeoportal_geoportal/templates/testi18n.html +6 -8
- c2cgeoportal_geoportal/views/__init__.py +23 -4
- c2cgeoportal_geoportal/views/dev.py +9 -7
- c2cgeoportal_geoportal/views/dynamic.py +56 -19
- c2cgeoportal_geoportal/views/entry.py +93 -24
- c2cgeoportal_geoportal/views/fulltextsearch.py +28 -22
- c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
- c2cgeoportal_geoportal/views/i18n.py +91 -9
- c2cgeoportal_geoportal/views/layers.py +160 -126
- c2cgeoportal_geoportal/views/login.py +106 -93
- c2cgeoportal_geoportal/views/mapserverproxy.py +46 -29
- c2cgeoportal_geoportal/views/memory.py +12 -12
- c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
- c2cgeoportal_geoportal/views/pdfreport.py +26 -22
- c2cgeoportal_geoportal/views/printproxy.py +60 -52
- c2cgeoportal_geoportal/views/profile.py +24 -23
- c2cgeoportal_geoportal/views/proxy.py +87 -69
- c2cgeoportal_geoportal/views/raster.py +35 -24
- c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
- c2cgeoportal_geoportal/views/shortener.py +27 -24
- c2cgeoportal_geoportal/views/theme.py +427 -321
- c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
- c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.157.dist-info}/METADATA +25 -20
- c2cgeoportal_geoportal-2.7.1.157.dist-info/RECORD +185 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.157.dist-info}/WHEEL +1 -1
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.157.dist-info}/entry_points.txt +3 -1
- tests/__init__.py +7 -3
- tests/test_cachebuster.py +0 -2
- tests/test_caching.py +17 -25
- tests/test_checker.py +0 -2
- tests/test_decimaljson.py +4 -4
- tests/test_headerstween.py +0 -2
- tests/test_i18n.py +1 -1
- tests/test_init.py +4 -7
- tests/test_locale_negociator.py +0 -2
- tests/test_mapserverproxy_route_predicate.py +0 -2
- tests/test_raster.py +0 -2
- tests/test_wmstparsing.py +0 -2
- c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
- c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
- c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
- c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
- c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
- c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
- c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
- c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
- c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
- c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
- c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
- c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
- c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
- c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
- c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
- c2cgeoportal_geoportal/templates/dynamic.js +0 -21
- c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
- /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
- /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
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.7.1.157.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
# Copyright (c) 2011-
|
3
|
+
# Copyright (c) 2011-2024, Camptocamp SA
|
4
4
|
# All rights reserved.
|
5
5
|
|
6
6
|
# Redistribution and use in source and binary forms, with or without
|
@@ -30,64 +30,61 @@
|
|
30
30
|
|
31
31
|
import logging
|
32
32
|
import sys
|
33
|
-
import
|
34
|
-
from typing import Dict, List, Union
|
33
|
+
from typing import Any, Dict, List, Optional, Union
|
35
34
|
|
35
|
+
import pyramid.request
|
36
|
+
import pyramid.response
|
36
37
|
import requests
|
37
38
|
from pyramid.httpexceptions import HTTPBadGateway, exception_response
|
38
|
-
from pyramid.response import Response
|
39
39
|
|
40
|
-
from
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
get_region,
|
45
|
-
set_common_headers,
|
46
|
-
)
|
40
|
+
from c2cgeoportal_commons.lib.url import Url
|
41
|
+
from c2cgeoportal_geoportal.lib.caching import get_region
|
42
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
43
|
+
from c2cgeoportal_geoportal.views import restrict_headers
|
47
44
|
|
48
45
|
LOG = logging.getLogger(__name__)
|
49
46
|
CACHE_REGION = get_region("std")
|
50
47
|
|
51
48
|
|
52
49
|
class Proxy:
|
53
|
-
|
50
|
+
"""Some methodes used by all the proxy."""
|
51
|
+
|
52
|
+
def __init__(self, request: pyramid.request.Request):
|
54
53
|
self.request = request
|
55
|
-
self.host_forward_host = request.registry.settings
|
54
|
+
self.host_forward_host = request.registry.settings.get("host_forward_host", [])
|
55
|
+
self.headers_whitelist = request.registry.settings.get("headers_whitelist", [])
|
56
|
+
self.headers_blacklist = request.registry.settings.get("headers_blacklist", [])
|
56
57
|
self.http_options = self.request.registry.settings.get("http_options", {})
|
57
58
|
|
58
|
-
def _proxy(
|
59
|
-
|
59
|
+
def _proxy(
|
60
|
+
self,
|
61
|
+
url: Url,
|
62
|
+
params: Optional[Dict[str, str]] = None,
|
63
|
+
method: Optional[str] = None,
|
64
|
+
cache: bool = False,
|
65
|
+
body: Optional[bytes] = None,
|
66
|
+
headers: Optional[Dict[str, str]] = None,
|
67
|
+
) -> requests.models.Response:
|
68
|
+
# Get query string
|
60
69
|
params = dict(self.request.params) if params is None else params
|
61
|
-
|
62
|
-
url_params = urllib.parse.parse_qs(parsed_url.query)
|
63
|
-
all_params: Dict[str, Union[str]] = {}
|
64
|
-
all_params.update(params)
|
65
|
-
all_params.update({k: ",".join(v) for k, v in url_params.items()})
|
66
|
-
query_string = urllib.parse.urlencode(all_params)
|
67
|
-
|
68
|
-
if parsed_url.port is None:
|
69
|
-
url = "{}://{}{}?{}".format(parsed_url.scheme, parsed_url.hostname, parsed_url.path, query_string)
|
70
|
-
else: # pragma: no cover
|
71
|
-
url = "{}://{}:{:d}{}?{}".format(
|
72
|
-
parsed_url.scheme, parsed_url.hostname, parsed_url.port, parsed_url.path, query_string
|
73
|
-
)
|
70
|
+
url = url.clone().add_query(params, True)
|
74
71
|
|
75
72
|
LOG.debug("Send query to URL:\n%s.", url)
|
76
73
|
|
77
74
|
if method is None:
|
78
75
|
method = self.request.method
|
79
76
|
|
80
|
-
if headers is None
|
81
|
-
headers = dict(self.request.headers)
|
77
|
+
headers = dict(self.request.headers if headers is None else headers)
|
82
78
|
|
83
|
-
# Forward request to target (without Host Header)
|
84
|
-
|
79
|
+
# Forward request to target (without Host Header).
|
80
|
+
# The original Host will be added back by pyramid.
|
81
|
+
if url.hostname not in self.host_forward_host and "Host" in headers:
|
85
82
|
headers.pop("Host")
|
86
83
|
|
87
84
|
# Forward the request tracking ID to the other service. This will allow to follow the logs belonging
|
88
85
|
# to a single request coming from the user
|
89
86
|
headers.setdefault("X-Request-ID", self.request.c2c_request_id)
|
90
|
-
# If we
|
87
|
+
# If we really want to respect the specification, we should chain with the content of the previous
|
91
88
|
# proxy, see also:
|
92
89
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
|
93
90
|
forwarded = {"for": self.request.client_addr, "proto": self.request.scheme}
|
@@ -98,19 +95,30 @@ class Proxy:
|
|
98
95
|
headers["Forwarded"] = ",".join([headers["Forwarded"], forwarded_str])
|
99
96
|
else:
|
100
97
|
headers["Forwarded"] = forwarded_str
|
98
|
+
# Set alternative "X-Forwarded" headers
|
99
|
+
for forwarded_elements in reversed(headers["Forwarded"].split(",")):
|
100
|
+
for element in forwarded_elements.split(";"):
|
101
|
+
key, value = element.split("=")
|
102
|
+
header_key = f"X-Forwarded-{key.capitalize()}"
|
103
|
+
header_value = headers.get(header_key)
|
104
|
+
headers[header_key] = value if header_value is None else ", ".join([header_value, value])
|
101
105
|
|
102
106
|
if not cache:
|
103
107
|
headers["Cache-Control"] = "no-cache"
|
104
108
|
|
105
|
-
if method in ("POST", "PUT") and body is None:
|
109
|
+
if method in ("POST", "PUT") and body is None:
|
106
110
|
body = self.request.body
|
107
111
|
|
112
|
+
headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist)
|
113
|
+
|
108
114
|
try:
|
109
115
|
if method in ("POST", "PUT"):
|
110
|
-
response = requests.request(
|
116
|
+
response = requests.request(
|
117
|
+
method, url.url(), data=body, headers=headers, **self.http_options
|
118
|
+
)
|
111
119
|
else:
|
112
|
-
response = requests.request(method, url, headers=headers, **self.http_options)
|
113
|
-
except Exception:
|
120
|
+
response = requests.request(method, url.url(), headers=headers, **self.http_options)
|
121
|
+
except Exception:
|
114
122
|
errors = ["Error '%s' while getting the URL:", "%s", "Method: %s", "--- With headers ---", "%s"]
|
115
123
|
args1 = [
|
116
124
|
sys.exc_info()[0],
|
@@ -118,19 +126,21 @@ class Proxy:
|
|
118
126
|
method,
|
119
127
|
"\n".join(
|
120
128
|
[
|
121
|
-
"{}: {
|
129
|
+
f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
|
122
130
|
for h, v in list(headers.items())
|
123
131
|
]
|
124
132
|
),
|
125
133
|
]
|
126
|
-
if method in ("POST", "PUT"):
|
134
|
+
if method in ("POST", "PUT") and body is not None:
|
127
135
|
errors += ["--- Query with body ---", "%s"]
|
128
136
|
args1.append(body.decode("utf-8"))
|
129
|
-
LOG.
|
137
|
+
LOG.exception("\n".join(errors), *args1)
|
130
138
|
|
131
|
-
raise HTTPBadGateway(
|
139
|
+
raise HTTPBadGateway( # pylint: disable=raise-missing-from
|
140
|
+
"Error on backend, See logs for detail"
|
141
|
+
)
|
132
142
|
|
133
|
-
if not response.ok:
|
143
|
+
if not response.ok:
|
134
144
|
errors = [
|
135
145
|
"Error '%s' in response of URL:",
|
136
146
|
"%s",
|
@@ -141,17 +151,17 @@ class Proxy:
|
|
141
151
|
]
|
142
152
|
args2: List[Union[str, int]] = [
|
143
153
|
response.reason,
|
144
|
-
url,
|
154
|
+
url.url(),
|
145
155
|
response.status_code,
|
146
156
|
method,
|
147
157
|
"\n".join(
|
148
158
|
[
|
149
|
-
"{}: {
|
159
|
+
f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
|
150
160
|
for h, v in list(headers.items())
|
151
161
|
]
|
152
162
|
),
|
153
163
|
]
|
154
|
-
if method in ("POST", "PUT"):
|
164
|
+
if method in ("POST", "PUT") and body is not None:
|
155
165
|
errors += ["--- Query with body ---", "%s"]
|
156
166
|
args2.append(body.decode("utf-8"))
|
157
167
|
errors += ["--- Return content ---", "%s"]
|
@@ -164,41 +174,49 @@ class Proxy:
|
|
164
174
|
|
165
175
|
return response
|
166
176
|
|
167
|
-
@CACHE_REGION.cache_on_arguments()
|
168
|
-
def _proxy_cache(self, method, *args, **kwargs
|
177
|
+
@CACHE_REGION.cache_on_arguments() # type: ignore
|
178
|
+
def _proxy_cache(self, host: str, method: str, *args: Any, **kwargs: Any) -> pyramid.response.Response:
|
169
179
|
# Method is only for the cache
|
170
|
-
del method
|
180
|
+
del host, method
|
181
|
+
|
171
182
|
kwargs["cache"] = True
|
172
183
|
return self._proxy(*args, **kwargs)
|
173
184
|
|
174
185
|
def _proxy_response(
|
175
|
-
self,
|
176
|
-
|
186
|
+
self,
|
187
|
+
service_name: str,
|
188
|
+
url: Url,
|
189
|
+
headers_update: Optional[Dict[str, str]] = None,
|
190
|
+
public: bool = False,
|
191
|
+
**kwargs: Any,
|
192
|
+
) -> pyramid.response.Response:
|
177
193
|
if headers_update is None:
|
178
194
|
headers_update = {}
|
179
195
|
cache = kwargs.get("cache", False)
|
180
196
|
if cache is True:
|
181
|
-
response = self._proxy_cache(url, self.request.method, **kwargs)
|
197
|
+
response = self._proxy_cache(url, self.request.host, self.request.method, **kwargs)
|
182
198
|
else:
|
183
199
|
response = self._proxy(url, **kwargs)
|
184
200
|
|
185
|
-
cache_control = (
|
201
|
+
cache_control = (
|
202
|
+
(Cache.PUBLIC if public else Cache.PRIVATE)
|
203
|
+
if cache
|
204
|
+
else (Cache.PUBLIC_NO if public else Cache.PRIVATE_NO)
|
205
|
+
)
|
186
206
|
return self._build_response(
|
187
207
|
response, response.content, cache_control, service_name, headers_update=headers_update
|
188
208
|
)
|
189
209
|
|
190
210
|
def _build_response(
|
191
211
|
self,
|
192
|
-
response,
|
193
|
-
content,
|
194
|
-
cache_control,
|
195
|
-
service_name,
|
196
|
-
headers=None,
|
197
|
-
headers_update=None,
|
198
|
-
content_type=None,
|
199
|
-
):
|
200
|
-
if isinstance(content, str):
|
201
|
-
content = content.encode("utf-8")
|
212
|
+
response: pyramid.response.Response,
|
213
|
+
content: bytes,
|
214
|
+
cache_control: Cache,
|
215
|
+
service_name: str,
|
216
|
+
headers: Optional[Dict[str, str]] = None,
|
217
|
+
headers_update: Optional[Dict[str, str]] = None,
|
218
|
+
content_type: Optional[str] = None,
|
219
|
+
) -> pyramid.response.Response:
|
202
220
|
if headers_update is None:
|
203
221
|
headers_update = {}
|
204
222
|
headers = response.headers if headers is None else headers
|
@@ -216,28 +234,28 @@ class Proxy:
|
|
216
234
|
"Trailers",
|
217
235
|
"Transfer-Encoding",
|
218
236
|
"Upgrade",
|
219
|
-
]:
|
237
|
+
]:
|
220
238
|
if header in headers:
|
221
239
|
del headers[header]
|
222
240
|
# Other problematic headers
|
223
|
-
for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
|
241
|
+
for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
|
224
242
|
if header in headers:
|
225
243
|
del headers[header]
|
226
244
|
|
227
245
|
headers.update(headers_update)
|
228
246
|
|
229
|
-
response = Response(content, status=response.status_code, headers=headers)
|
247
|
+
response = pyramid.response.Response(content, status=response.status_code, headers=headers)
|
230
248
|
|
231
249
|
return set_common_headers(
|
232
250
|
self.request, service_name, cache_control, response=response, content_type=content_type
|
233
251
|
)
|
234
252
|
|
235
253
|
@staticmethod
|
236
|
-
def _get_lower_params(params):
|
254
|
+
def _get_lower_params(params: Dict[str, str]) -> Dict[str, str]:
|
237
255
|
return dict((k.lower(), str(v).lower()) for k, v in params.items())
|
238
256
|
|
239
|
-
def
|
240
|
-
headers = self.request.headers
|
241
|
-
if "Cookie" in headers:
|
257
|
+
def get_headers(self) -> Dict[str, str]:
|
258
|
+
headers: Dict[str, str] = self.request.headers
|
259
|
+
if "Cookie" in headers:
|
242
260
|
headers.pop("Cookie")
|
243
261
|
return headers
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
1
|
# Copyright (c) 2012-2021, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
@@ -33,28 +31,35 @@ import logging
|
|
33
31
|
import math
|
34
32
|
import os
|
35
33
|
import traceback
|
36
|
-
from typing import Any, Dict, Optional
|
34
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
37
35
|
|
38
36
|
import numpy
|
37
|
+
import pyramid.request
|
39
38
|
import zope.event.classhandler
|
40
39
|
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound
|
41
40
|
from pyramid.view import view_config
|
41
|
+
from rasterio.io import DatasetReader
|
42
42
|
|
43
43
|
from c2cgeoportal_commons.models import InvalidateCacheEvent
|
44
|
-
from c2cgeoportal_geoportal.lib.
|
44
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
45
|
+
|
46
|
+
if TYPE_CHECKING:
|
47
|
+
import fiona.collection
|
45
48
|
|
46
49
|
LOG = logging.getLogger(__name__)
|
47
50
|
|
48
51
|
|
49
52
|
class Raster:
|
50
|
-
|
53
|
+
"""All the view concerned the raster (point, not the profile profile)."""
|
54
|
+
|
55
|
+
data: Dict[str, "fiona.collection.Collection"] = {}
|
51
56
|
|
52
|
-
def __init__(self, request):
|
57
|
+
def __init__(self, request: pyramid.request.Request):
|
53
58
|
self.request = request
|
54
59
|
self.rasters = self.request.registry.settings["raster"]
|
55
60
|
|
56
|
-
@zope.event.classhandler.handler(InvalidateCacheEvent)
|
57
|
-
def handle(event: InvalidateCacheEvent)
|
61
|
+
@zope.event.classhandler.handler(InvalidateCacheEvent) # type: ignore
|
62
|
+
def handle(event: InvalidateCacheEvent) -> None:
|
58
63
|
del event
|
59
64
|
for _, v in Raster.data.items():
|
60
65
|
v.close()
|
@@ -62,21 +67,21 @@ class Raster:
|
|
62
67
|
|
63
68
|
def _get_required_finite_float_param(self, name: str) -> float:
|
64
69
|
if name not in self.request.params:
|
65
|
-
raise HTTPBadRequest("'{}' should be in the query string parameters"
|
70
|
+
raise HTTPBadRequest(f"'{name}' should be in the query string parameters")
|
66
71
|
try:
|
67
72
|
result = float(self.request.params[name])
|
68
73
|
except ValueError:
|
69
|
-
raise HTTPBadRequest(
|
70
|
-
"'{}' ({}) parameters should be a number"
|
74
|
+
raise HTTPBadRequest( # pylint: disable=raise-missing-from
|
75
|
+
f"'{name}' ({self.request.params[name]}) parameters should be a number"
|
71
76
|
)
|
72
77
|
if not math.isfinite(result):
|
73
78
|
raise HTTPBadRequest(
|
74
|
-
"'{}' ({}) parameters should be a finite number"
|
79
|
+
f"'{name}' ({self.request.params[name]}) parameters should be a finite number"
|
75
80
|
)
|
76
81
|
return result
|
77
82
|
|
78
|
-
@view_config(route_name="raster", renderer="fast_json")
|
79
|
-
def raster(self):
|
83
|
+
@view_config(route_name="raster", renderer="fast_json") # type: ignore
|
84
|
+
def raster(self) -> Dict[str, Any]:
|
80
85
|
lon = self._get_required_finite_float_param("lon")
|
81
86
|
lat = self._get_required_finite_float_param("lat")
|
82
87
|
|
@@ -87,7 +92,7 @@ class Raster:
|
|
87
92
|
if layer in self.rasters:
|
88
93
|
rasters[layer] = self.rasters[layer]
|
89
94
|
else:
|
90
|
-
raise HTTPNotFound("Layer {} not found"
|
95
|
+
raise HTTPNotFound(f"Layer {layer} not found")
|
91
96
|
else:
|
92
97
|
rasters = self.rasters
|
93
98
|
|
@@ -95,10 +100,10 @@ class Raster:
|
|
95
100
|
for ref in list(rasters.keys()):
|
96
101
|
result[ref] = self._get_raster_value(rasters[ref], ref, lon, lat)
|
97
102
|
|
98
|
-
set_common_headers(self.request, "raster",
|
103
|
+
set_common_headers(self.request, "raster", Cache.PUBLIC_NO)
|
99
104
|
return result
|
100
105
|
|
101
|
-
def _get_data(self, layer, name):
|
106
|
+
def _get_data(self, layer: Dict[str, Any], name: str) -> "fiona.collection.Collection":
|
102
107
|
if name not in self.data:
|
103
108
|
path = layer["file"]
|
104
109
|
if layer.get("type", "shp_index") == "shp_index":
|
@@ -114,7 +119,9 @@ class Raster:
|
|
114
119
|
|
115
120
|
return self.data[name]
|
116
121
|
|
117
|
-
def _get_raster_value(
|
122
|
+
def _get_raster_value(
|
123
|
+
self, layer: Dict[str, Any], name: str, lon: float, lat: float
|
124
|
+
) -> Optional[decimal.Decimal]:
|
118
125
|
data = self._get_data(layer, name)
|
119
126
|
type_ = layer.get("type", "shp_index")
|
120
127
|
if type_ == "shp_index":
|
@@ -135,21 +142,25 @@ class Raster:
|
|
135
142
|
else:
|
136
143
|
raise ValueError("Unsupported type " + type_)
|
137
144
|
|
138
|
-
|
139
|
-
|
145
|
+
result_d = None
|
146
|
+
if "round" in layer and result is not None:
|
147
|
+
result_d = self._round(result, layer["round"])
|
140
148
|
elif result is not None:
|
141
|
-
|
149
|
+
result_d = decimal.Decimal(str(result))
|
142
150
|
|
143
|
-
return
|
151
|
+
return result_d
|
144
152
|
|
145
153
|
@staticmethod
|
146
|
-
def _get_value(
|
154
|
+
def _get_value(
|
155
|
+
layer: Dict[str, Any], name: str, dataset: DatasetReader, lon: float, lat: float
|
156
|
+
) -> Optional[numpy.float32]:
|
147
157
|
index = dataset.index(lon, lat)
|
148
158
|
|
149
159
|
shape = dataset.shape
|
160
|
+
result: Optional[numpy.float32]
|
150
161
|
if 0 <= index[0] < shape[0] and 0 <= index[1] < shape[1]:
|
151
162
|
|
152
|
-
def get_index(index_):
|
163
|
+
def get_index(index_: int) -> Tuple[int, int]:
|
153
164
|
return index_, index_ + 1
|
154
165
|
|
155
166
|
result = dataset.read(1, window=(get_index(index[0]), get_index(index[1])))[0][0]
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2011-2020, 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
|
@@ -31,41 +29,45 @@
|
|
31
29
|
import ast
|
32
30
|
import logging
|
33
31
|
|
32
|
+
import pyramid.request
|
33
|
+
import pyramid.response
|
34
34
|
from pyramid.httpexceptions import HTTPBadRequest
|
35
35
|
from pyramid.view import view_config
|
36
36
|
|
37
|
-
from c2cgeoportal_geoportal.lib.
|
37
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache
|
38
38
|
from c2cgeoportal_geoportal.views.proxy import Proxy
|
39
39
|
|
40
40
|
LOG = logging.getLogger(__name__)
|
41
41
|
|
42
42
|
|
43
43
|
class ResourceProxy(Proxy):
|
44
|
-
|
44
|
+
"""All the views concerned the resources (it's a kind of proxy)."""
|
45
|
+
|
46
|
+
def __init__(self, request: pyramid.request.Request):
|
45
47
|
Proxy.__init__(self, request)
|
46
48
|
self.request = request
|
47
49
|
self.settings = request.registry.settings.get("resourceproxy", {})
|
48
50
|
|
49
|
-
@view_config(route_name="resourceproxy")
|
50
|
-
def proxy(self)
|
51
|
+
@view_config(route_name="resourceproxy") # type: ignore
|
52
|
+
def proxy(self) -> pyramid.response.Response:
|
51
53
|
target = self.request.params.get("target", "")
|
52
54
|
targets = self.settings.get("targets", [])
|
53
|
-
if target in targets:
|
55
|
+
if target in targets:
|
54
56
|
url = targets[target]
|
55
57
|
values = ast.literal_eval(self.request.params.get("values"))
|
56
58
|
url = url % values
|
57
59
|
|
58
60
|
response = self._proxy(url=url)
|
59
61
|
|
60
|
-
cache_control =
|
62
|
+
cache_control = Cache.PRIVATE_NO
|
61
63
|
content_type = response.headers["Content-Type"]
|
62
64
|
|
63
65
|
response = self._build_response(
|
64
|
-
response, response.
|
66
|
+
response, response.content, cache_control, "externalresource", content_type=content_type
|
65
67
|
)
|
66
68
|
for header in response.headers.keys():
|
67
69
|
if header not in self.settings["allowed_headers"]:
|
68
70
|
response.headers.pop(header)
|
69
71
|
return response
|
70
|
-
LOG.warning("Target
|
72
|
+
LOG.warning("Target URL not found: %s", target)
|
71
73
|
return HTTPBadRequest("URL not allowed")
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2013-2021, Camptocamp SA
|
1
|
+
# Copyright (c) 2013-2022, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -32,43 +30,47 @@ import logging
|
|
32
30
|
import random
|
33
31
|
import string
|
34
32
|
from datetime import datetime
|
33
|
+
from typing import Dict
|
35
34
|
from urllib.parse import urlparse
|
36
35
|
|
36
|
+
import pyramid.request
|
37
37
|
from pyramid.httpexceptions import HTTPBadRequest, HTTPFound, HTTPInternalServerError, HTTPNotFound
|
38
38
|
from pyramid.view import view_config
|
39
39
|
|
40
40
|
from c2cgeoportal_commons.lib.email_ import send_email_config
|
41
41
|
from c2cgeoportal_commons.models import DBSession
|
42
42
|
from c2cgeoportal_commons.models.static import Shorturl
|
43
|
-
from c2cgeoportal_geoportal.lib.
|
43
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
44
44
|
|
45
45
|
logger = logging.getLogger(__name__)
|
46
46
|
|
47
47
|
|
48
48
|
class Shortener:
|
49
|
-
|
49
|
+
"""All the views conserne the shortener."""
|
50
|
+
|
51
|
+
def __init__(self, request: pyramid.request.Request):
|
50
52
|
self.request = request
|
51
53
|
self.settings = request.registry.settings.get("shortener", {})
|
52
54
|
self.short_bases = [self.request.route_url("shortener_get", ref="")]
|
53
55
|
if "base_url" in self.settings:
|
54
56
|
self.short_bases.append(self.settings["base_url"])
|
55
57
|
|
56
|
-
@view_config(route_name="shortener_get")
|
57
|
-
def get(self):
|
58
|
+
@view_config(route_name="shortener_get") # type: ignore
|
59
|
+
def get(self) -> HTTPFound:
|
58
60
|
ref = self.request.matchdict["ref"]
|
59
61
|
short_urls = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()
|
60
62
|
|
61
63
|
if len(short_urls) != 1:
|
62
|
-
raise HTTPNotFound("Ref '{
|
64
|
+
raise HTTPNotFound(f"Ref '{ref!s}' not found")
|
63
65
|
|
64
66
|
short_urls[0].nb_hits += 1
|
65
67
|
short_urls[0].last_hit = datetime.now()
|
66
68
|
|
67
|
-
set_common_headers(self.request, "shortener",
|
69
|
+
set_common_headers(self.request, "shortener", Cache.PUBLIC_NO)
|
68
70
|
return HTTPFound(location=short_urls[0].url)
|
69
71
|
|
70
|
-
@view_config(route_name="shortener_create", renderer="json")
|
71
|
-
def create(self):
|
72
|
+
@view_config(route_name="shortener_create", renderer="json") # type: ignore
|
73
|
+
def create(self) -> Dict[str, str]:
|
72
74
|
|
73
75
|
if "url" not in self.request.params:
|
74
76
|
raise HTTPBadRequest("The parameter url is required")
|
@@ -76,23 +78,22 @@ class Shortener:
|
|
76
78
|
url = self.request.params["url"]
|
77
79
|
|
78
80
|
# see: https://httpd.apache.org/docs/2.2/mod/core.html#limitrequestline
|
79
|
-
if len(url) > 8190:
|
80
|
-
raise HTTPBadRequest("The parameter url is too long ({} > {})"
|
81
|
+
if len(url) > 8190:
|
82
|
+
raise HTTPBadRequest(f"The parameter url is too long ({len(url)} > {8190})")
|
81
83
|
|
82
84
|
# Check that it is an internal URL...
|
83
85
|
uri_parts = urlparse(url)
|
84
86
|
if "allowed_hosts" in self.settings:
|
85
|
-
if uri_parts.netloc not in self.settings["allowed_hosts"]:
|
87
|
+
if uri_parts.netloc not in self.settings["allowed_hosts"]:
|
86
88
|
raise HTTPBadRequest(
|
87
|
-
"The requested host '{}' is not part of allowed hosts:
|
88
|
-
|
89
|
-
)
|
89
|
+
f"The requested host '{uri_parts.netloc}' is not part of allowed hosts: "
|
90
|
+
f"{', '.join(self.settings['allowed_hosts'])}"
|
90
91
|
)
|
91
92
|
else:
|
92
93
|
hostname = uri_parts.hostname
|
93
94
|
if hostname != self.request.server_name:
|
94
95
|
raise HTTPBadRequest(
|
95
|
-
"The requested host '{
|
96
|
+
f"The requested host '{hostname!s}' should be '{self.request.server_name!s}'"
|
96
97
|
)
|
97
98
|
|
98
99
|
shortened = False
|
@@ -106,16 +107,16 @@ class Shortener:
|
|
106
107
|
tries = 0
|
107
108
|
while not shortened:
|
108
109
|
ref = "".join(
|
109
|
-
random.choice(string.ascii_letters + string.digits)
|
110
|
+
random.choice(string.ascii_letters + string.digits) # nosec
|
110
111
|
for i in range(self.settings.get("length", 4))
|
111
112
|
)
|
112
113
|
test_url = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()
|
113
114
|
if not test_url:
|
114
115
|
break
|
115
|
-
tries += 1
|
116
|
-
if tries > 20:
|
116
|
+
tries += 1
|
117
|
+
if tries > 20:
|
117
118
|
message = "No free ref found, considered to increase the length"
|
118
|
-
|
119
|
+
logger.error(message)
|
119
120
|
raise HTTPInternalServerError(message)
|
120
121
|
|
121
122
|
user_email = self.request.user.email if self.request.user is not None else None
|
@@ -135,7 +136,7 @@ class Shortener:
|
|
135
136
|
else:
|
136
137
|
s_url = self.request.route_url("shortener_get", ref=ref)
|
137
138
|
|
138
|
-
if email is not None:
|
139
|
+
if email is not None:
|
139
140
|
send_email_config(
|
140
141
|
self.request.registry.settings,
|
141
142
|
"shortener",
|
@@ -143,7 +144,9 @@ class Shortener:
|
|
143
144
|
full_url=url,
|
144
145
|
short_url=s_url,
|
145
146
|
message=self.request.params.get("message", ""),
|
147
|
+
application_url=self.request.route_url("base"),
|
148
|
+
current_url=self.request.current_route_url(),
|
146
149
|
)
|
147
150
|
|
148
|
-
set_common_headers(self.request, "shortener",
|
151
|
+
set_common_headers(self.request, "shortener", Cache.PRIVATE_NO)
|
149
152
|
return {"short_url": s_url}
|