cmdbox 0.6.0.3__py3-none-any.whl → 0.6.1__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.
Potentially problematic release.
This version of cmdbox might be problematic. Click here for more details.
- cmdbox/app/app.py +2 -2
- cmdbox/app/auth/signin.py +144 -28
- cmdbox/app/common.py +44 -5
- cmdbox/app/edge.py +1 -1
- cmdbox/app/edge_tool.py +1 -1
- cmdbox/app/features/cli/agent_base.py +2 -394
- cmdbox/app/features/cli/cmdbox_audit_search.py +0 -2
- cmdbox/app/features/cli/cmdbox_audit_write.py +9 -0
- cmdbox/app/features/cli/cmdbox_cmd_list.py +2 -2
- cmdbox/app/features/cli/cmdbox_cmd_load.py +2 -2
- cmdbox/app/features/cli/cmdbox_mcp_proxy.py +90 -0
- cmdbox/app/features/cli/cmdbox_web_apikey_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_apikey_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_gencert.py +25 -2
- cmdbox/app/features/cli/cmdbox_web_group_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_list.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_start.py +68 -26
- cmdbox/app/features/cli/cmdbox_web_user_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_list.py +1 -1
- cmdbox/app/features/web/cmdbox_web_agent.py +10 -4
- cmdbox/app/features/web/cmdbox_web_signin.py +1 -1
- cmdbox/app/mcp.py +375 -0
- cmdbox/app/options.py +10 -0
- cmdbox/app/web.py +60 -53
- cmdbox/autoload.py +10 -0
- cmdbox/extensions/user_list.yml +25 -1
- cmdbox/licenses/{LICENSE.Deprecated.1.2.18(MIT License).txt → LICENSE_PyJWT_2_10_1_MIT_License.txt} +2 -2
- cmdbox/licenses/LICENSE_exceptiongroup_1_3_0_MIT_License.txt +73 -0
- cmdbox/licenses/LICENSE_fastmcp_2_9_2_Apache_Software_License.txt +201 -0
- cmdbox/licenses/{LICENSE.graphviz.0.20.3(MIT License).txt → LICENSE_graphviz_0_21_UNKNOWN.txt} +1 -1
- cmdbox/licenses/LICENSE_jaraco_functools_4_2_1_UNKNOWN.txt +18 -0
- cmdbox/licenses/{LICENSE.numpy.2.2.5(BSD License).txt → LICENSE_numpy_2_3_1_BSD_License.txt} +8 -8
- cmdbox/licenses/LICENSE_openapi-pydantic_0_5_1_MIT_License.txt +40 -0
- cmdbox/licenses/LICENSE_propcache_0_3_2_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_proto-plus_1_26_1_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_shellingham_1_5_4_ISC_License-ISCL.txt +13 -0
- cmdbox/licenses/LICENSE_sphinx-last-updated-by-git_0_3_8_BSD_License.txt +22 -0
- cmdbox/licenses/LICENSE_tenacity_8_5_0_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_tokenizers_0_21_2_Apache_Software_License.txt +1 -0
- cmdbox/licenses/LICENSE_typer_0_16_0_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_typing-inspection_0_4_1_UNKNOWN.txt +21 -0
- cmdbox/licenses/LICENSE_yarl_1_20_1_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_zipp_3_23_0_UNKNOWN.txt +18 -0
- cmdbox/licenses/files.txt +176 -166
- cmdbox/logconf_mcp.yml +43 -0
- cmdbox/version.py +2 -2
- cmdbox/web/agent.html +1 -33
- cmdbox/web/assets/cmdbox/agent.js +2 -3
- cmdbox/web/assets/cmdbox/audit.js +1 -1
- cmdbox/web/assets/cmdbox/common.js +73 -8
- cmdbox/web/assets/cmdbox/main.js +1 -2
- cmdbox/web/assets/cmdbox/svgicon.js +122 -0
- cmdbox/web/assets/cmdbox/users.js +4 -3
- cmdbox/web/audit.html +1 -35
- cmdbox/web/filer.html +1 -13
- cmdbox/web/gui.html +2 -50
- cmdbox/web/result.html +2 -46
- cmdbox/web/signin.html +1 -26
- cmdbox/web/users.html +1 -41
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/METADATA +32 -7
- cmdbox-0.6.1.dist-info/RECORD +383 -0
- cmdbox/licenses/LICENSE.keyring.25.6.0(MIT License).txt +0 -17
- cmdbox/licenses/LICENSE.typing-inspection.0.4.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.wrapt.1.17.2(BSD License).txt +0 -24
- cmdbox/licenses/LICENSE.zipp.3.21.0(MIT License).txt +0 -17
- cmdbox-0.6.0.3.dist-info/RECORD +0 -368
- /cmdbox/licenses/{LICENSE.Authlib.1.5.2(BSD License).txt → LICENSE_Authlib_1_6_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Jinja2.3.1.6(BSD License).txt → LICENSE_Jinja2_3_1_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.MarkupSafe.3.0.2(BSD License).txt → LICENSE_MarkupSafe_3_0_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.PyYAML.6.0.2(MIT License).txt → LICENSE_PyYAML_6_0_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Pygments.2.19.1(BSD License).txt → LICENSE_Pygments_2_19_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.SQLAlchemy.2.0.40(MIT License).txt → LICENSE_SQLAlchemy_2_0_41_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Sphinx.8.2.3(UNKNOWN).txt → LICENSE_Sphinx_8_2_3_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiohappyeyeballs.2.6.1(Python Software Foundation License).txt → LICENSE_aiohappyeyeballs_2_6_1_Python_Software_Foundation_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiohttp.3.11.18(Apache Software License).txt → LICENSE_aiohttp_3_12_13_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiosignal.1.3.2(Apache Software License).txt → LICENSE_aiosignal_1_3_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.alabaster.1.0.0(BSD License).txt → LICENSE_alabaster_1_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.annotated-types.0.7.0(MIT License).txt → LICENSE_annotated-types_0_7_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.anyio.4.9.0(MIT License).txt → LICENSE_anyio_4_9_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.argcomplete.3.6.2(Apache Software License).txt → LICENSE_argcomplete_3_6_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.async-timeout.5.0.1(Apache Software License).txt → LICENSE_async-timeout_5_0_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.attrs.25.3.0(UNKNOWN).txt → LICENSE_attrs_25_3_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.babel.2.17.0(BSD License).txt → LICENSE_babel_2_17_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.backports.tarfile.1.2.0(MIT License).txt → LICENSE_backports_tarfile_1_2_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cachetools.5.5.2(MIT License).txt → LICENSE_cachetools_5_5_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.certifi.2025.4.26(Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE_certifi_2025_6_15_Mozilla_Public_License_2_0-MPL_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cffi.1.17.1(MIT License).txt → LICENSE_cffi_1_17_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.charset-normalizer.3.4.2(MIT License).txt → LICENSE_charset-normalizer_3_4_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.click.8.2.0(UNKNOWN).txt → LICENSE_click_8_2_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jiter.0.9.0(MIT License).txt → LICENSE_cloudpickle_3_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.colorama.0.4.6(BSD License).txt → LICENSE_colorama_0_4_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cryptography.44.0.3(Apache Software License; BSD License).txt → LICENSE_cryptography_45_0_4_Apache-2_0_OR_BSD-3-Clause.txt} +0 -0
- /cmdbox/licenses/{LICENSE.distro.1.9.0(Apache Software License).txt → LICENSE_distro_1_9_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.docstring_parser.0.16(MIT License).txt → LICENSE_docstring_parser_0_16_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.docutils.0.21.2(BSD License; GNU General Public License (GPL); Public Domain; Python Software Foundation License).txt → LICENSE_docutils_0_21_2_BSD_License-GNU_General_Public_License-GPL-Public_Domain-Python_Software_Foundation_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.fastapi.0.115.12(MIT License).txt → LICENSE_fastapi_0_115_14_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.filelock.3.18.0(The Unlicense (Unlicense)).txt → LICENSE_filelock_3_18_0_The_Unlicense-Unlicense.txt} +0 -0
- /cmdbox/licenses/{LICENSE.frozenlist.1.6.0(Apache-2.0).txt → LICENSE_frozenlist_1_7_0_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.fsspec.2025.3.2(BSD License).txt → LICENSE_fsspec_2025_5_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.gevent.25.4.2(MIT).txt → LICENSE_gevent_25_5_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-adk.0.5.0(Apache Software License).txt → LICENSE_google-adk_1_5_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-api-core.2.24.2(Apache Software License).txt → LICENSE_google-api-core_2_25_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-api-python-client.2.169.0(Apache Software License).txt → LICENSE_google-api-python-client_2_174_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-auth-httplib2.0.2.0(Apache Software License).txt → LICENSE_google-auth-httplib2_0_2_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-auth.2.40.1(Apache Software License).txt → LICENSE_google-auth_2_40_3_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-aiplatform.1.92.0(Apache 2.0).txt → LICENSE_google-cloud-aiplatform_1_100_0_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-bigquery.3.31.0(Apache Software License).txt → LICENSE_google-cloud-appengine-logging_1_6_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-core.2.4.3(Apache Software License).txt → LICENSE_google-cloud-audit-log_0_3_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-resource-manager.1.14.2(Apache Software License).txt → LICENSE_google-cloud-bigquery_3_34_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-secret-manager.2.23.3(Apache Software License).txt → LICENSE_google-cloud-core_2_4_3_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-speech.2.32.0(Apache Software License).txt → LICENSE_google-cloud-logging_3_12_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-storage.2.19.0(Apache Software License).txt → LICENSE_google-cloud-resource-manager_1_14_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-trace.1.16.1(Apache Software License).txt → LICENSE_google-cloud-secret-manager_2_24_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-crc32c.1.7.1(Apache 2.0).txt → LICENSE_google-cloud-speech_2_33_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-genai.1.14.0(Apache Software License).txt → LICENSE_google-cloud-storage_2_19_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-resumable-media.2.7.2(Apache Software License).txt → LICENSE_google-cloud-trace_1_16_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.googleapis-common-protos.1.70.0(Apache Software License).txt → LICENSE_google-crc32c_1_7_1_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpc-google-iam-v1.0.14.2(Apache Software License).txt → LICENSE_google-genai_1_23_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.id.1.5.0(Apache Software License).txt → LICENSE_google-resumable-media_2_7_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.importlib_metadata.8.6.1(Apache Software License).txt → LICENSE_googleapis-common-protos_1_70_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.greenlet.3.2.2(MIT AND Python-2.0).txt → LICENSE_greenlet_3_2_3_MIT_AND_Python-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.propcache.0.3.1(Apache Software License).txt → LICENSE_grpc-google-iam-v1_0_14_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpcio-status.1.71.0(Apache Software License).txt → LICENSE_grpcio-status_1_73_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpcio.1.71.0(Apache Software License).txt → LICENSE_grpcio_1_73_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.gunicorn.23.0.0(MIT License).txt → LICENSE_gunicorn_23_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.h11.0.16.0(MIT License).txt → LICENSE_h11_0_16_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpcore.1.0.9(BSD License).txt → LICENSE_httpcore_1_0_9_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httplib2.0.22.0(MIT License).txt → LICENSE_httplib2_0_22_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httptools.0.6.4(MIT License).txt → LICENSE_httptools_0_6_4_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpx-sse.0.4.0(MIT).txt → LICENSE_httpx-sse_0_4_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpx.0.28.1(BSD License).txt → LICENSE_httpx_0_28_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.huggingface-hub.0.31.1(Apache Software License).txt → LICENSE_huggingface-hub_0_33_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.proto-plus.1.26.1(Apache Software License).txt → LICENSE_id_1_5_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.idna.3.10(BSD License).txt → LICENSE_idna_3_10_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.imagesize.1.4.1(MIT License).txt → LICENSE_imagesize_1_4_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.yarl.1.20.0(Apache Software License).txt → LICENSE_importlib_metadata_8_7_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.itsdangerous.2.2.0(BSD License).txt → LICENSE_itsdangerous_2_2_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.classes.3.4.0(MIT License).txt → LICENSE_jaraco_classes_3_4_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.context.6.0.1(MIT License).txt → LICENSE_jaraco_context_6_0_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-applehelp.2.0.0(BSD License).txt → LICENSE_jiter_0_10_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jsonschema-specifications.2025.4.1(UNKNOWN).txt → LICENSE_jsonschema-specifications_2025_4_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jsonschema.4.23.0(MIT License).txt → LICENSE_jsonschema_4_24_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.functools.4.1.0(MIT License).txt → LICENSE_keyring_25_6_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.litellm.1.69.0(MIT License).txt → LICENSE_litellm_1_73_6_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.markdown-it-py.3.0.0(MIT License).txt → LICENSE_markdown-it-py_3_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.mcp.1.8.0(MIT License).txt → LICENSE_mcp_1_9_4_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.mdurl.0.1.2(MIT License).txt → LICENSE_mdurl_0_1_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.more-itertools.10.7.0(MIT License).txt → LICENSE_more-itertools_10_7_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.multidict.6.4.3(Apache Software License).txt → LICENSE_multidict_6_6_2_Apache_License_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.nh3.0.2.21(MIT).txt → LICENSE_nh3_0_2_21_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.openai.1.75.0(Apache Software License).txt → LICENSE_openai_1_93_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-api.1.33.0(Apache Software License).txt → LICENSE_opentelemetry-api_1_34_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-exporter-gcp-trace.1.9.0(Apache Software License).txt → LICENSE_opentelemetry-exporter-gcp-trace_1_9_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-resourcedetector-gcp.1.9.0a0(Apache Software License).txt → LICENSE_opentelemetry-resourcedetector-gcp_1_9_0a0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-sdk.1.33.0(Apache Software License).txt → LICENSE_opentelemetry-sdk_1_34_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-semantic-conventions.0.54b0(Apache Software License).txt → LICENSE_opentelemetry-semantic-conventions_0_55b1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.packaging.25.0(Apache Software License; BSD License).txt → LICENSE_packaging_25_0_Apache_Software_License-BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pillow.11.2.1(UNKNOWN).txt → LICENSE_pillow_11_2_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pip.24.0(MIT License).txt → LICENSE_pip_24_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.plyer.2.1.0(MIT License).txt → LICENSE_plyer_2_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.prettytable.3.16.0(UNKNOWN).txt → LICENSE_prettytable_3_16_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.prompt_toolkit.3.0.51(BSD License).txt → LICENSE_prompt_toolkit_3_0_51_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.protobuf.5.29.4(3-Clause BSD License).txt → LICENSE_protobuf_6_31_1_3-Clause_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg-binary.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_psycopg-binary_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_psycopg_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyasn1.0.6.1(BSD License).txt → LICENSE_pyasn1_0_6_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyasn1_modules.0.4.2(BSD License).txt → LICENSE_pyasn1_modules_0_4_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pycparser.2.22(BSD License).txt → LICENSE_pycparser_2_22_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pycryptodome.3.22.0(BSD License; Public Domain).txt → LICENSE_pycryptodome_3_23_0_BSD_License-Public_Domain.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic-settings.2.9.1(MIT License).txt → LICENSE_pydantic-settings_2_10_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic.2.11.4(MIT License).txt → LICENSE_pydantic_2_11_7_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic_core.2.33.2(MIT License).txt → LICENSE_pydantic_core_2_33_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyparsing.3.2.3(MIT License).txt → LICENSE_pyparsing_3_2_3_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pystray.0.19.5(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_pystray_0_19_5_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-dateutil.2.9.0.post0(Apache Software License; BSD License).txt → LICENSE_python-dateutil_2_9_0_post0_Apache_Software_License-BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-dotenv.1.1.0(BSD License).txt → LICENSE_python-dotenv_1_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-multipart.0.0.20(Apache Software License).txt → LICENSE_python-multipart_0_0_20_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pywin32-ctypes.0.2.3(BSD-3-Clause).txt → LICENSE_pywin32-ctypes_0_2_3_BSD-3-Clause.txt} +0 -0
- /cmdbox/licenses/{LICENSE.questionary.2.1.0(MIT License).txt → LICENSE_questionary_2_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.readme_renderer.44.0(Apache Software License).txt → LICENSE_readme_renderer_44_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.redis.6.0.0(MIT License).txt → LICENSE_redis_6_2_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.referencing.0.36.2(UNKNOWN).txt → LICENSE_referencing_0_36_2_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.regex.2024.11.6(Apache Software License).txt → LICENSE_regex_2024_11_6_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.requests-toolbelt.1.0.0(Apache Software License).txt → LICENSE_requests-toolbelt_1_0_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.requests.2.32.3(Apache Software License).txt → LICENSE_requests_2_32_4_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rfc3986.2.0.0(Apache Software License).txt → LICENSE_rfc3986_2_0_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rich.14.0.0(MIT License).txt → LICENSE_rich_14_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.roman-numerals-py.3.1.0(CC0 1.0 Universal (CC0 1.0) Public Domain Dedication; Zero-Clause BSD (0BSD)).txt → LICENSE_roman-numerals-py_3_1_0_CC0_1_0_Universal-CC0_1_0-Public_Domain_Dedication-Zero-Clause_BSD-0BSD.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rpds-py.0.24.0(MIT).txt → LICENSE_rpds-py_0_25_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rsa.4.9.1(Apache Software License).txt → LICENSE_rsa_4_9_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.setuptools.65.5.0(MIT License).txt → LICENSE_setuptools_65_5_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.shapely.2.1.0(BSD License).txt → LICENSE_shapely_2_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.six.1.17.0(MIT License).txt → LICENSE_six_1_17_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sniffio.1.3.1(Apache Software License; MIT License).txt → LICENSE_sniffio_1_3_1_Apache_Software_License-MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.snowballstemmer.3.0.1(BSD License).txt → LICENSE_snowballstemmer_3_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-intl.2.3.1(BSD License).txt → LICENSE_sphinx-intl_2_3_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-rtd-theme.3.0.2(MIT License).txt → LICENSE_sphinx-rtd-theme_3_0_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-sitemap.2.6.0(MIT License).txt → LICENSE_sphinx-sitemap_2_7_2_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx_fontawesome.0.0.6(GNU General Public License v2 (GPLv2)).txt → LICENSE_sphinx_fontawesome_0_0_6_GNU_General_Public_License_v2-GPLv2.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-devhelp.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-applehelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-htmlhelp.2.1.0(BSD License).txt → LICENSE_sphinxcontrib-devhelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-jquery.4.1(BSD License).txt → LICENSE_sphinxcontrib-htmlhelp_2_1_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-qthelp.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-jquery_4_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-jsmath.1.0.1(BSD License).txt → LICENSE_sphinxcontrib-jsmath_1_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-serializinghtml.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-qthelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tokenizers.0.21.1(Apache Software License).txt → LICENSE_sphinxcontrib-serializinghtml_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sse-starlette.2.3.4(BSD License).txt → LICENSE_sse-starlette_2_3_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.starlette.0.46.2(BSD License).txt → LICENSE_starlette_0_46_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tabulate.0.9.0(MIT License).txt → LICENSE_tabulate_0_9_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tiktoken.0.9.0(MIT License).txt → LICENSE_tiktoken_0_9_0_MIT_License-Copyright-c-2022_OpenAI-Shantanu_Jain-Permission_is_hereby_granted-free_of_charge-to_any_pers.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tomli.2.2.1(MIT License).txt → LICENSE_tomli_2_2_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tqdm.4.67.1(MIT License; Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE_tqdm_4_67_1_MIT_License-Mozilla_Public_License_2_0-MPL_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.twine.6.1.0(Apache Software License).txt → LICENSE_twine_6_1_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.typing_extensions.4.13.2(UNKNOWN).txt → LICENSE_typing_extensions_4_14_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tzdata.2025.2(Apache Software License).txt → LICENSE_tzdata_2025_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tzlocal.5.3.1(MIT License).txt → LICENSE_tzlocal_5_3_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.uritemplate.4.1.1(Apache Software License; BSD License).txt → LICENSE_uritemplate_4_2_0_BSD_3-Clause_OR_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.urllib3.2.4.0(UNKNOWN).txt → LICENSE_urllib3_2_5_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.uvicorn.0.34.2(BSD License).txt → LICENSE_uvicorn_0_35_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.watchfiles.1.0.5(MIT License).txt → LICENSE_watchfiles_1_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.wcwidth.0.2.13(MIT License).txt → LICENSE_wcwidth_0_2_13_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.websockets.15.0.1(BSD License).txt → LICENSE_websockets_15_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.wheel.0.45.1(MIT License).txt → LICENSE_wheel_0_45_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.zope.event.5.0(Zope Public License).txt → LICENSE_zope_event_5_1_Zope_Public_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.zope.interface.7.2(Zope Public License).txt → LICENSE_zope_interface_7_2_Zope_Public_License.txt} +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/LICENSE +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/WHEEL +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/top_level.txt +0 -0
cmdbox/app/app.py
CHANGED
|
@@ -9,9 +9,9 @@ import time
|
|
|
9
9
|
import sys
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def main(args_list:list=None):
|
|
12
|
+
def main(args_list:list=None, webcall:bool=False):
|
|
13
13
|
app = CmdBoxApp.getInstance(appcls=CmdBoxApp, ver=version)
|
|
14
|
-
return app.main(args_list)[0]
|
|
14
|
+
return app.main(args_list, webcall=webcall)[0]
|
|
15
15
|
|
|
16
16
|
class CmdBoxApp:
|
|
17
17
|
_instance = None
|
cmdbox/app/auth/signin.py
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
from cmdbox.app import common, options
|
|
2
|
+
from cryptography import x509
|
|
3
|
+
from cryptography.hazmat.primitives import serialization
|
|
4
|
+
from cryptography.hazmat.backends import default_backend
|
|
2
5
|
from fastapi import Request, Response, HTTPException, WebSocket
|
|
3
6
|
from fastapi.responses import RedirectResponse
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
from typing import Dict, Any, Tuple, List, Union
|
|
9
|
+
import argparse
|
|
6
10
|
import copy
|
|
7
11
|
import contextvars
|
|
8
12
|
import logging
|
|
13
|
+
import jwt
|
|
9
14
|
import string
|
|
10
15
|
|
|
11
16
|
|
|
@@ -87,7 +92,7 @@ class Signin(object):
|
|
|
87
92
|
if self.signin_file_data is None:
|
|
88
93
|
return None
|
|
89
94
|
if 'signin' in req.session:
|
|
90
|
-
self.signin_file_data = Signin.load_signin_file(self.signin_file, self.signin_file_data) # サインインファイルの更新をチェック
|
|
95
|
+
self.signin_file_data = Signin.load_signin_file(self.signin_file, self.signin_file_data, self=self) # サインインファイルの更新をチェック
|
|
91
96
|
return Signin._check_signin(req, res, self.signin_file_data, self.logger)
|
|
92
97
|
|
|
93
98
|
@classmethod
|
|
@@ -157,17 +162,37 @@ class Signin(object):
|
|
|
157
162
|
if not auth.startswith('Bearer '):
|
|
158
163
|
#self.logger.warning(f"Bearer not found. headers={req.headers}")
|
|
159
164
|
return RedirectResponse(url=f'/signin{req.url.path}?error=apikeyfail')
|
|
160
|
-
bearer, apikey = auth.split(' ')
|
|
161
|
-
apikey = common.hash_password(apikey.strip(), 'sha1')
|
|
165
|
+
bearer, apikey = auth.split(' ').strip()
|
|
162
166
|
if logger.level == logging.DEBUG:
|
|
163
|
-
logger.debug(f"
|
|
167
|
+
logger.debug(f"received apikey: {apikey}")
|
|
164
168
|
find_user = None
|
|
169
|
+
jwt_enabled = signin_file_data['apikey']['verify_jwt']['enabled']
|
|
165
170
|
for user in signin_file_data['users']:
|
|
166
171
|
if 'apikeys' not in user:
|
|
167
172
|
continue
|
|
168
173
|
for ak, key in user['apikeys'].items():
|
|
169
174
|
if apikey == key:
|
|
170
|
-
|
|
175
|
+
if jwt_enabled:
|
|
176
|
+
publickey = None
|
|
177
|
+
if hasattr(cls, 'verify_jwt_certificate') and cls.verify_jwt_certificate is not None:
|
|
178
|
+
publickey = cls.verify_jwt_certificate.public_key()
|
|
179
|
+
if hasattr(cls, 'verify_jwt_publickey') and cls.verify_jwt_publickey is not None:
|
|
180
|
+
publickey = cls.verify_jwt_publickey
|
|
181
|
+
algorithm = signin_file_data['apikey']['verify_jwt']['algorithm']
|
|
182
|
+
issuer = signin_file_data['apikey']['verify_jwt']['issuer']
|
|
183
|
+
audience = signin_file_data['apikey']['verify_jwt']['audience']
|
|
184
|
+
claims = jwt.decode(apikey, publickey, algorithms=[algorithm],
|
|
185
|
+
issuer=issuer, audience=audience,
|
|
186
|
+
options={'verify_iss': issuer is not None,
|
|
187
|
+
'verify_aud': audience is not None},)
|
|
188
|
+
find_user = dict(**claims, **user)
|
|
189
|
+
find_user['uid'] = find_user['uid'] if 'uid' in find_user else -1
|
|
190
|
+
find_user['name'] = find_user['name'] if 'name' in find_user else None
|
|
191
|
+
find_user['groups'] = find_user['groups'] if 'groups' in find_user else None
|
|
192
|
+
find_user['email'] = find_user['email'] if 'email' in find_user else None
|
|
193
|
+
find_user['apikey_name'] = find_user['apikey_name'] if 'apikey_name' in find_user else None
|
|
194
|
+
else:
|
|
195
|
+
find_user = user
|
|
171
196
|
if find_user is None:
|
|
172
197
|
logger.warning(f"No matching user found for apikey.")
|
|
173
198
|
return RedirectResponse(url=f'/signin{req.url.path}?error=apikeyfail')
|
|
@@ -196,7 +221,22 @@ class Signin(object):
|
|
|
196
221
|
return RedirectResponse(url=f'/signin{req.url.path}?error=unauthorizedsite')
|
|
197
222
|
|
|
198
223
|
@classmethod
|
|
199
|
-
def
|
|
224
|
+
def load_pem_private_key(cls, data:bytes, passphrase:str=None):
|
|
225
|
+
return serialization.load_pem_private_key(
|
|
226
|
+
data,
|
|
227
|
+
password=passphrase.encode('utf-8') if passphrase else None,
|
|
228
|
+
backend=default_backend())
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def load_pem_public_key(cls, data:bytes):
|
|
232
|
+
return serialization.load_pem_public_key(data, backend=default_backend())
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def load_pem_x509_certificate(cls, data:bytes):
|
|
236
|
+
return x509.load_pem_x509_certificate(data, backend=default_backend())
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def load_signin_file(cls, signin_file:Path, signin_file_data:Dict[str, Any]=None, self=None) -> Dict[str, Any]:
|
|
200
240
|
"""
|
|
201
241
|
サインインファイルを読み込む
|
|
202
242
|
|
|
@@ -390,6 +430,90 @@ class Signin(object):
|
|
|
390
430
|
raise HTTPException(status_code=500, detail=f'signin_file format error. "reset" not found in "password.lockout". ({signin_file})')
|
|
391
431
|
if type(yml['password']['lockout']['reset']) is not int:
|
|
392
432
|
raise HTTPException(status_code=500, detail=f'signin_file format error. "reset" not int type in "password.lockout". ({signin_file})')
|
|
433
|
+
# apikeyのフォーマットチェック
|
|
434
|
+
if 'apikey' not in yml:
|
|
435
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "apikey" not found. ({signin_file})')
|
|
436
|
+
if 'gen_cert' not in yml['apikey']:
|
|
437
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "gen_cert" not found in "apikey". ({signin_file})')
|
|
438
|
+
if 'enabled' not in yml['apikey']['gen_cert']:
|
|
439
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "apikey.gen_cert". ({signin_file})')
|
|
440
|
+
if type(yml['apikey']['gen_cert']['enabled']) is not bool:
|
|
441
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "apikey.gen_cert". ({signin_file})')
|
|
442
|
+
if yml['apikey']['gen_cert']['enabled']:
|
|
443
|
+
if 'privatekey' not in yml['apikey']['gen_cert']:
|
|
444
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "privatekey" not found in "apikey.gen_cert". ({signin_file})')
|
|
445
|
+
if 'certificate' not in yml['apikey']['gen_cert']:
|
|
446
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "certificate" not found in "apikey.gen_cert". ({signin_file})')
|
|
447
|
+
if 'publickey' not in yml['apikey']['gen_cert']:
|
|
448
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "publickey" not found in "apikey.gen_cert". ({signin_file})')
|
|
449
|
+
if not Path(yml['apikey']['gen_cert']['certificate']).is_file():
|
|
450
|
+
from cmdbox.app.features.cli.cmdbox_web_gencert import WebGencert
|
|
451
|
+
gencert = WebGencert(self.appcls, self.ver)
|
|
452
|
+
opt = dict(webhost=self.ver.__appid__, overwrite=True,
|
|
453
|
+
output_cert=yml['apikey']['gen_cert']['certificate'], output_cert_format='PEM',
|
|
454
|
+
output_pkey=yml['apikey']['gen_cert']['publickey'], output_pkey_format='PEM',
|
|
455
|
+
output_key=yml['apikey']['gen_cert']['privatekey'], output_key_format='PEM',)
|
|
456
|
+
args = argparse.Namespace(**opt)
|
|
457
|
+
status, res, _ = gencert.apprun(self.logger, args, 0, [])
|
|
458
|
+
if status != 0:
|
|
459
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "gen_cert" generate error in "apikey.gen_cert". ({signin_file}) {res}')
|
|
460
|
+
if 'gen_jwt' not in yml['apikey']:
|
|
461
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "gen_jwt" not found in "apikey". ({signin_file})')
|
|
462
|
+
if 'enabled' not in yml['apikey']['gen_jwt']:
|
|
463
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "apikey.gen_jwt". ({signin_file})')
|
|
464
|
+
if type(yml['apikey']['gen_jwt']['enabled']) is not bool:
|
|
465
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "apikey.gen_jwt". ({signin_file})')
|
|
466
|
+
if yml['apikey']['gen_jwt']['enabled']:
|
|
467
|
+
if 'privatekey' not in yml['apikey']['gen_jwt']:
|
|
468
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "privatekey" not found in "apikey.gen_jwt". ({signin_file})')
|
|
469
|
+
if 'privatekey_passphrase' not in yml['apikey']['gen_jwt']:
|
|
470
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "privatekey_passphrase" not found in "apikey.gen_jwt". ({signin_file})')
|
|
471
|
+
if not Path(yml['apikey']['gen_jwt']['privatekey']).is_file():
|
|
472
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "privatekey" file not found in "apikey.gen_jwt". ({signin_file})')
|
|
473
|
+
with open(yml['apikey']['gen_jwt']['privatekey'], 'rb') as f:
|
|
474
|
+
cls.gen_jwt_privatekey = cls.load_pem_private_key(f.read(), yml['apikey']['gen_jwt'].get('privatekey_passphrase', None))
|
|
475
|
+
if 'algorithm' not in yml['apikey']['gen_jwt']:
|
|
476
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "algorithm" not found in "apikey.gen_jwt". ({signin_file})')
|
|
477
|
+
cls.gen_jwt_algorithm = yml['apikey']['gen_jwt']['algorithm']
|
|
478
|
+
if 'claims' not in yml['apikey']['gen_jwt']:
|
|
479
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "claims" not found in "apikey.gen_jwt". ({signin_file})')
|
|
480
|
+
cls.gen_jwt_claims = yml['apikey']['gen_jwt']['claims'].copy()
|
|
481
|
+
if type(yml['apikey']['gen_jwt']['claims']) is not dict:
|
|
482
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "claims" not dict type in "apikey.gen_jwt". ({signin_file})')
|
|
483
|
+
if 'verify_jwt' not in yml['apikey']:
|
|
484
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "verify_jwt" not found in "apikey". ({signin_file})')
|
|
485
|
+
if 'enabled' not in yml['apikey']['verify_jwt']:
|
|
486
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "apikey.verify_jwt". ({signin_file})')
|
|
487
|
+
if type(yml['apikey']['verify_jwt']['enabled']) is not bool:
|
|
488
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "apikey.verify_jwt". ({signin_file})')
|
|
489
|
+
if yml['apikey']['verify_jwt']['enabled']:
|
|
490
|
+
if 'certificate' not in yml['apikey']['verify_jwt']:
|
|
491
|
+
if 'publickey' not in yml['apikey']['verify_jwt']:
|
|
492
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "certificate" or "publickey" not found in "apikey.verify_jwt". ({signin_file})')
|
|
493
|
+
if not Path(yml['apikey']['verify_jwt']['publickey']).is_file():
|
|
494
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "publickey" file not found in "apikey.verify_jwt". ({signin_file})')
|
|
495
|
+
with open(yml['apikey']['verify_jwt']['publickey'], 'rb') as f:
|
|
496
|
+
cls.verify_jwt_publickey_str = f.read()
|
|
497
|
+
cls.verify_jwt_publickey = cls.load_pem_public_key(cls.verify_jwt_publickey_str)
|
|
498
|
+
cls.verify_jwt_publickey_str = cls.verify_jwt_publickey_str.decode('utf-8')
|
|
499
|
+
else:
|
|
500
|
+
if not Path(yml['apikey']['verify_jwt']['certificate']).is_file():
|
|
501
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "certificate" file not found in "apikey.verify_jwt". ({signin_file})')
|
|
502
|
+
with open(yml['apikey']['verify_jwt']['certificate'], 'rb') as f:
|
|
503
|
+
cls.verify_jwt_certificate = cls.load_pem_x509_certificate(f.read())
|
|
504
|
+
cls.verify_jwt_publickey = cls.verify_jwt_certificate.public_key()
|
|
505
|
+
cls.verify_jwt_publickey_str = cls.verify_jwt_publickey.public_bytes(
|
|
506
|
+
encoding=serialization.Encoding.PEM,
|
|
507
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo).decode('utf-8')
|
|
508
|
+
if 'issuer' not in yml['apikey']['verify_jwt']:
|
|
509
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "issuer" not found in "apikey.verify_jwt". ({signin_file})')
|
|
510
|
+
cls.verify_jwt_issuer = yml['apikey']['verify_jwt']['issuer']
|
|
511
|
+
if 'audience' not in yml['apikey']['verify_jwt']:
|
|
512
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "audience" not found in "apikey.verify_jwt". ({signin_file})')
|
|
513
|
+
cls.verify_jwt_audience = yml['apikey']['verify_jwt']['audience']
|
|
514
|
+
if 'algorithm' not in yml['apikey']['verify_jwt']:
|
|
515
|
+
raise HTTPException(status_code=500, detail=f'signin_file format error. "algorithm" not found in "apikey.verify_jwt". ({signin_file})')
|
|
516
|
+
cls.verify_jwt_algorithm = yml['apikey']['verify_jwt']['algorithm']
|
|
393
517
|
# oauth2のフォーマットチェック
|
|
394
518
|
if 'oauth2' not in yml:
|
|
395
519
|
raise HTTPException(status_code=500, detail=f'signin_file format error. "oauth2" not found. ({signin_file})')
|
|
@@ -485,7 +609,7 @@ class Signin(object):
|
|
|
485
609
|
group_names (List[str]): グループ名リスト
|
|
486
610
|
master_groups (List[Dict[str, Any]], optional): 親グループ名. Defaults to None.
|
|
487
611
|
"""
|
|
488
|
-
copy_signin_data = copy.deepcopy(signin_file_data)
|
|
612
|
+
copy_signin_data = copy.deepcopy(dict(groups=signin_file_data['groups']))
|
|
489
613
|
master_groups = copy_signin_data['groups'] if master_groups is None else master_groups
|
|
490
614
|
gns = []
|
|
491
615
|
for gn in group_names.copy():
|
|
@@ -788,16 +912,12 @@ async def create_request_scope(req:Request=None, res:Response=None, websocket:We
|
|
|
788
912
|
これは、FastAPIのDependsで使用されることを意図しています。
|
|
789
913
|
次のように使用します。
|
|
790
914
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
::
|
|
915
|
+
from cmdbox.app.auth import signin
|
|
916
|
+
from fastapi import Depends, Request, Response
|
|
794
917
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
@app.get("/some-endpoint")
|
|
799
|
-
async def some_endpoint(req: Request, res: Response, scope=Depends(signin.create_request_scope)):
|
|
800
|
-
# 何らかの処理
|
|
918
|
+
@app.get("/some-endpoint")
|
|
919
|
+
async def some_endpoint(req: Request, res: Response, scope=Depends(signin.create_request_scope)):
|
|
920
|
+
pass
|
|
801
921
|
|
|
802
922
|
Args:
|
|
803
923
|
req (Request): リクエスト
|
|
@@ -818,18 +938,14 @@ def get_request_scope() -> Dict[str, Any]:
|
|
|
818
938
|
"""
|
|
819
939
|
FastAPIのDepends用に、ContextVarからリクエストスコープを取得します。
|
|
820
940
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
scope['res'] # Responseオブジェクト
|
|
830
|
-
scope['session'] # sessionを表す辞書
|
|
831
|
-
scope['websocket'] # WebSocket接続
|
|
832
|
-
scope['logger'] # loggerオブジェクト
|
|
941
|
+
from cmdbox.app.auth import signin
|
|
942
|
+
from fastapi import Request, Response
|
|
943
|
+
scope = signin.get_request_scope()
|
|
944
|
+
scope['req'] # Requestオブジェクト
|
|
945
|
+
scope['res'] # Responseオブジェクト
|
|
946
|
+
scope['session'] # sessionを表す辞書
|
|
947
|
+
scope['websocket'] # WebSocket接続
|
|
948
|
+
scope['logger'] # loggerオブジェクト
|
|
833
949
|
|
|
834
950
|
Returns:
|
|
835
951
|
Dict[str, Any]: リクエストとレスポンスとWebSocket接続
|
cmdbox/app/common.py
CHANGED
|
@@ -93,7 +93,33 @@ def save_yml(yml_path:Path, data:dict) -> None:
|
|
|
93
93
|
with open(yml_path, 'w') as f:
|
|
94
94
|
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
class CommonValue:
|
|
97
|
+
_map = dict()
|
|
98
|
+
|
|
99
|
+
def set_common_value(key:str, value:Any) -> None:
|
|
100
|
+
"""
|
|
101
|
+
共通の値を設定します。
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
key (str): キー
|
|
105
|
+
value (Any): 値
|
|
106
|
+
"""
|
|
107
|
+
CommonValue._map[key] = value
|
|
108
|
+
|
|
109
|
+
def get_common_value(key:str, default:Any=None) -> Any:
|
|
110
|
+
"""
|
|
111
|
+
共通の値を取得します。
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
key (str): キー
|
|
115
|
+
default (Any, optional): デフォルト値. Defaults to None.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Any: 取得した値。存在しない場合はdefault値を返します。
|
|
119
|
+
"""
|
|
120
|
+
return CommonValue._map.get(key, default)
|
|
121
|
+
|
|
122
|
+
def reset_logger(name:str, stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s - %(message)s', datefmt:str='%Y-%m-%d %H:%M:%S', level:int=logging.INFO) -> None:
|
|
97
123
|
"""
|
|
98
124
|
指定されたロガーのハンドラをクリアし、新しいハンドラを追加します。
|
|
99
125
|
Args:
|
|
@@ -101,13 +127,21 @@ def reset_logger(name:str, stderr:bool=False, fmt:str='[%(asctime)s] %(levelname
|
|
|
101
127
|
stderr (bool, optional): 標準エラー出力を使用するかどうか. Defaults to False.
|
|
102
128
|
fmt (str, optional): ログフォーマット. Defaults to '[%(asctime)s] %(levelname)s - %(message)s'.
|
|
103
129
|
datefmt (str, optional): 日時フォーマット. Defaults to '%Y-%m-%d %H:%M:%S'.
|
|
130
|
+
level (int, optional): ログレベル. Defaults to logging.INFO.
|
|
104
131
|
"""
|
|
105
132
|
logger = logging.getLogger(name)
|
|
106
133
|
logger.handlers.clear()
|
|
107
134
|
logger.propagate = False
|
|
108
|
-
logger.
|
|
135
|
+
logger.setLevel(level)
|
|
136
|
+
logger.addHandler(create_log_handler(stderr, fmt, datefmt, level))
|
|
137
|
+
if get_common_value('webcall', False):
|
|
138
|
+
# webcallの場合はStreamHandlerを削除
|
|
139
|
+
for handler in logger.handlers:
|
|
140
|
+
hc = handler.__class__
|
|
141
|
+
if issubclass(hc, logging.StreamHandler) and not issubclass(hc, logging.FileHandler):
|
|
142
|
+
logger.removeHandler(handler)
|
|
109
143
|
|
|
110
|
-
def create_log_handler(stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s - %(message)s', datefmt:str='%Y-%m-%d %H:%M:%S') -> logging.Handler:
|
|
144
|
+
def create_log_handler(stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s - %(message)s', datefmt:str='%Y-%m-%d %H:%M:%S', level:int=logging.INFO) -> logging.Handler:
|
|
111
145
|
"""
|
|
112
146
|
ログハンドラを生成します。
|
|
113
147
|
|
|
@@ -123,6 +157,7 @@ def create_log_handler(stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s -
|
|
|
123
157
|
# tracebacks_word_wrap=False, log_time_format='[%Y-%m-%d %H:%M]')
|
|
124
158
|
handler = loghandler.ColorfulStreamHandler(sys.stdout if not stderr else sys.stderr)
|
|
125
159
|
handler.setFormatter(formatter)
|
|
160
|
+
handler.setLevel(level)
|
|
126
161
|
return handler
|
|
127
162
|
|
|
128
163
|
def create_console(stderr:bool=False, file=None) -> Console:
|
|
@@ -135,8 +170,10 @@ def create_console(stderr:bool=False, file=None) -> Console:
|
|
|
135
170
|
Returns:
|
|
136
171
|
Console: コンソール
|
|
137
172
|
"""
|
|
138
|
-
console = Console(height=
|
|
139
|
-
|
|
173
|
+
#console = Console(soft_wrap=True, height=True, highlighter=loghandler.LogLevelHighlighter(), theme=loghandler.theme,
|
|
174
|
+
# stderr=stderr, file=file)
|
|
175
|
+
console = Console(height=True, highlighter=loghandler.LogLevelHighlighter(), theme=loghandler.theme,
|
|
176
|
+
soft_wrap=True, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
|
|
140
177
|
#console = Console(soft_wrap=True, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
|
|
141
178
|
return console
|
|
142
179
|
|
|
@@ -153,6 +190,7 @@ def default_logger(debug:bool=False, ver=version, webcall:bool=False) -> logging
|
|
|
153
190
|
logging.Logger: ロガー
|
|
154
191
|
"""
|
|
155
192
|
logger = logging.getLogger(ver.__appid__)
|
|
193
|
+
set_common_value('webcall', webcall)
|
|
156
194
|
if not webcall:
|
|
157
195
|
handler = create_log_handler()
|
|
158
196
|
handler.setLevel(logging.DEBUG if debug else logging.INFO)
|
|
@@ -197,6 +235,7 @@ def load_config(mode:str, debug:bool=False, data=HOME_DIR, webcall:bool=False, v
|
|
|
197
235
|
with open(log_conf_path) as f:
|
|
198
236
|
log_config = yaml.safe_load(f)
|
|
199
237
|
std_key = None
|
|
238
|
+
set_common_value('webcall', webcall)
|
|
200
239
|
for k, h in log_config['handlers'].items():
|
|
201
240
|
if 'filename' in h:
|
|
202
241
|
h['filename'] = data / h['filename']
|
cmdbox/app/edge.py
CHANGED
|
@@ -450,7 +450,7 @@ class Edge(object):
|
|
|
450
450
|
|
|
451
451
|
status, res, headers = self.site_request(self.session.post, "/dosignin/gui", data=dict(name=user, password=password), ok_status=[200, 307])
|
|
452
452
|
if status != 0 or headers.get('signin') is None:
|
|
453
|
-
return 1, dict(warn=f"Signin failed.")
|
|
453
|
+
return 1, dict(warn=f"Signin failed.", headers=headers)
|
|
454
454
|
status, self.user_info = self.load_user_info()
|
|
455
455
|
self.user_info['auth_type'] = auth_type
|
|
456
456
|
self.user_info['password'] = password
|
cmdbox/app/edge_tool.py
CHANGED