xlwings-server 1.1.0__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.
- xlwings_server/.env.template +145 -0
- xlwings_server/__init__.py +12 -0
- xlwings_server/_version.py +34 -0
- xlwings_server/auth/__init__.py +0 -0
- xlwings_server/auth/custom/__init__.py +26 -0
- xlwings_server/auth/entraid/__init__.py +131 -0
- xlwings_server/auth/entraid/jwks.py +10 -0
- xlwings_server/azure_functions_templates/.funcignore +28 -0
- xlwings_server/azure_functions_templates/function_app.py +28 -0
- xlwings_server/azure_functions_templates/host.json +22 -0
- xlwings_server/azure_functions_templates/local.settings.json +8 -0
- xlwings_server/build_utils/__init__.py +9 -0
- xlwings_server/build_utils/static_file_hasher.py +212 -0
- xlwings_server/cli.py +1592 -0
- xlwings_server/config.py +228 -0
- xlwings_server/custom_functions/__init__.py +8 -0
- xlwings_server/custom_functions/examples.py +177 -0
- xlwings_server/custom_scripts/__init__.py +8 -0
- xlwings_server/custom_scripts/examples.py +94 -0
- xlwings_server/databases.py +19 -0
- xlwings_server/dependencies.py +126 -0
- xlwings_server/docker_templates/.dockerignore +15 -0
- xlwings_server/docker_templates/Dockerfile +60 -0
- xlwings_server/docker_templates/docker-compose.yaml +32 -0
- xlwings_server/hotreload.py +59 -0
- xlwings_server/main.py +242 -0
- xlwings_server/models/__init__.py +14 -0
- xlwings_server/models/user.py +53 -0
- xlwings_server/object_handles.py +142 -0
- xlwings_server/routers/__init__.py +0 -0
- xlwings_server/routers/manifest.py +82 -0
- xlwings_server/routers/root.py +16 -0
- xlwings_server/routers/socketio.py +69 -0
- xlwings_server/routers/taskpane.py +12 -0
- xlwings_server/routers/xlwings.py +197 -0
- xlwings_server/security_headers.json +53 -0
- xlwings_server/serializers/__init__.py +25 -0
- xlwings_server/serializers/default_serializer.py +19 -0
- xlwings_server/serializers/dictionary_serializer.py +25 -0
- xlwings_server/serializers/framework.py +50 -0
- xlwings_server/serializers/numpy_serializer.py +26 -0
- xlwings_server/serializers/pandas_serializer.py +95 -0
- xlwings_server/static/css/core.css +28 -0
- xlwings_server/static/css/style.css +0 -0
- xlwings_server/static/images/favicon.png +0 -0
- xlwings_server/static/images/xlwings-16.png +0 -0
- xlwings_server/static/images/xlwings-32.png +0 -0
- xlwings_server/static/images/xlwings-64.png +0 -0
- xlwings_server/static/images/xlwings-80.png +0 -0
- xlwings_server/static/js/auth.js +13 -0
- xlwings_server/static/js/config.js +4 -0
- xlwings_server/static/js/core/alpinejs-csp-boilerplate.js +11 -0
- xlwings_server/static/js/core/bootstrap-customizations.js +7 -0
- xlwings_server/static/js/core/custom-functions-code.js +296 -0
- xlwings_server/static/js/core/examples.js +62 -0
- xlwings_server/static/js/core/hotreload.js +3 -0
- xlwings_server/static/js/core/htmx-handlers.js +86 -0
- xlwings_server/static/js/core/officejs-history-fix-part1.js +3 -0
- xlwings_server/static/js/core/officejs-history-fix-part2.js +2 -0
- xlwings_server/static/js/core/reload-custom-functions.js +79 -0
- xlwings_server/static/js/core/socketio-handlers.js +34 -0
- xlwings_server/static/js/core/xlwings-alert.js +22 -0
- xlwings_server/static/js/core/xlwingsjs/alert.js +85 -0
- xlwings_server/static/js/core/xlwingsjs/auth.js +63 -0
- xlwings_server/static/js/core/xlwingsjs/sheet-buttons.js +133 -0
- xlwings_server/static/js/core/xlwingsjs/utils.js +119 -0
- xlwings_server/static/js/core/xlwingsjs/wasm.js +131 -0
- xlwings_server/static/js/core/xlwingsjs/xlwings.js +1060 -0
- xlwings_server/static/js/main.js +0 -0
- xlwings_server/static/js/ribbon.js +17 -0
- xlwings_server/static/vendor/@alpinejs/LICENSE +21 -0
- xlwings_server/static/vendor/@alpinejs/csp/dist/cdn.min.js +7 -0
- xlwings_server/static/vendor/@microsoft/office-js/LICENSE.md +76 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/af-za/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/agaveerrorux.js +18 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon32x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon96x96.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/businessbarclose_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/dropdownarrow_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/ellipsis_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/miniinfoblue_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_default_icon.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_status_icons.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/office.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/refresh_16x16x32.png +0 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/index.html +16 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/style/agaveerrorux.css +482 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/am-et/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ae/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-bh/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-dz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-eg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-iq/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-jo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-kw/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-lb/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ly/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ma/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-om/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-qa/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sa/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sy/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-tn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ye/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.8.0.min.js +2 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.9.0.min.js +2 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/az-latn-az/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/be-by/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bg-bg/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bn-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/bs-latn-ba/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ca-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/cs-cz/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/cy-gb/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/da-dk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-at/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-de/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-li/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/de-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/el-gr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-029/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-au/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-bz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ca/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-gb/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ie/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-in/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-jm/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-my/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-nz/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-ph/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-sg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-tt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-us/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-za/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/en-zw/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ar/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-bo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-cl/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-co/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-cr/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-do/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ec/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-gt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-hn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-mx/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ni/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pa/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pe/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-pr/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-py/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-sv/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-us/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-uy/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es-ve/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/es6-promise.js +5 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/et-ee/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/eu-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.01.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.02.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.00.js +19 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01-core.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excel-winrt-16.00.js +25 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelios-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.01.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.02.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fa-ir/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fi-fi/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fil-ph/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-be/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ca/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-fr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/fr-mc/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ga-ie/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/gl-es/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/gu-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/he-il/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hi-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hr-ba/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hr-hr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/html2canvas.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hu-hu/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/hy-am/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/id-id/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/is-is/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/it-ch/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/it-it/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ja-jp/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ka-ge/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/kk-kz/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/km-kh/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/kn-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ko-kr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lb-lu/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lo-la/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lt-lt/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/lv-lv/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mk-mk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ml-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mn-mn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mr-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ms-bn/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ms-my/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/mt-mt/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nb-no/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ne-np/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nl-be/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nl-nl/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/nn-no/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/o15apptofilemappingtable.js +11 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/office-vsdoc.js +28596 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/office.js +84 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pl-pl/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pt-br/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/pt-pt/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ro-ro/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ru-ru/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/si-lk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sk-sk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sl-si/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sq-al/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-cs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-rs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-cs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-rs/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sv-fi/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sv-se/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/sw-ke/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ta-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/te-in/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs_agave.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/th-th/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/tr-tr/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/uk-ua/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/ur-pk/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/vi-vn/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.browserauth.js +77 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.implicit.js +35 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-cn/office_strings.js +1 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-hk/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-mo/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-sg/office_strings.js +8 -0
- xlwings_server/static/vendor/@microsoft/office-js/dist/zh-tw/office_strings.js +1 -0
- xlwings_server/static/vendor/axios/dist/axios.min.js +3 -0
- xlwings_server/static/vendor/axios/dist/axios.min.js.map +1 -0
- xlwings_server/static/vendor/bootstrap/LICENSE +21 -0
- xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
- xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
- xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css +12 -0
- xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css.map +1 -0
- xlwings_server/static/vendor/htmx-ext-head-support/head-support.js +144 -0
- xlwings_server/static/vendor/htmx-ext-loading-states/loading-states.js +184 -0
- xlwings_server/static/vendor/htmx.org/LICENSE +13 -0
- xlwings_server/static/vendor/htmx.org/dist/htmx.min.js +1 -0
- xlwings_server/static/vendor/socket.io/LICENSE +22 -0
- xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js +7 -0
- xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js.map +1 -0
- xlwings_server/templates/_book.html +8 -0
- xlwings_server/templates/alert_base.html +16 -0
- xlwings_server/templates/base.html +117 -0
- xlwings_server/templates/examples/alpine/README.md +26 -0
- xlwings_server/templates/examples/alpine/taskpane.html +47 -0
- xlwings_server/templates/examples/auth/README.md +38 -0
- xlwings_server/templates/examples/auth/protected.html +8 -0
- xlwings_server/templates/examples/auth/public.html +11 -0
- xlwings_server/templates/examples/excel_object_model/README.md +49 -0
- xlwings_server/templates/examples/excel_object_model/add_name_form.html +27 -0
- xlwings_server/templates/examples/hello_world/README.md +9 -0
- xlwings_server/templates/examples/hello_world/taskpane_hello.html +24 -0
- xlwings_server/templates/examples/htmx_form/README.md +44 -0
- xlwings_server/templates/examples/htmx_form/_greeting.html +6 -0
- xlwings_server/templates/examples/htmx_form/taskpane_htmx_form.html +21 -0
- xlwings_server/templates/examples/live_form_validation/README.md +60 -0
- xlwings_server/templates/examples/live_form_validation/add_name_form.html +33 -0
- xlwings_server/templates/examples/multi_app/README.md +34 -0
- xlwings_server/templates/examples/multi_app/taskpane1.html +7 -0
- xlwings_server/templates/examples/multi_app/taskpane2.html +7 -0
- xlwings_server/templates/examples/multi_app/taskpane_loader.html +5 -0
- xlwings_server/templates/examples/navigation/README.md +28 -0
- xlwings_server/templates/examples/navigation/_navigation.html +16 -0
- xlwings_server/templates/examples/navigation/taskpane_one.html +8 -0
- xlwings_server/templates/examples/navigation/taskpane_three.html +8 -0
- xlwings_server/templates/examples/navigation/taskpane_two.html +8 -0
- xlwings_server/templates/examples/pictures/README.md +42 -0
- xlwings_server/templates/examples/pictures/_picture.html +4 -0
- xlwings_server/templates/examples/pictures/taskpane_pictures.html +26 -0
- xlwings_server/templates/manifest.xml +155 -0
- xlwings_server/templates/taskpane.html +1 -0
- xlwings_server/templates/xlwings_alert.html +27 -0
- xlwings_server/templates.py +61 -0
- xlwings_server/utils.py +32 -0
- xlwings_server/wasm/__init__.py +0 -0
- xlwings_server/wasm/config.py +24 -0
- xlwings_server/wasm/main.py +236 -0
- xlwings_server/wasm/requirements.txt +5 -0
- xlwings_server-1.1.0.dist-info/METADATA +61 -0
- xlwings_server-1.1.0.dist-info/RECORD +313 -0
- xlwings_server-1.1.0.dist-info/WHEEL +4 -0
- xlwings_server-1.1.0.dist-info/entry_points.txt +2 -0
- xlwings_server-1.1.0.dist-info/licenses/LICENSE.md +223 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Get a free trial key from https://www.xlwings.org/trial
|
|
2
|
+
XLWINGS_LICENSE_KEY="your_license_key"
|
|
3
|
+
|
|
4
|
+
# Use one of "dev", "qa", "uat", "staging", or "prod". "dev" will activate hotreload for
|
|
5
|
+
# the task pane and other dev-specific features. This setting also takes care of loading
|
|
6
|
+
# the correct ID in the manifest. Except for "prod", the environment name will show up
|
|
7
|
+
# in the add-in name (ProjectName [dev]) and custom functions (NAMESPACE_DEV.MYFUNC)
|
|
8
|
+
XLWINGS_ENVIRONMENT="dev"
|
|
9
|
+
|
|
10
|
+
# Secret key is generated by "xlwings-server init"
|
|
11
|
+
XLWINGS_SECRET_KEY=""
|
|
12
|
+
|
|
13
|
+
# This sets the HTTP response headers recommended by OWASP. Some of the headers have to
|
|
14
|
+
# be less restrictive if XLWINGS_ENABLE_EXCEL_ONLINE=true. Note that this only applies
|
|
15
|
+
# to xlwings Server, not xlwings Wasm.
|
|
16
|
+
# XLWINGS_ADD_SECURITY_HEADERS=true
|
|
17
|
+
|
|
18
|
+
# If you mount xlwings Server on a non-root path (e.g., https://my.domain.com/myapp)
|
|
19
|
+
# via a reverse proxy such as nginx, you need to set this to: "/myapp". You most likely
|
|
20
|
+
# also need to set the XLWINGS_STATIC_URL_PATH="/myapp/static"
|
|
21
|
+
# XLWINGS_APP_PATH=""
|
|
22
|
+
|
|
23
|
+
# To authenticate users, provide the Auth providers as list.
|
|
24
|
+
# E.g, to enable Entra ID SSO auth: XLWINGS_AUTH_PROVIDERS=["entraid"]. If you want
|
|
25
|
+
# to accept multipe authentication methods, you will need the name as
|
|
26
|
+
# Auth-Provider header from the client.
|
|
27
|
+
# XLWINGS_AUTH_PROVIDERS=[]
|
|
28
|
+
|
|
29
|
+
# Enable Single Sign-On (SSO) via Microsoft Entra ID (previously Azure AD)
|
|
30
|
+
# XLWINGS_AUTH_ENTRAID_CLIENT_ID=
|
|
31
|
+
# XLWINGS_AUTH_ENTRAID_TENANT_ID=
|
|
32
|
+
|
|
33
|
+
# RBAC (role-based access control)
|
|
34
|
+
# If your auth provider supports roles, you can list the required roles here.
|
|
35
|
+
# E.g.: XLWINGS_AUTH_REQUIRED_ROLES=["xlwings.user"]
|
|
36
|
+
# XLWINGS_AUTH_REQUIRED_ROLES=[]
|
|
37
|
+
|
|
38
|
+
# Set this to true if you have users from external organizations
|
|
39
|
+
# XLWINGS_AUTH_ENTRAID_MULTITENANT=false
|
|
40
|
+
|
|
41
|
+
# If the add-in will be distributed via Microsoft's public add-in store, set this to
|
|
42
|
+
# true to load office.js via their CDN
|
|
43
|
+
# XLWINGS_CDN_OFFICEJS=false
|
|
44
|
+
|
|
45
|
+
# By default, xlwings Wasm uses the CDN for Pyodide. If you set this to false,
|
|
46
|
+
# Excel won't require any connection to the public Internet as everything will be served
|
|
47
|
+
# from the static file server. This, however, requires you to provide all Python wheels
|
|
48
|
+
# via the app/static/vendors/pyodide folder.
|
|
49
|
+
# XLWINGS_CDN_PYODIDE=true
|
|
50
|
+
|
|
51
|
+
# If you use the Office Scripts integration or custom functions in Excel on the web,
|
|
52
|
+
# you need to set this to ["*"]. Otherwise you should disable it by setting it to [].
|
|
53
|
+
XLWINGS_CORS_ALLOW_ORIGINS=["*"]
|
|
54
|
+
|
|
55
|
+
# Maximum number of retry attempts for failed custom function requests
|
|
56
|
+
# XLWINGS_CUSTOM_FUNCTIONS_MAX_RETRIES=3
|
|
57
|
+
|
|
58
|
+
# HTTP status codes that should trigger retries for custom function requests
|
|
59
|
+
# XLWINGS_CUSTOM_FUNCTIONS_RETRY_CODES=[500, 502, 504]
|
|
60
|
+
|
|
61
|
+
# This allows you to override the date format for custom functions globally.
|
|
62
|
+
# Example: XLWINGS_DATE_FORMAT="m/d/yyyy"
|
|
63
|
+
# XLWINGS_DATE_FORMAT=
|
|
64
|
+
|
|
65
|
+
# Task pane HTML file
|
|
66
|
+
# XLWINGS_TASKPANE_HTML="taskpane.html"
|
|
67
|
+
|
|
68
|
+
# This loads Alpine.js (CSP build) for client-side interactions
|
|
69
|
+
# see: https://alpinejs.dev/advanced/csp
|
|
70
|
+
# XLWINGS_ENABLE_ALPINEJS_CSP=true
|
|
71
|
+
|
|
72
|
+
# This loads Bootstrap with the xlwings theme
|
|
73
|
+
# XLWINGS_ENABLE_BOOTSTRAP=true
|
|
74
|
+
|
|
75
|
+
# Excel on the web requires less strict security headers
|
|
76
|
+
# XLWINGS_ENABLE_EXCEL_ONLINE=true
|
|
77
|
+
|
|
78
|
+
# Enables hot reloading of Office.js add-ins.
|
|
79
|
+
# Requires XLWINGS_ENVIRONMENT="dev" and XLWINGS_ENABLE_SOCKETIO=true.
|
|
80
|
+
# XLWINGS_ENABLE_HOTRELOAD=true
|
|
81
|
+
|
|
82
|
+
# This loads htmx for client-server interaction, see https://htmx.org
|
|
83
|
+
# XLWINGS_ENABLE_HTMX=true
|
|
84
|
+
|
|
85
|
+
# If true, it uses xlwings Wasm instead of xlwings Server. This will use Python
|
|
86
|
+
# via WASM and won't require a Python installation on the backend. This allows you to
|
|
87
|
+
# deploy the add-in to a static file server such as GitHub Pages or Cloudflare Pages.
|
|
88
|
+
# XLWINGS_ENABLE_WASM=false
|
|
89
|
+
|
|
90
|
+
# This loads Socket.io, which is required for streaming custom functions
|
|
91
|
+
# and can be used for realtime interaction on the task pane. Note that this must also
|
|
92
|
+
# be true to enable hotreloading of the taskpane during development.
|
|
93
|
+
# XLWINGS_ENABLE_SOCKETIO=true
|
|
94
|
+
|
|
95
|
+
# Provide custom headers in the form {"header1": "value1", "header2": "value2"}. E.g. for CSP header:
|
|
96
|
+
# {"Content-Security-Policy": "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content; img-src 'self' data:"}
|
|
97
|
+
# For Excel online, you need to remove "frame-ancestors 'none' and append: "; font-src 'self' https://res-1.cdn.office.net; style-src 'self' 'unsafe-inline'"
|
|
98
|
+
# With XLWINGS_CDN_OFFICEJS=true, you need to append "; script-src 'self' https://appsforoffice.microsoft.com;"
|
|
99
|
+
# XLWINGS_CUSTOM_HEADERS={}
|
|
100
|
+
|
|
101
|
+
# This will be prepended to all custom functions, e.g., "XLWINGS.MYFUNC". Note that
|
|
102
|
+
# if the environment is not "prod", it will append the environment to the namespace,
|
|
103
|
+
# e.g., XLWINGS_DEV to prevent name clashes when you have the same add-in from multiple
|
|
104
|
+
# environments installed.
|
|
105
|
+
# XLWINGS_FUNCTIONS_NAMESPACE="XLWINGS"
|
|
106
|
+
|
|
107
|
+
# In case xlwings Server doesn't manage to get the URL correct under /manifest, you
|
|
108
|
+
# can set the proper hostname here, e.g., mydomain.com (without the https://)
|
|
109
|
+
# XLWINGS_HOSTNAME=
|
|
110
|
+
|
|
111
|
+
# Set the log level to "DEBUG" for more details, but this can log sensitive tokens!
|
|
112
|
+
# XLWINGS_LOG_LEVEL="INFO"
|
|
113
|
+
|
|
114
|
+
# A Redis URL for the caching of object handles. Required for production use.
|
|
115
|
+
# Example: redis://host:6379/0 or rediss://host:6379/0
|
|
116
|
+
# XLWINGS_OBJECT_CACHE_URL=
|
|
117
|
+
|
|
118
|
+
# This setting expects a cron expression that determines when objects that are cached
|
|
119
|
+
# via object handles should be purged from the cache. This requires
|
|
120
|
+
# XLWINGS_OBJECT_CACHE_URL to be configured. By default, the object cache is purged
|
|
121
|
+
# every Saturday at 12:00 PM (UTC).
|
|
122
|
+
# XLWINGS_OBJECT_CACHE_EXPIRE_AT="0 12 * * sat"
|
|
123
|
+
|
|
124
|
+
# If true, cached objects (via object handles) will be compressed when stored in Redis.
|
|
125
|
+
# This requires XLWINGS_OBJECT_CACHE_URL to be configured.
|
|
126
|
+
# XLWINGS_OBJECT_CACHE_ENABLE_COMPRESSION=true
|
|
127
|
+
|
|
128
|
+
# This will determine the name of the add-in. If the environment is not "prod", the name
|
|
129
|
+
# of the environment will be shown like this: Project Name [dev].
|
|
130
|
+
# XLWINGS_PROJECT_NAME=""
|
|
131
|
+
|
|
132
|
+
# Number of seconds after which requests to the backend will timeout
|
|
133
|
+
# XLWINGS_REQUEST_TIMEOUT=300
|
|
134
|
+
|
|
135
|
+
# If you run the Socket.IO server in an own process, you need to configure a message
|
|
136
|
+
# queue, such as Redis/Valkey. E.g.: redis://host:6379/0
|
|
137
|
+
# XLWINGS_SOCKETIO_MESSAGE_QUEUE_URL=
|
|
138
|
+
|
|
139
|
+
# If you run the Socket.IO server in an own process, you need to set this to true for
|
|
140
|
+
# ONLY (!) the app that represents the Socket.IO server.
|
|
141
|
+
# XLWINGS_SOCKETIO_SERVER_APP=false
|
|
142
|
+
|
|
143
|
+
# The absolute path to the static files. If you set XLWINGS_APP_PATH to "/myapp", you
|
|
144
|
+
# likely need to change this to "/myapp/static".
|
|
145
|
+
# XLWINGS_STATIC_URL_PATH="/static"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from ._version import __version__
|
|
5
|
+
except ImportError:
|
|
6
|
+
__version__ = "0.0.0"
|
|
7
|
+
|
|
8
|
+
# Store package directory for use throughout the application
|
|
9
|
+
# This is evaluated when the package is first imported, before any sys.path manipulation
|
|
10
|
+
PACKAGE_DIR = Path(__file__).parent.resolve()
|
|
11
|
+
|
|
12
|
+
from .config import settings # noqa: E402
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '1.1.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 1, 0)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import secrets
|
|
3
|
+
|
|
4
|
+
from aiocache import cached
|
|
5
|
+
from fastapi import status
|
|
6
|
+
from fastapi.exceptions import HTTPException
|
|
7
|
+
|
|
8
|
+
from xlwings_server.models import User
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@cached(ttl=60 * 60)
|
|
14
|
+
async def validate_token(token_string: str):
|
|
15
|
+
"""token_string has to be delivered via the Authorization header from Excel. The
|
|
16
|
+
Authorization header is set by implementing the globalThis.getAuth() function
|
|
17
|
+
under static/js/auth.js.
|
|
18
|
+
See https://server.xlwings.org/en/latest/auth_providers/#custom-authentication
|
|
19
|
+
"""
|
|
20
|
+
if secrets.compare_digest(token_string, "test-token"): # TODO: implement
|
|
21
|
+
return User(id="customid", name="custom user")
|
|
22
|
+
else:
|
|
23
|
+
raise HTTPException(
|
|
24
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
25
|
+
detail="Custom Auth Error: Couldn't validate token",
|
|
26
|
+
)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
from aiocache import cached
|
|
10
|
+
from fastapi import Depends, Header, status
|
|
11
|
+
from fastapi.exceptions import HTTPException
|
|
12
|
+
from joserfc import jwt
|
|
13
|
+
from joserfc.jwk import KeySet
|
|
14
|
+
from joserfc.jwt import JWTClaimsRegistry
|
|
15
|
+
|
|
16
|
+
from ... import models
|
|
17
|
+
from ...config import settings
|
|
18
|
+
|
|
19
|
+
# Try to import jwks from project directory first (user override)
|
|
20
|
+
# Fall back to package location (default implementation)
|
|
21
|
+
try:
|
|
22
|
+
from auth.entraid import jwks
|
|
23
|
+
except ModuleNotFoundError:
|
|
24
|
+
from . import jwks
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
OPENID_CONNECT_DISCOVERY_DOCUMENT_URL = (
|
|
29
|
+
"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@cached(ttl=60 * 60 * 24)
|
|
34
|
+
async def get_jwks_json_default():
|
|
35
|
+
logger.info("Get default JWKS json for Entra ID")
|
|
36
|
+
async with httpx.AsyncClient() as client:
|
|
37
|
+
response = await client.get(OPENID_CONNECT_DISCOVERY_DOCUMENT_URL)
|
|
38
|
+
jwks_uri = response.json()["jwks_uri"]
|
|
39
|
+
async with httpx.AsyncClient() as client:
|
|
40
|
+
response = await client.get(jwks_uri)
|
|
41
|
+
return response.json()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def get_key_set():
|
|
45
|
+
jwks_data = await jwks.get_jwks_json()
|
|
46
|
+
if jwks_data is None:
|
|
47
|
+
jwks_data = await get_jwks_json_default()
|
|
48
|
+
key_set = KeySet.import_key_set(jwks_data)
|
|
49
|
+
return key_set
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@cached(ttl=60 * 60)
|
|
53
|
+
async def validate_token(token_string: str):
|
|
54
|
+
"""Validates the Entra ID access/id token. Returns a user object."""
|
|
55
|
+
logger.debug(f"Validating token: {token_string}")
|
|
56
|
+
if token_string.lower().startswith("error"):
|
|
57
|
+
raise HTTPException(
|
|
58
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
59
|
+
detail=f"Auth error with Entra ID token: {token_string} See https://learn.microsoft.com/en-us/office/dev/add-ins/develop/troubleshoot-sso-in-office-add-ins#causes-and-handling-of-errors-from-getaccesstoken",
|
|
60
|
+
)
|
|
61
|
+
if token_string.lower().startswith("bearer"):
|
|
62
|
+
parts = token_string.split()
|
|
63
|
+
if len(parts) != 2:
|
|
64
|
+
raise HTTPException(
|
|
65
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
66
|
+
detail="Auth error: Invalid token, must be in format 'Bearer xxxx'",
|
|
67
|
+
)
|
|
68
|
+
token_string = parts[1]
|
|
69
|
+
else:
|
|
70
|
+
raise HTTPException(
|
|
71
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
72
|
+
detail="Auth error: Invalid token, must be in format 'Bearer xxxx'",
|
|
73
|
+
)
|
|
74
|
+
key_set = await get_key_set()
|
|
75
|
+
try:
|
|
76
|
+
token = jwt.decode(token_string, key_set)
|
|
77
|
+
except Exception:
|
|
78
|
+
logger.exception("Auth error: Failed to decode token")
|
|
79
|
+
raise HTTPException(
|
|
80
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
81
|
+
detail="Auth error: Failed to decode token",
|
|
82
|
+
)
|
|
83
|
+
token_version = token.claims.get("ver")
|
|
84
|
+
# https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#token-formats
|
|
85
|
+
# Upgrade to 2.0:
|
|
86
|
+
# https://learn.microsoft.com/en-us/answers/questions/639834/how-to-get-access-token-version-20.html
|
|
87
|
+
if token_version == "1.0":
|
|
88
|
+
issuer = f"https://sts.windows.net/{settings.auth_entraid_tenant_id}/"
|
|
89
|
+
audience = f"api://{settings.auth_entraid_client_id}"
|
|
90
|
+
elif token_version == "2.0":
|
|
91
|
+
issuer = (
|
|
92
|
+
f"https://login.microsoftonline.com/{settings.auth_entraid_tenant_id}/v2.0"
|
|
93
|
+
)
|
|
94
|
+
audience = settings.auth_entraid_client_id
|
|
95
|
+
else:
|
|
96
|
+
raise HTTPException(
|
|
97
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
98
|
+
detail=f"Auth error: Unsupported token version: {token_version}",
|
|
99
|
+
)
|
|
100
|
+
try:
|
|
101
|
+
if not settings.auth_entraid_multitenant:
|
|
102
|
+
claims_requests = JWTClaimsRegistry(
|
|
103
|
+
aud={"essential": True, "value": audience},
|
|
104
|
+
iss={"essential": True, "value": issuer},
|
|
105
|
+
)
|
|
106
|
+
claims_requests.validate(token.claims)
|
|
107
|
+
else:
|
|
108
|
+
claims_requests = JWTClaimsRegistry(
|
|
109
|
+
aud={"essential": True, "value": audience},
|
|
110
|
+
)
|
|
111
|
+
claims_requests.validate(token.claims)
|
|
112
|
+
# External users have their own tenant_id
|
|
113
|
+
issuer_regex = r"https://login\.microsoftonline\.com/(common|organizations|consumers|[0-9a-fA-F-]{36})/v2\.0"
|
|
114
|
+
if not re.match(issuer_regex, issuer):
|
|
115
|
+
logger.debug(f"Couldn't match issuer for token: {token_string}")
|
|
116
|
+
raise HTTPException(
|
|
117
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
118
|
+
detail="Auth error: Couldn't validate token",
|
|
119
|
+
)
|
|
120
|
+
logger.debug(claims_requests)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.debug(f"Authentication error for token: {token_string}")
|
|
123
|
+
logger.info(repr(e))
|
|
124
|
+
raise HTTPException(
|
|
125
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
126
|
+
detail="Auth error: Couldn't validate token",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
current_user = models.User(claims=token.claims)
|
|
130
|
+
logger.info(f"User authenticated: {current_user.name}")
|
|
131
|
+
return current_user
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from aiocache import cached
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@cached(ttl=60 * 60 * 24)
|
|
5
|
+
async def get_jwks_json():
|
|
6
|
+
"""If your application runs on an air-gapped server and can't download the
|
|
7
|
+
Azure JSON Web Key Set (JWKS), you can provide your own function here to access
|
|
8
|
+
the JSON file.
|
|
9
|
+
"""
|
|
10
|
+
pass
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Virtual environments
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
|
|
5
|
+
# Secrets & certificates
|
|
6
|
+
.env
|
|
7
|
+
certs/
|
|
8
|
+
*.pem
|
|
9
|
+
*.key
|
|
10
|
+
|
|
11
|
+
# Git
|
|
12
|
+
.git/
|
|
13
|
+
.gitignore
|
|
14
|
+
|
|
15
|
+
# Python artifacts
|
|
16
|
+
__pycache__/
|
|
17
|
+
*.pyc
|
|
18
|
+
|
|
19
|
+
# Local dev settings
|
|
20
|
+
local.settings.json
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
|
|
26
|
+
# Misc
|
|
27
|
+
*.md
|
|
28
|
+
.DS_Store
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Azure Functions
|
|
3
|
+
|
|
4
|
+
Note: Azure Functions don't support streaming functions/socket.io.
|
|
5
|
+
|
|
6
|
+
The other files required for Azure Functions are:
|
|
7
|
+
- host.json
|
|
8
|
+
- local.settings.json
|
|
9
|
+
- .funcignore
|
|
10
|
+
|
|
11
|
+
The function is always called http_app_func, see:
|
|
12
|
+
https://github.com/Azure-Samples/fastapi-on-azure-functions/issues/31
|
|
13
|
+
|
|
14
|
+
For app logs, in Azure portal go to:
|
|
15
|
+
Function App > My Function App. Than, under `http_app_func`, click on `Invocations and more`.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import azure.functions as func
|
|
22
|
+
|
|
23
|
+
# Must come before importing xlwings_server
|
|
24
|
+
os.environ["XLWINGS_PROJECT_DIR"] = str(Path(__file__).parent)
|
|
25
|
+
|
|
26
|
+
from xlwings_server.main import main_app # noqa: E402
|
|
27
|
+
|
|
28
|
+
app = func.AsgiFunctionApp(app=main_app, http_auth_level=func.AuthLevel.ANONYMOUS)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0",
|
|
3
|
+
"logging": {
|
|
4
|
+
"applicationInsights": {
|
|
5
|
+
"samplingSettings": {
|
|
6
|
+
"isEnabled": true,
|
|
7
|
+
"excludedTypes": "Request"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"extensionBundle": {
|
|
12
|
+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
|
13
|
+
"version": "[4.*, 5.0.0)"
|
|
14
|
+
},
|
|
15
|
+
"extensions":
|
|
16
|
+
{
|
|
17
|
+
"http":
|
|
18
|
+
{
|
|
19
|
+
"routePrefix": ""
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Static file hasher for cache-busting in production builds.
|
|
2
|
+
|
|
3
|
+
This module provides the StaticFileHasher class which adds content-based hashes
|
|
4
|
+
to static file names (e.g., main.js -> main.a1b2c3d4.js) and updates all references
|
|
5
|
+
in HTML, JS, and CSS files accordingly. This is primarily used during the Wasm
|
|
6
|
+
build process to enable effective cache-busting in production deployments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StaticFileHasher:
|
|
16
|
+
"""Hashes static files and updates references for cache-busting.
|
|
17
|
+
|
|
18
|
+
This utility is used during the Wasm build process to add content-based
|
|
19
|
+
hashes to static file names (e.g., main.js -> main.a1b2c3d4.js) and update
|
|
20
|
+
all references in HTML, JS, and CSS files accordingly.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
static_dir: Directory containing static files to hash
|
|
24
|
+
templates_dir: Directory containing templates with references to update
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, static_dir: Path, templates_dir: Path):
|
|
28
|
+
self.static_dir = static_dir
|
|
29
|
+
self.templates_dir = templates_dir
|
|
30
|
+
self.file_mapping: dict[
|
|
31
|
+
str, dict[str, str]
|
|
32
|
+
] = {} # rel_path -> {old_name: new_name, path: Path}
|
|
33
|
+
self.processed_files: set[Path] = set()
|
|
34
|
+
|
|
35
|
+
def get_relative_path(self, path: Path, base_dir: Path) -> Path:
|
|
36
|
+
"""Get the relative path from base_dir to the given path"""
|
|
37
|
+
try:
|
|
38
|
+
return path.relative_to(base_dir)
|
|
39
|
+
except ValueError:
|
|
40
|
+
return path
|
|
41
|
+
|
|
42
|
+
def should_process_file(self, path: Path) -> bool:
|
|
43
|
+
"""Determine if a file should be processed based on various criteria"""
|
|
44
|
+
if not path.is_file() or path.name.startswith("."):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
excluded_patterns = {
|
|
48
|
+
".map",
|
|
49
|
+
".md",
|
|
50
|
+
".py",
|
|
51
|
+
".scss",
|
|
52
|
+
".txt",
|
|
53
|
+
".whl",
|
|
54
|
+
".xml",
|
|
55
|
+
"custom-functions-code.js",
|
|
56
|
+
"custom-functions-meta.json",
|
|
57
|
+
"eula.html",
|
|
58
|
+
"fonts/",
|
|
59
|
+
"images/ribbon/",
|
|
60
|
+
"index.html",
|
|
61
|
+
"manifest.xml",
|
|
62
|
+
"privacy.html",
|
|
63
|
+
"support.html",
|
|
64
|
+
"taskpane.html",
|
|
65
|
+
"vendor/",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
path_str = str(path)
|
|
69
|
+
return (
|
|
70
|
+
"." in path.name # requires extension
|
|
71
|
+
and not any(pattern in path_str for pattern in excluded_patterns)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def hash_file(self, path: Path) -> str:
|
|
75
|
+
"""Generate a hash for the given file"""
|
|
76
|
+
contents = path.read_bytes()
|
|
77
|
+
return hashlib.sha256(contents).hexdigest()[:8]
|
|
78
|
+
|
|
79
|
+
def generate_new_name(self, filename: str, digest: str) -> str:
|
|
80
|
+
"""Generate the new filename with hash included"""
|
|
81
|
+
name, ext = os.path.splitext(filename)
|
|
82
|
+
return f"{name}.{digest}{ext}"
|
|
83
|
+
|
|
84
|
+
def get_replacement_patterns(
|
|
85
|
+
self, file_path: Path, rel_path: str, old_name: str, new_name: str
|
|
86
|
+
) -> list[tuple[str, str]]:
|
|
87
|
+
"""Generate all possible path patterns for replacement"""
|
|
88
|
+
patterns = []
|
|
89
|
+
dir_path = os.path.dirname(rel_path)
|
|
90
|
+
|
|
91
|
+
# For absolute paths (mainly HTML references)
|
|
92
|
+
abs_patterns = [
|
|
93
|
+
(f"{rel_path}", f"{dir_path}/{new_name}"),
|
|
94
|
+
(f"/{rel_path}", f"/{dir_path}/{new_name}"),
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
# For relative paths (mainly JS imports)
|
|
98
|
+
try:
|
|
99
|
+
current_dir = file_path.parent.relative_to(self.static_dir)
|
|
100
|
+
target_dir = Path(dir_path)
|
|
101
|
+
rel_import_path = os.path.relpath(target_dir, current_dir)
|
|
102
|
+
|
|
103
|
+
# Handle the case where files are in the same directory
|
|
104
|
+
if rel_import_path == ".":
|
|
105
|
+
rel_patterns = [
|
|
106
|
+
(f"./{old_name}", f"./{new_name}"),
|
|
107
|
+
(old_name, new_name),
|
|
108
|
+
]
|
|
109
|
+
else:
|
|
110
|
+
rel_import_path = rel_import_path.replace("\\", "/")
|
|
111
|
+
if not rel_import_path.startswith("."):
|
|
112
|
+
rel_import_path = f"./{rel_import_path}"
|
|
113
|
+
rel_patterns = [
|
|
114
|
+
(f"{rel_import_path}/{old_name}", f"{rel_import_path}/{new_name}"),
|
|
115
|
+
]
|
|
116
|
+
except ValueError:
|
|
117
|
+
rel_patterns = []
|
|
118
|
+
|
|
119
|
+
# Combine all patterns
|
|
120
|
+
all_paths = abs_patterns + rel_patterns
|
|
121
|
+
|
|
122
|
+
# Generate variations for each pattern
|
|
123
|
+
for old_path, new_path in all_paths:
|
|
124
|
+
# Normalize slashes
|
|
125
|
+
old_path = old_path.replace("\\", "/")
|
|
126
|
+
new_path = new_path.replace("\\", "/")
|
|
127
|
+
|
|
128
|
+
# Remove any double slashes
|
|
129
|
+
old_path = re.sub(r"/{2,}", "/", old_path)
|
|
130
|
+
new_path = re.sub(r"/{2,}", "/", new_path)
|
|
131
|
+
|
|
132
|
+
variations = [
|
|
133
|
+
(old_path, new_path),
|
|
134
|
+
(f"'{old_path}'", f"'{new_path}'"),
|
|
135
|
+
(f'"{old_path}"', f'"{new_path}"'),
|
|
136
|
+
(f'from "{old_path}"', f'from "{new_path}"'),
|
|
137
|
+
(f"from '{old_path}'", f"from '{new_path}'"),
|
|
138
|
+
(f'import "{old_path}"', f'import "{new_path}"'),
|
|
139
|
+
(f"import '{old_path}'", f"import '{new_path}'"),
|
|
140
|
+
(f"url({old_path})", f"url({new_path})"),
|
|
141
|
+
(f'url("{old_path}")', f'url("{new_path}")'),
|
|
142
|
+
(f"url('{old_path}')", f"url('{new_path}')"),
|
|
143
|
+
]
|
|
144
|
+
patterns.extend(variations)
|
|
145
|
+
|
|
146
|
+
return patterns
|
|
147
|
+
|
|
148
|
+
def replace_in_file(
|
|
149
|
+
self, file_path: Path, excluded_dirs: list[str] | None = None
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Replace all occurrences of original filenames with hashed versions"""
|
|
152
|
+
if excluded_dirs is None:
|
|
153
|
+
excluded_dirs = []
|
|
154
|
+
|
|
155
|
+
if any(excluded_dir in file_path.parts for excluded_dir in excluded_dirs):
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
content = file_path.read_text()
|
|
159
|
+
new_content = content
|
|
160
|
+
|
|
161
|
+
# Sort paths by length (longest first) to avoid partial replacements
|
|
162
|
+
sorted_paths = sorted(self.file_mapping.keys(), key=len, reverse=True)
|
|
163
|
+
|
|
164
|
+
for rel_path in sorted_paths:
|
|
165
|
+
file_info = self.file_mapping[rel_path]
|
|
166
|
+
old_name = file_info["old_name"]
|
|
167
|
+
new_name = file_info["new_name"]
|
|
168
|
+
|
|
169
|
+
patterns = self.get_replacement_patterns(
|
|
170
|
+
file_path, rel_path, old_name, new_name
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
for old_pattern, new_pattern in patterns:
|
|
174
|
+
new_content = new_content.replace(old_pattern, new_pattern)
|
|
175
|
+
|
|
176
|
+
if new_content != content:
|
|
177
|
+
file_path.write_text(new_content)
|
|
178
|
+
|
|
179
|
+
def process_files(self):
|
|
180
|
+
"""Process all static files and update references"""
|
|
181
|
+
# First pass: hash all files and create mapping
|
|
182
|
+
for source_path in self.static_dir.rglob("*"):
|
|
183
|
+
if self.should_process_file(source_path):
|
|
184
|
+
rel_path = self.get_relative_path(source_path, self.static_dir)
|
|
185
|
+
rel_path_str = str(rel_path).replace("\\", "/")
|
|
186
|
+
|
|
187
|
+
digest = self.hash_file(source_path)
|
|
188
|
+
old_name = source_path.name
|
|
189
|
+
new_name = self.generate_new_name(old_name, digest)
|
|
190
|
+
|
|
191
|
+
self.file_mapping[rel_path_str] = {
|
|
192
|
+
"old_name": old_name,
|
|
193
|
+
"new_name": new_name,
|
|
194
|
+
"path": source_path,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# Second pass: rename files
|
|
198
|
+
for file_info in self.file_mapping.values():
|
|
199
|
+
old_path = file_info["path"]
|
|
200
|
+
if old_path.exists(): # Check if not already renamed
|
|
201
|
+
new_path = old_path.with_name(file_info["new_name"])
|
|
202
|
+
old_path.rename(new_path)
|
|
203
|
+
|
|
204
|
+
# Third pass: update references in files
|
|
205
|
+
for template_file in self.templates_dir.rglob("*.html"):
|
|
206
|
+
self.replace_in_file(template_file)
|
|
207
|
+
|
|
208
|
+
for js_file in self.static_dir.rglob("*.js"):
|
|
209
|
+
self.replace_in_file(js_file, excluded_dirs=["vendor"])
|
|
210
|
+
|
|
211
|
+
for css_file in self.static_dir.rglob("*.css"):
|
|
212
|
+
self.replace_in_file(css_file, excluded_dirs=["vendor"])
|