fastapi-rtk 2.7.0__tar.gz → 2.8.0__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.
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/PKG-INFO +1 -1
- fastapi_rtk-2.8.0/fastapi_rtk/_version.py +1 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/security.py +1 -1
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/const.py +1 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/apis.py +46 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_import.py +39 -12
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/sqla/test_apis.py +56 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/sqla/test_security_manager.py +55 -0
- fastapi_rtk-2.7.0/fastapi_rtk/_version.py +0 -1
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/.gitignore +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/LICENSE +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/README.md +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/base_api.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/apis/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/apis/info.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/apis/license.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/auth.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/ldap.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/strategies/config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/strategies/db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/auth/strategies/jwt.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/column.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/exceptions.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/generic/session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/column.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/filter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/bases/session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/cli.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/export.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/translate.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/const.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/decorators.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/decorators.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/dependencies.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/exceptions.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/file_managers/file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/file_managers/image_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/globals.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/babel/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/babel/cli.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/babel/config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/babel.cfg +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/lazy_text.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/messages.pot +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/middlewares.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/mixins.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/models.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/py.typed +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/registry.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/routers.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/schemas.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/models.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/setting.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/async_task_runner.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/class_factory.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/csv_json_converter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/deep_merge.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/extender_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/formatter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/hooks.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/lazy.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/merge_schema.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/pydantic.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/run_utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/self_dependencies.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/sqla.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/timezone.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/update_signature.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/utils/use_default_when_none.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/version.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/pyproject.toml +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/docker-compose.gis.yml +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/api/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/api/test_model_rest_api.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/apis/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/apis/test_info_callable_schema.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/apis/test_info_contract.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/auth/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/auth/test_auth_flow.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/auth/test_oauth.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/file_managers/test_file_routes.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/security/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/integration/test_app_smoke.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/model_rest_api/test_columns.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/model_rest_api/test_init.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/api/test_base_api.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/apis/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/apis/test_info.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/apis/test_license.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/hashers/test_scrypt.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/password_helpers/test_fab.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/strategies/test_config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/strategies/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/strategies/test_jwt.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/auth/test_auth.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_column.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_exceptions.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/generic/test_session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_audit.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_audit_types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_column.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/backends/sqla/test_session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_filter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_interface.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_model.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/bases/test_session.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/test_export.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/test_security.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/test_security_crypto.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/commands/test_translate.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/test_cli.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/test_decorators.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/test_types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/cli/test_utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/test_file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/test_image_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/lang/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/lang/test_babel_cli.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/lang/test_babel_config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/lang/test_lazy_text.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/security/sqla/test_models.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_config.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_const.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_db.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_decorators.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_dependencies.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_exceptions.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_filters.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_globals.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_manager.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_middlewares.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_mixins.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_models.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_registry.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_routers.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_schemas.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_setting.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_types.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/test_version.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/__init__.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_async_task_runner.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_class_factory.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_csv_json_converter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_deep_merge.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_extender_mixin.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_formatter.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_hooks.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_lazy.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_merge_schema.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_pydantic.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_run_utils.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_self_dependencies.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_smartdefaultdict.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_sqla.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_timezone.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_update_signature.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/tests/unit/utils/test_use_default_when_none.py +0 -0
- {fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-rtk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
|
|
5
5
|
Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
|
|
6
6
|
Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.8.0"
|
|
@@ -129,7 +129,7 @@ async def import_users(
|
|
|
129
129
|
] = False,
|
|
130
130
|
):
|
|
131
131
|
"""
|
|
132
|
-
Import users from a file produced by export-users
|
|
132
|
+
Import users from a file produced by export-users, skipping case-insensitive username/email matches.
|
|
133
133
|
"""
|
|
134
134
|
payload = _load_import_payload(
|
|
135
135
|
file_path,
|
|
@@ -148,6 +148,7 @@ BEARER_STRATEGY_CONFIG = {
|
|
|
148
148
|
class ErrorCode(str, enum.Enum):
|
|
149
149
|
GET_USER_MISSING_TOKEN_OR_INACTIVE_USER = "GET_USER_MISSING_TOKEN_OR_INACTIVE_USER"
|
|
150
150
|
GET_USER_NO_ROLES = "GET_USER_NO_ROLES"
|
|
151
|
+
USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS"
|
|
151
152
|
PERMISSION_DENIED = "PERMISSION_DENIED"
|
|
152
153
|
FEATURE_NOT_IMPLEMENTED = "FEATURE_NOT_IMPLEMENTED"
|
|
153
154
|
PAGE_NOT_FOUND = "PAGE_NOT_FOUND"
|
|
@@ -238,6 +238,9 @@ class UsersApi(ModelRestApi):
|
|
|
238
238
|
if col.name == "password":
|
|
239
239
|
col.required = False
|
|
240
240
|
|
|
241
|
+
async def pre_add(self, item, params):
|
|
242
|
+
await self._check_user_unique(params.session, item.email, item.username)
|
|
243
|
+
|
|
241
244
|
async def post_add(self, item, params):
|
|
242
245
|
await g.current_app.sm.update_user(
|
|
243
246
|
user_update={"password": item.password},
|
|
@@ -246,6 +249,14 @@ class UsersApi(ModelRestApi):
|
|
|
246
249
|
)
|
|
247
250
|
await self.datamodel.add(params.session, item)
|
|
248
251
|
|
|
252
|
+
async def pre_update_merge(self, item, data, params):
|
|
253
|
+
await self._check_user_unique(
|
|
254
|
+
params.session,
|
|
255
|
+
data.get("email", item.email),
|
|
256
|
+
data.get("username", item.username),
|
|
257
|
+
current_id=item.id,
|
|
258
|
+
)
|
|
259
|
+
|
|
249
260
|
async def post_update(self, item, params):
|
|
250
261
|
if params.body.password:
|
|
251
262
|
await g.current_app.sm.update_user(
|
|
@@ -255,6 +266,41 @@ class UsersApi(ModelRestApi):
|
|
|
255
266
|
)
|
|
256
267
|
await self.datamodel.add(params.session, item)
|
|
257
268
|
|
|
269
|
+
@classmethod
|
|
270
|
+
async def _check_user_unique(
|
|
271
|
+
cls,
|
|
272
|
+
session: SQLASession,
|
|
273
|
+
email: str,
|
|
274
|
+
username: str,
|
|
275
|
+
current_id: int | None = None,
|
|
276
|
+
):
|
|
277
|
+
"""
|
|
278
|
+
Reject a create/update that collides with an existing user.
|
|
279
|
+
|
|
280
|
+
Matching is case-insensitive (mirrors `get_by_email` /
|
|
281
|
+
`get_by_username`), so `A.b@x.com` and `a.b@x.com` are the same
|
|
282
|
+
user and cannot both exist via the API.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
session (SQLASession): The active database session.
|
|
286
|
+
email (str): The email to check for an existing owner.
|
|
287
|
+
username (str): The username to check for an existing owner.
|
|
288
|
+
current_id (int | None, optional): Id of the row being updated, excluded from the conflict check. Defaults to None.
|
|
289
|
+
|
|
290
|
+
Raises:
|
|
291
|
+
fastapi.HTTPException: 400 when another user already owns the email or username.
|
|
292
|
+
"""
|
|
293
|
+
user_db = UserDatabase(session, User)
|
|
294
|
+
for existing in (
|
|
295
|
+
await user_db.get_by_email(email),
|
|
296
|
+
await user_db.get_by_username(username),
|
|
297
|
+
):
|
|
298
|
+
if existing is not None and existing.id != current_id:
|
|
299
|
+
raise fastapi.HTTPException(
|
|
300
|
+
fastapi.status.HTTP_400_BAD_REQUEST,
|
|
301
|
+
ErrorCode.USER_ALREADY_EXISTS,
|
|
302
|
+
)
|
|
303
|
+
|
|
258
304
|
|
|
259
305
|
class RolesApi(ModelRestApi):
|
|
260
306
|
resource_name = "roles"
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/security/sqla/security_manager/_import.py
RENAMED
|
@@ -17,7 +17,10 @@ inserts rather than N reads plus N inserts.
|
|
|
17
17
|
import collections.abc
|
|
18
18
|
import typing
|
|
19
19
|
|
|
20
|
+
import sqlalchemy
|
|
21
|
+
|
|
20
22
|
from ....backends.sqla.session import SQLASession
|
|
23
|
+
from ....utils import smart_run
|
|
21
24
|
|
|
22
25
|
if typing.TYPE_CHECKING:
|
|
23
26
|
from ..models import Role
|
|
@@ -43,12 +46,14 @@ async def import_users(
|
|
|
43
46
|
|
|
44
47
|
The payload's top-level keys are usernames; each value is the user record
|
|
45
48
|
dict (email, password hash, first/last name, roles list, optional active
|
|
46
|
-
flag).
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
flag). A record is skipped when its username or email case-insensitively
|
|
50
|
+
matches an existing user or an already-imported record, so a case-variant
|
|
51
|
+
of an existing account is never inserted as a second row. Password hashes
|
|
52
|
+
are written verbatim: callers are responsible for ensuring the source dump
|
|
53
|
+
matches the destination's hashing scheme.
|
|
49
54
|
|
|
50
|
-
Bulk lookups: one
|
|
51
|
-
referenced roles, regardless of payload size.
|
|
55
|
+
Bulk lookups: one case-insensitive query for existing usernames/emails and
|
|
56
|
+
one for referenced roles, regardless of payload size.
|
|
52
57
|
|
|
53
58
|
Args:
|
|
54
59
|
sm (SecurityManager): The owning security manager (provides
|
|
@@ -71,17 +76,28 @@ async def import_users(
|
|
|
71
76
|
if not payload:
|
|
72
77
|
return {"imported": [], "skipped": []}
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
user_model = sm.user_model
|
|
80
|
+
lowered_usernames = [username.lower() for username in payload]
|
|
81
|
+
lowered_emails = [
|
|
82
|
+
(record.get("email") or "").lower() for record in payload.values()
|
|
83
|
+
]
|
|
84
|
+
statement = sqlalchemy.select(user_model.username, user_model.email).where(
|
|
85
|
+
sqlalchemy.or_(
|
|
86
|
+
sqlalchemy.func.lower(user_model.username).in_(lowered_usernames),
|
|
87
|
+
sqlalchemy.func.lower(user_model.email).in_(lowered_emails),
|
|
88
|
+
)
|
|
78
89
|
)
|
|
79
|
-
|
|
90
|
+
existing = await smart_run(session.execute, statement)
|
|
91
|
+
existing_usernames_lc: set[str] = set()
|
|
92
|
+
existing_emails_lc: set[str] = set()
|
|
93
|
+
for username, email in existing.all():
|
|
94
|
+
existing_usernames_lc.add(username.lower())
|
|
95
|
+
existing_emails_lc.add((email or "").lower())
|
|
80
96
|
|
|
81
97
|
per_user_role_names: dict[str, list[str]] = {
|
|
82
98
|
username: _normalize_role_names(record.get("roles") or [])
|
|
83
99
|
for username, record in payload.items()
|
|
84
|
-
if username not in
|
|
100
|
+
if username.lower() not in existing_usernames_lc
|
|
85
101
|
}
|
|
86
102
|
referenced_role_names: set[str] = set()
|
|
87
103
|
for names in per_user_role_names.values():
|
|
@@ -96,10 +112,21 @@ async def import_users(
|
|
|
96
112
|
|
|
97
113
|
imported: list[str] = []
|
|
98
114
|
skipped: list[str] = []
|
|
115
|
+
seen_usernames_lc: set[str] = set()
|
|
116
|
+
seen_emails_lc: set[str] = set()
|
|
99
117
|
for username, record in payload.items():
|
|
100
|
-
|
|
118
|
+
username_lc = username.lower()
|
|
119
|
+
email_lc = (record.get("email") or "").lower()
|
|
120
|
+
if (
|
|
121
|
+
username_lc in existing_usernames_lc
|
|
122
|
+
or username_lc in seen_usernames_lc
|
|
123
|
+
or email_lc in existing_emails_lc
|
|
124
|
+
or email_lc in seen_emails_lc
|
|
125
|
+
):
|
|
101
126
|
skipped.append(username)
|
|
102
127
|
continue
|
|
128
|
+
seen_usernames_lc.add(username_lc)
|
|
129
|
+
seen_emails_lc.add(email_lc)
|
|
103
130
|
role_objs = [role_map[name] for name in per_user_role_names[username]]
|
|
104
131
|
user = sm.user_model(
|
|
105
132
|
username=username,
|
|
@@ -11,6 +11,8 @@ can be tested with a constructed UsersApi without going through
|
|
|
11
11
|
|
|
12
12
|
import types
|
|
13
13
|
|
|
14
|
+
import fastapi
|
|
15
|
+
import pytest
|
|
14
16
|
|
|
15
17
|
from fastapi_rtk.security.sqla.apis import (
|
|
16
18
|
AuthApi,
|
|
@@ -161,3 +163,57 @@ class TestAuthApiSchemas:
|
|
|
161
163
|
api = object.__new__(AuthApi)
|
|
162
164
|
schema = api.update_user_schema
|
|
163
165
|
assert schema is not None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.mark.asyncio
|
|
169
|
+
class TestUsersApiUniquenessGuard:
|
|
170
|
+
def _seed(self, session, username, email):
|
|
171
|
+
user = User(username=username, email=email, password="h")
|
|
172
|
+
session.add(user)
|
|
173
|
+
session.commit()
|
|
174
|
+
return user
|
|
175
|
+
|
|
176
|
+
def _api(self):
|
|
177
|
+
return object.__new__(UsersApi)
|
|
178
|
+
|
|
179
|
+
async def test_pre_add_rejects_case_variant_email(self, session):
|
|
180
|
+
self._seed(session, "existing", "Foo@X.com")
|
|
181
|
+
item = User(username="brandnew", email="foo@x.com", password="h")
|
|
182
|
+
with pytest.raises(fastapi.HTTPException) as exc:
|
|
183
|
+
await self._api().pre_add(item, types.SimpleNamespace(session=session))
|
|
184
|
+
assert exc.value.status_code == 400
|
|
185
|
+
|
|
186
|
+
async def test_pre_add_rejects_case_variant_username(self, session):
|
|
187
|
+
self._seed(session, "Admin", "admin@x.com")
|
|
188
|
+
item = User(username="admin", email="fresh@x.com", password="h")
|
|
189
|
+
with pytest.raises(fastapi.HTTPException):
|
|
190
|
+
await self._api().pre_add(item, types.SimpleNamespace(session=session))
|
|
191
|
+
|
|
192
|
+
async def test_pre_add_allows_unique_user(self, session):
|
|
193
|
+
self._seed(session, "existing", "foo@x.com")
|
|
194
|
+
item = User(username="brandnew", email="bar@x.com", password="h")
|
|
195
|
+
assert (
|
|
196
|
+
await self._api().pre_add(item, types.SimpleNamespace(session=session))
|
|
197
|
+
is None
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
async def test_pre_update_merge_rejects_other_users_email(self, session):
|
|
201
|
+
self._seed(session, "aa", "a@x.com")
|
|
202
|
+
target = self._seed(session, "bb", "b@x.com")
|
|
203
|
+
with pytest.raises(fastapi.HTTPException):
|
|
204
|
+
await self._api().pre_update_merge(
|
|
205
|
+
target,
|
|
206
|
+
{"email": "A@X.com"},
|
|
207
|
+
types.SimpleNamespace(session=session),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
async def test_pre_update_merge_allows_own_email(self, session):
|
|
211
|
+
target = self._seed(session, "aa", "A@x.com")
|
|
212
|
+
assert (
|
|
213
|
+
await self._api().pre_update_merge(
|
|
214
|
+
target,
|
|
215
|
+
{"email": "a@x.com"},
|
|
216
|
+
types.SimpleNamespace(session=session),
|
|
217
|
+
)
|
|
218
|
+
is None
|
|
219
|
+
)
|
|
@@ -382,6 +382,61 @@ class TestImportData:
|
|
|
382
382
|
result = await sm.import_data("users", payload, session=session)
|
|
383
383
|
assert result["imported"] == ["admin"]
|
|
384
384
|
|
|
385
|
+
async def test_import_users_skips_case_variant_username(self, sm, session):
|
|
386
|
+
base = {
|
|
387
|
+
"password": "$2b$12$x",
|
|
388
|
+
"first_name": "",
|
|
389
|
+
"last_name": "",
|
|
390
|
+
"roles": [],
|
|
391
|
+
}
|
|
392
|
+
await sm.import_data(
|
|
393
|
+
"users",
|
|
394
|
+
{"Admin": {"email": "a@x.com", "username": "Admin", **base}},
|
|
395
|
+
session=session,
|
|
396
|
+
)
|
|
397
|
+
result = await sm.import_data(
|
|
398
|
+
"users",
|
|
399
|
+
{"admin": {"email": "different@x.com", "username": "admin", **base}},
|
|
400
|
+
session=session,
|
|
401
|
+
)
|
|
402
|
+
assert result["imported"] == []
|
|
403
|
+
assert result["skipped"] == ["admin"]
|
|
404
|
+
|
|
405
|
+
async def test_import_users_skips_case_variant_email(self, sm, session):
|
|
406
|
+
base = {
|
|
407
|
+
"password": "$2b$12$x",
|
|
408
|
+
"first_name": "",
|
|
409
|
+
"last_name": "",
|
|
410
|
+
"roles": [],
|
|
411
|
+
}
|
|
412
|
+
await sm.import_data(
|
|
413
|
+
"users",
|
|
414
|
+
{"admin": {"email": "Admin@Example.com", "username": "admin", **base}},
|
|
415
|
+
session=session,
|
|
416
|
+
)
|
|
417
|
+
result = await sm.import_data(
|
|
418
|
+
"users",
|
|
419
|
+
{"other": {"email": "admin@example.com", "username": "other", **base}},
|
|
420
|
+
session=session,
|
|
421
|
+
)
|
|
422
|
+
assert result["imported"] == []
|
|
423
|
+
assert result["skipped"] == ["other"]
|
|
424
|
+
|
|
425
|
+
async def test_import_users_dedups_case_variant_within_payload(self, sm, session):
|
|
426
|
+
base = {
|
|
427
|
+
"password": "$2b$12$x",
|
|
428
|
+
"first_name": "",
|
|
429
|
+
"last_name": "",
|
|
430
|
+
"roles": [],
|
|
431
|
+
}
|
|
432
|
+
payload = {
|
|
433
|
+
"first": {"email": "Dup@x.com", "username": "first", **base},
|
|
434
|
+
"second": {"email": "dup@x.com", "username": "second", **base},
|
|
435
|
+
}
|
|
436
|
+
result = await sm.import_data("users", payload, session=session)
|
|
437
|
+
assert result["imported"] == ["first"]
|
|
438
|
+
assert result["skipped"] == ["second"]
|
|
439
|
+
|
|
385
440
|
|
|
386
441
|
class TestShouldReraise:
|
|
387
442
|
"""Behavior of the `_should_reraise` helper that backs `raise_exception`."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.7.0"
|
|
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
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.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
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.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
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{fastapi_rtk-2.7.0 → fastapi_rtk-2.8.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|