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.
- c2cgeoportal_geoportal/__init__.py +261 -130
- c2cgeoportal_geoportal/lib/__init__.py +74 -120
- c2cgeoportal_geoportal/lib/authentication.py +170 -21
- c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
- c2cgeoportal_geoportal/lib/cacheversion.py +19 -11
- c2cgeoportal_geoportal/lib/caching.py +66 -160
- c2cgeoportal_geoportal/lib/check_collector.py +17 -10
- c2cgeoportal_geoportal/lib/checker.py +62 -64
- c2cgeoportal_geoportal/lib/common_headers.py +170 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +70 -31
- c2cgeoportal_geoportal/lib/filter_capabilities.py +127 -97
- c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
- c2cgeoportal_geoportal/lib/functionality.py +36 -21
- c2cgeoportal_geoportal/lib/headers.py +14 -5
- c2cgeoportal_geoportal/lib/i18n.py +39 -0
- c2cgeoportal_geoportal/lib/layers.py +29 -10
- c2cgeoportal_geoportal/lib/lingua_extractor.py +408 -211
- c2cgeoportal_geoportal/lib/loader.py +18 -16
- c2cgeoportal_geoportal/lib/metrics.py +29 -18
- c2cgeoportal_geoportal/lib/oauth2.py +1036 -0
- c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
- c2cgeoportal_geoportal/lib/xsd.py +29 -19
- 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/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -4
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +24 -15
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +1 -0
- 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/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
- 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 +4 -4
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +47 -0
- 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/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +3 -6
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +32 -20
- 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} +4 -8
- 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} +34 -24
- 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/{{cookiecutter.project}}/ci/config.yaml +25 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +474 -0
- 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} +43 -18
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +82 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +60 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +396 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +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 +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +17 -9
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +30 -27
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
- 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/{{cookiecutter.project}}/pyproject.toml +3 -0
- 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/{{cookiecutter.project}}/setup.cfg +7 -0
- 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/{{cookiecutter.project}}/.upgrade.yaml +191 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1160 -0
- c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +99 -47
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1410 -0
- c2cgeoportal_geoportal/scripts/__init__.py +18 -32
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +295 -200
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +5 -6
- c2cgeoportal_geoportal/scripts/manage_users.py +34 -37
- c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +92 -25
- c2cgeoportal_geoportal/scripts/urllogin.py +23 -17
- c2cgeoportal_geoportal/templates/login.html +88 -84
- c2cgeoportal_geoportal/templates/notlogin.html +62 -0
- 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 +71 -40
- c2cgeoportal_geoportal/views/entry.py +93 -24
- c2cgeoportal_geoportal/views/fulltextsearch.py +36 -29
- c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
- c2cgeoportal_geoportal/views/i18n.py +91 -9
- c2cgeoportal_geoportal/views/layers.py +173 -134
- c2cgeoportal_geoportal/views/login.py +206 -87
- c2cgeoportal_geoportal/views/mapserverproxy.py +59 -35
- c2cgeoportal_geoportal/views/memory.py +13 -13
- c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
- c2cgeoportal_geoportal/views/pdfreport.py +31 -27
- c2cgeoportal_geoportal/views/printproxy.py +70 -54
- c2cgeoportal_geoportal/views/profile.py +25 -24
- c2cgeoportal_geoportal/views/proxy.py +100 -70
- c2cgeoportal_geoportal/views/raster.py +47 -29
- c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
- c2cgeoportal_geoportal/views/shortener.py +31 -24
- c2cgeoportal_geoportal/views/theme.py +475 -365
- c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
- c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/METADATA +17 -11
- c2cgeoportal_geoportal-2.7.1.156.dist-info/RECORD +185 -0
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/WHEEL +1 -1
- {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.156.dist-info}/entry_points.txt +3 -1
- tests/__init__.py +24 -3
- tests/test_cachebuster.py +1 -3
- tests/test_caching.py +19 -26
- tests/test_checker.py +2 -3
- tests/test_decimaljson.py +4 -4
- tests/test_headerstween.py +0 -3
- tests/test_i18n.py +31 -0
- tests/test_init.py +12 -27
- tests/test_locale_negociator.py +6 -6
- tests/test_mapserverproxy_route_predicate.py +0 -2
- tests/test_raster.py +18 -5
- tests/test_wmstparsing.py +7 -8
- c2cgeoportal_geoportal/scaffolds/__init__.py +0 -226
- c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -11
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/ci.yaml_tmpl +0 -56
- c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -12
- c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -144
- c2cgeoportal_geoportal/scaffolds/create/docker-compose-lib.yaml_tmpl +0 -302
- c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml_tmpl +0 -54
- c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -49
- c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -39
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -5
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -19
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -48
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/images/favicon.ico +0 -0
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl +0 -37
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl +0 -22
- c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
- c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
- c2cgeoportal_geoportal/scaffolds/create/geoportal/setup.py_tmpl +0 -18
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
- c2cgeoportal_geoportal/scaffolds/create/geoportal/vars.yaml_tmpl +0 -224
- c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -71
- 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 -166
- c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/legend.jrxml +0 -27
- c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -29
- c2cgeoportal_geoportal/scaffolds/create/scripts/publish-docker +0 -124
- c2cgeoportal_geoportal/scaffolds/create/setup.cfg_tmpl +0 -3
- c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -169
- c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
- c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -171
- c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -64
- c2cgeoportal_geoportal/scaffolds/update/geoportal/CONST_vars.yaml_tmpl +0 -783
- c2cgeoportal_geoportal/templates/dynamic.js +0 -21
- c2cgeoportal_geoportal-2.5.0.100.dist-info/RECORD +0 -162
- tests/test_get_url.py +0 -96
- tests/test_lib.py +0 -77
- /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/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/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}}}/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/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
- {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
|
-
#
|
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
|
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
|
-
|
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."
|
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]."
|
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
|
-
|
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.
|
163
|
-
|
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 '{}'."
|
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
|
170
|
-
|
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
|
177
|
-
|
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:
|
232
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
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
|
-
#
|
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
|
42
|
-
|
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
|
-
#
|
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(
|
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
|
-
"""
|
57
|
+
"""Get back the cachebuster URL."""
|
54
58
|
|
55
|
-
def __init__(
|
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
|