nene2-python 1.8.28__tar.gz → 1.8.29__tar.gz
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.
- {nene2_python-1.8.28 → nene2_python-1.8.29}/CHANGELOG.md +13 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/PKG-INFO +1 -1
- nene2_python-1.8.29/docs/field-trials/2026-05-field-trial-84.md +151 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/pyproject.toml +1 -1
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/__init__.py +2 -0
- nene2_python-1.8.29/src/nene2/auth/deps.py +50 -0
- nene2_python-1.8.29/tests/nene2/auth/test_make_require_auth.py +99 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/uv.lock +1 -1
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.env.example +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.gitignore +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/AGENTS.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/CLAUDE.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/Dockerfile +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/LICENSE +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/README.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/README +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/env.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic.ini +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/compose.yaml +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/de/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/fr/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/api.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/roadmap.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/todo/current.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/zh/index.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/package-lock.json +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/package.json +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/__main__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/app.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/mcp.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/schema.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -5,6 +5,19 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [1.8.28] — 2026-05-20
|
|
9
|
+
|
|
10
|
+
FT83 フィールドトライアル — Depends() DI パターン検証と PaginationResponse / PaginationDep 改善。
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `PaginationResponse.model_dump()` を `to_dict()` の Pydantic 互換エイリアスとして追加 (#355) (FT83)
|
|
14
|
+
— Pydantic v2 ユーザーが `model_dump()` を期待して AttributeError になる問題を解消
|
|
15
|
+
- `PaginationDep` 型エイリアスを `nene2.http` に追加 (#355) (FT83)
|
|
16
|
+
— `Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]` の省略形
|
|
17
|
+
- Field trial report: `docs/field-trials/2026-05-field-trial-83.md` (FT83)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
8
21
|
## [1.8.27] — 2026-05-20
|
|
9
22
|
|
|
10
23
|
FT81 フィールドトライアル — CORS 設定パターン検証と setup_middlewares() への CORS 統合。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.29
|
|
4
4
|
Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
|
|
5
5
|
Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
|
|
6
6
|
Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# FT84: 認証 Depends ユーティリティ — CurrentUser / require_auth パターン検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**テーマ**: nene2.auth に認証 Depends ユーティリティがない摩擦点と make_require_auth 設計検証
|
|
5
|
+
**バージョン**: v1.8.28
|
|
6
|
+
**FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft84-auth-depends/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`LocalTokenVerifier` と `TokenVerifierProtocol` は実装されているが、
|
|
13
|
+
FastAPI の Depends パターンに接続するユーティリティがない。
|
|
14
|
+
ユーザーは `HTTPBearer` → `verify()` → 未認証 401 を毎プロジェクトで手組みする必要がある。
|
|
15
|
+
`make_require_auth(verifier)` ファクトリーを追加することで解消できる。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 発見した問題
|
|
20
|
+
|
|
21
|
+
### 問題1: 認証 Depends を毎回手組みする必要がある
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# 現状: プロジェクトごとに以下のコードを書く必要がある (50+ 行のボイラープレート)
|
|
25
|
+
|
|
26
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
27
|
+
from nene2.auth import LocalTokenVerifier
|
|
28
|
+
|
|
29
|
+
security = HTTPBearer(auto_error=False)
|
|
30
|
+
_verifier = LocalTokenVerifier.from_env("BEARER_TOKENS")
|
|
31
|
+
|
|
32
|
+
def get_current_user(
|
|
33
|
+
credentials: HTTPAuthorizationCredentials | None = Depends(security),
|
|
34
|
+
) -> str | None:
|
|
35
|
+
if credentials is None:
|
|
36
|
+
return None
|
|
37
|
+
if not _verifier.verify(credentials.credentials):
|
|
38
|
+
return None
|
|
39
|
+
return credentials.credentials
|
|
40
|
+
|
|
41
|
+
def require_auth(user: str | None = Depends(get_current_user)) -> str:
|
|
42
|
+
if user is None:
|
|
43
|
+
raise HTTPException(status_code=401)
|
|
44
|
+
return user
|
|
45
|
+
|
|
46
|
+
# ハンドラーで使う
|
|
47
|
+
@app.post("/items")
|
|
48
|
+
def create(body: ItemBody, user: str = Depends(require_auth)) -> JSONResponse: ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 問題2: 任意認証パターンが冗長
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# 未認証でも通るが user を取得したいパターン
|
|
55
|
+
def optional_auth(user: str | None = Depends(get_current_user)) -> str | None:
|
|
56
|
+
return user # None = 未認証、str = ユーザーID
|
|
57
|
+
|
|
58
|
+
@app.get("/feed")
|
|
59
|
+
def get_feed(user: str | None = Depends(optional_auth)) -> JSONResponse:
|
|
60
|
+
if user:
|
|
61
|
+
return JSONResponse({"personalized": True})
|
|
62
|
+
return JSONResponse({"personalized": False})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 問題3: TokenVerifierProtocol を Depends で直接使えない
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# こういう書き方をしたくなるが Depends は callable を期待する
|
|
69
|
+
@app.post("/items")
|
|
70
|
+
def create(
|
|
71
|
+
body: ItemBody,
|
|
72
|
+
token: Annotated[str, Depends(LocalTokenVerifier.from_env("TOKENS"))], # ← 機能しない
|
|
73
|
+
) -> JSONResponse: ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## テスト結果(全12件パス)
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
test_public_endpoint_no_auth PASSED
|
|
82
|
+
test_create_item_with_valid_token PASSED
|
|
83
|
+
test_create_item_without_token_returns_401 PASSED
|
|
84
|
+
test_create_item_with_invalid_token_returns_401 PASSED
|
|
85
|
+
test_get_me_returns_user_info PASSED
|
|
86
|
+
test_delete_item_authenticated PASSED
|
|
87
|
+
test_delete_item_unauthenticated_returns_401 PASSED
|
|
88
|
+
test_request_id_present_on_401 PASSED # nene2 ミドルウェアと共存
|
|
89
|
+
test_security_headers_present_on_401 PASSED # nene2 ミドルウェアと共存
|
|
90
|
+
test_friction_boilerplate_for_auth_depends PASSED # 摩擦記録
|
|
91
|
+
test_friction_optional_auth_pattern_verbose PASSED # 摩擦記録
|
|
92
|
+
test_friction_verifier_not_injectable_as_depends PASSED # 摩擦記録
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 摩擦ポイント一覧
|
|
98
|
+
|
|
99
|
+
| ID | 内容 | 深刻度 |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| F84-1 | 認証 Depends ボイラープレートを毎プロジェクトで手組みする必要がある | 中 |
|
|
102
|
+
| F84-2 | 任意認証(Optional User)パターンが冗長 | 低 |
|
|
103
|
+
| F84-3 | `TokenVerifierProtocol` を Depends で直接使えない | 低 |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 使用感(主観評価)
|
|
108
|
+
|
|
109
|
+
### 直感性 ★★☆☆☆
|
|
110
|
+
|
|
111
|
+
「nene2 でトークン認証を追加する」という操作に必要なステップが多すぎる。
|
|
112
|
+
`BearerTokenMiddleware` は全経路に認証を入れるには便利だが、
|
|
113
|
+
「このエンドポイントだけ認証を要求」パターンに Depends ユーティリティがない。
|
|
114
|
+
FastAPI の HTTPBearer を使う方法は FastAPI のドキュメントを参照しなければわからない。
|
|
115
|
+
|
|
116
|
+
### 実害の深刻さ ★★★☆☆
|
|
117
|
+
|
|
118
|
+
認証は多くのプロジェクトで必要。毎回 50 行のボイラープレートを書くのは
|
|
119
|
+
DRY 原則に反する。バグが入りやすく、テスト漏れにもつながる。
|
|
120
|
+
|
|
121
|
+
### 修正のしやすさ ★★★★★
|
|
122
|
+
|
|
123
|
+
`make_require_auth(verifier)` 関数を `nene2.auth` に追加するだけ:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
def make_require_auth(verifier: TokenVerifierProtocol) -> Callable[..., str]:
|
|
127
|
+
security = HTTPBearer(auto_error=False)
|
|
128
|
+
def get_current_user(credentials: ... = Depends(security)) -> str | None:
|
|
129
|
+
if credentials is None or not verifier.verify(credentials.credentials):
|
|
130
|
+
return None
|
|
131
|
+
return credentials.credentials
|
|
132
|
+
def require_auth(user: str | None = Depends(get_current_user)) -> str:
|
|
133
|
+
if user is None:
|
|
134
|
+
raise HTTPException(status_code=401)
|
|
135
|
+
return user
|
|
136
|
+
return require_auth
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 総合コメント
|
|
140
|
+
|
|
141
|
+
nene2 の認証機能は「ミドルウェアで全経路を保護する」ユースケースは
|
|
142
|
+
`BearerTokenMiddleware` でカバーされている。
|
|
143
|
+
不足しているのは「特定エンドポイントのみ認証を要求する」ユースケース。
|
|
144
|
+
`make_require_auth()` を追加することで「nene2 だけで完結」できる。
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 推奨アクション
|
|
149
|
+
|
|
150
|
+
1. **Issue #358**: `nene2.auth` に `make_require_auth(verifier)` ファクトリーを追加
|
|
151
|
+
— `require_auth = make_require_auth(LocalTokenVerifier.from_env("TOKENS"))` パターンを実現
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .api_key import ApiKeyAuthMiddleware
|
|
4
4
|
from .bearer_token import BearerTokenMiddleware
|
|
5
|
+
from .deps import make_require_auth
|
|
5
6
|
from .exceptions import TokenVerificationException
|
|
6
7
|
from .interfaces import TokenIssuerProtocol, TokenVerifierProtocol
|
|
7
8
|
from .local_verifier import LocalTokenVerifier
|
|
@@ -13,4 +14,5 @@ __all__ = [
|
|
|
13
14
|
"TokenIssuerProtocol",
|
|
14
15
|
"TokenVerificationException",
|
|
15
16
|
"TokenVerifierProtocol",
|
|
17
|
+
"make_require_auth",
|
|
16
18
|
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""FastAPI Depends utilities for authentication.
|
|
2
|
+
|
|
3
|
+
Provides ``make_require_auth`` to wire :class:`TokenVerifierProtocol` into
|
|
4
|
+
FastAPI's dependency injection system without boilerplate.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
from fastapi import Depends, HTTPException
|
|
11
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
12
|
+
|
|
13
|
+
from .interfaces import TokenVerifierProtocol
|
|
14
|
+
|
|
15
|
+
_security = HTTPBearer(auto_error=False)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def make_require_auth(verifier: TokenVerifierProtocol) -> Callable[..., str]:
|
|
19
|
+
"""Return a FastAPI Depends-compatible callable that enforces token authentication.
|
|
20
|
+
|
|
21
|
+
Usage::
|
|
22
|
+
|
|
23
|
+
from nene2.auth import LocalTokenVerifier, make_require_auth
|
|
24
|
+
|
|
25
|
+
verifier = LocalTokenVerifier.from_env("BEARER_TOKENS")
|
|
26
|
+
require_auth = make_require_auth(verifier)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.post("/items")
|
|
30
|
+
def create_item(
|
|
31
|
+
body: ItemBody,
|
|
32
|
+
token: Annotated[str, Depends(require_auth)],
|
|
33
|
+
) -> JSONResponse: ...
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
verifier: A :class:`TokenVerifierProtocol` implementation to validate tokens.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A dependency function that returns the raw token string when authenticated,
|
|
40
|
+
or raises ``HTTP 401`` when the token is absent or invalid.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def _get_token(
|
|
44
|
+
credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(_security)],
|
|
45
|
+
) -> str:
|
|
46
|
+
if credentials is None or not verifier.verify(credentials.credentials):
|
|
47
|
+
raise HTTPException(status_code=401, detail="Invalid or missing token")
|
|
48
|
+
return credentials.credentials
|
|
49
|
+
|
|
50
|
+
return _get_token
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Tests for make_require_auth() Depends factory."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, FastAPI
|
|
6
|
+
from fastapi.responses import JSONResponse
|
|
7
|
+
from fastapi.testclient import TestClient
|
|
8
|
+
|
|
9
|
+
from nene2.auth import LocalTokenVerifier, make_require_auth
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _make_app(tokens: list[str]) -> FastAPI:
|
|
13
|
+
verifier = LocalTokenVerifier(tokens)
|
|
14
|
+
require_auth = make_require_auth(verifier)
|
|
15
|
+
|
|
16
|
+
app = FastAPI()
|
|
17
|
+
|
|
18
|
+
@app.get("/public")
|
|
19
|
+
def public() -> JSONResponse:
|
|
20
|
+
return JSONResponse({"public": True})
|
|
21
|
+
|
|
22
|
+
@app.get("/protected")
|
|
23
|
+
def protected(token: Annotated[str, Depends(require_auth)]) -> JSONResponse:
|
|
24
|
+
return JSONResponse({"token": token})
|
|
25
|
+
|
|
26
|
+
@app.post("/items")
|
|
27
|
+
def create(token: Annotated[str, Depends(require_auth)]) -> JSONResponse:
|
|
28
|
+
return JSONResponse({"created_by": token[:8]}, status_code=201)
|
|
29
|
+
|
|
30
|
+
return app
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_valid_token_returns_200() -> None:
|
|
34
|
+
client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
|
|
35
|
+
r = client.get("/protected", headers={"Authorization": "Bearer secret-token"})
|
|
36
|
+
assert r.status_code == 200
|
|
37
|
+
assert r.json()["token"] == "secret-token" # noqa: S105
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_missing_token_returns_401() -> None:
|
|
41
|
+
client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
|
|
42
|
+
r = client.get("/protected")
|
|
43
|
+
assert r.status_code == 401
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_invalid_token_returns_401() -> None:
|
|
47
|
+
client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
|
|
48
|
+
r = client.get("/protected", headers={"Authorization": "Bearer wrong-token"})
|
|
49
|
+
assert r.status_code == 401
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_public_endpoint_no_auth_required() -> None:
|
|
53
|
+
client = TestClient(_make_app([]), raise_server_exceptions=False)
|
|
54
|
+
r = client.get("/public")
|
|
55
|
+
assert r.status_code == 200
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_post_with_valid_token_returns_201() -> None:
|
|
59
|
+
client = TestClient(_make_app(["my-api-key"]), raise_server_exceptions=False)
|
|
60
|
+
r = client.post("/items", headers={"Authorization": "Bearer my-api-key"})
|
|
61
|
+
assert r.status_code == 201
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_multiple_valid_tokens() -> None:
|
|
65
|
+
client = TestClient(_make_app(["token-a", "token-b"]), raise_server_exceptions=False)
|
|
66
|
+
r_a = client.get("/protected", headers={"Authorization": "Bearer token-a"})
|
|
67
|
+
r_b = client.get("/protected", headers={"Authorization": "Bearer token-b"})
|
|
68
|
+
assert r_a.status_code == 200
|
|
69
|
+
assert r_b.status_code == 200
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_empty_allowlist_rejects_all() -> None:
|
|
73
|
+
client = TestClient(_make_app([]), raise_server_exceptions=False)
|
|
74
|
+
r = client.get("/protected", headers={"Authorization": "Bearer any-token"})
|
|
75
|
+
assert r.status_code == 401
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_different_verifiers_independent() -> None:
|
|
79
|
+
"""2つの異なる verifier を同じアプリで使える。"""
|
|
80
|
+
verifier_a = LocalTokenVerifier(["token-a"])
|
|
81
|
+
verifier_b = LocalTokenVerifier(["token-b"])
|
|
82
|
+
require_a = make_require_auth(verifier_a)
|
|
83
|
+
require_b = make_require_auth(verifier_b)
|
|
84
|
+
|
|
85
|
+
app = FastAPI()
|
|
86
|
+
|
|
87
|
+
@app.get("/route-a")
|
|
88
|
+
def route_a(token: Annotated[str, Depends(require_a)]) -> JSONResponse:
|
|
89
|
+
return JSONResponse({"token": token})
|
|
90
|
+
|
|
91
|
+
@app.get("/route-b")
|
|
92
|
+
def route_b(token: Annotated[str, Depends(require_b)]) -> JSONResponse:
|
|
93
|
+
return JSONResponse({"token": token})
|
|
94
|
+
|
|
95
|
+
client = TestClient(app, raise_server_exceptions=False)
|
|
96
|
+
assert client.get("/route-a", headers={"Authorization": "Bearer token-a"}).status_code == 200
|
|
97
|
+
assert client.get("/route-a", headers={"Authorization": "Bearer token-b"}).status_code == 401
|
|
98
|
+
assert client.get("/route-b", headers={"Authorization": "Bearer token-b"}).status_code == 200
|
|
99
|
+
assert client.get("/route-b", headers={"Authorization": "Bearer token-a"}).status_code == 401
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/versions/001_create_notes_and_tags_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|