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