fastapi-rtk 2.12.0__tar.gz → 2.12.2__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.
Files changed (310) hide show
  1. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/PKG-INFO +1 -1
  2. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/__init__.py +3 -2
  3. fastapi_rtk-2.12.2/fastapi_rtk/_version.py +1 -0
  4. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/__init__.py +2 -0
  5. fastapi_rtk-2.12.2/fastapi_rtk/bases/user_cache.py +41 -0
  6. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/const.py +1 -0
  7. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/globals.py +18 -0
  8. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/apis.py +1 -1
  9. fastapi_rtk-2.12.2/fastapi_rtk/security/sqla/user_cache.py +238 -0
  10. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/setting.py +24 -0
  11. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_user_cache.py +117 -17
  12. fastapi_rtk-2.12.0/fastapi_rtk/_version.py +0 -1
  13. fastapi_rtk-2.12.0/fastapi_rtk/security/sqla/user_cache.py +0 -178
  14. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/.gitignore +0 -0
  15. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/LICENSE +0 -0
  16. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/README.md +0 -0
  17. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/__init__.py +0 -0
  18. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/base_api.py +0 -0
  19. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  20. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
  21. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
  22. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
  23. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
  24. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
  25. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
  26. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
  27. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
  28. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
  29. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
  30. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
  31. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
  32. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
  33. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/apis/__init__.py +0 -0
  34. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/apis/info.py +0 -0
  35. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/apis/license.py +0 -0
  36. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/__init__.py +0 -0
  37. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/auth.py +0 -0
  38. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  39. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  40. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  41. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/ldap.py +0 -0
  42. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  43. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  44. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  45. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/strategies/config.py +0 -0
  46. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/strategies/db.py +0 -0
  47. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  48. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/__init__.py +0 -0
  49. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/__init__.py +0 -0
  50. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/column.py +0 -0
  51. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/db.py +0 -0
  52. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  53. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/filters.py +0 -0
  54. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/interface.py +0 -0
  55. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/model.py +0 -0
  56. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/generic/session.py +0 -0
  57. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  58. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/column.py +0 -0
  59. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/db.py +0 -0
  60. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  61. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  62. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  63. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
  64. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
  65. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  66. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
  67. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  68. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  69. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  70. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  71. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  72. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/filters.py +0 -0
  73. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/interface.py +0 -0
  74. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/model.py +0 -0
  75. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/backends/sqla/session.py +0 -0
  76. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/cache.py +0 -0
  77. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/db.py +0 -0
  78. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/file_manager.py +0 -0
  79. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/filter.py +0 -0
  80. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/interface.py +0 -0
  81. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/model.py +0 -0
  82. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/bases/session.py +0 -0
  83. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/caches/__init__.py +0 -0
  84. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/caches/cache.py +0 -0
  85. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/caches/serializers.py +0 -0
  86. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/caches/storages.py +0 -0
  87. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/__init__.py +0 -0
  88. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/cli.py +0 -0
  89. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/__init__.py +0 -0
  90. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  91. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  92. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  93. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  94. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  95. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  96. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  97. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  98. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  99. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  100. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/export.py +0 -0
  101. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/security.py +0 -0
  102. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/commands/translate.py +0 -0
  103. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/const.py +0 -0
  104. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/decorators.py +0 -0
  105. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/types.py +0 -0
  106. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/cli/utils.py +0 -0
  107. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/config.py +0 -0
  108. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/db.py +0 -0
  109. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/decorators.py +0 -0
  110. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/dependencies.py +0 -0
  111. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/exceptions.py +0 -0
  112. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  113. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/file_managers/__init__.py +0 -0
  114. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/file_managers/file_manager.py +0 -0
  115. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/file_managers/image_manager.py +0 -0
  116. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  117. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  118. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/filters.py +0 -0
  119. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/__init__.py +0 -0
  120. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/babel/__init__.py +0 -0
  121. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/babel/cli.py +0 -0
  122. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/babel/config.py +0 -0
  123. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/babel.cfg +0 -0
  124. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/lazy_text.py +0 -0
  125. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/messages.pot +0 -0
  126. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  127. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  128. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  129. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  130. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/manager.py +0 -0
  131. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/middlewares.py +0 -0
  132. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/mixins.py +0 -0
  133. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/models.py +0 -0
  134. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/py.typed +0 -0
  135. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/registry.py +0 -0
  136. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/routers.py +0 -0
  137. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/schemas.py +0 -0
  138. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/__init__.py +0 -0
  139. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/__init__.py +0 -0
  140. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/models.py +0 -0
  141. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  142. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
  143. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
  144. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
  145. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  146. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  147. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  148. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
  149. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
  150. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/types.py +0 -0
  151. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/__init__.py +0 -0
  152. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/async_task_runner.py +0 -0
  153. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/class_factory.py +0 -0
  154. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  155. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/deep_merge.py +0 -0
  156. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/extender_mixin.py +0 -0
  157. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  158. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/formatter.py +0 -0
  159. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/hooks.py +0 -0
  160. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/lazy.py +0 -0
  161. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/merge_schema.py +0 -0
  162. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  163. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/pydantic.py +0 -0
  164. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/run_utils.py +0 -0
  165. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/self_dependencies.py +0 -0
  166. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  167. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/sqla.py +0 -0
  168. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/timezone.py +0 -0
  169. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/update_signature.py +0 -0
  170. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  171. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/fastapi_rtk/version.py +0 -0
  172. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/pyproject.toml +0 -0
  173. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/conftest.py +0 -0
  174. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/docker-compose.gis.yml +0 -0
  175. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/__init__.py +0 -0
  176. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/api/__init__.py +0 -0
  177. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/api/test_model_rest_api.py +0 -0
  178. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/apis/__init__.py +0 -0
  179. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/apis/test_info_callable_schema.py +0 -0
  180. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/apis/test_info_contract.py +0 -0
  181. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/auth/__init__.py +0 -0
  182. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/auth/test_auth_flow.py +0 -0
  183. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/auth/test_oauth.py +0 -0
  184. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/conftest.py +0 -0
  185. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/file_managers/__init__.py +0 -0
  186. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/file_managers/test_file_routes.py +0 -0
  187. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/security/__init__.py +0 -0
  188. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/security/sqla/__init__.py +0 -0
  189. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/security/sqla/test_security_manager.py +0 -0
  190. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/integration/test_app_smoke.py +0 -0
  191. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/__init__.py +0 -0
  192. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/__init__.py +0 -0
  193. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/conftest.py +0 -0
  194. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/model_rest_api/__init__.py +0 -0
  195. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  196. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  197. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/model_rest_api/test_init.py +0 -0
  198. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
  199. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/api/test_base_api.py +0 -0
  200. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/apis/__init__.py +0 -0
  201. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/apis/test_info.py +0 -0
  202. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/apis/test_license.py +0 -0
  203. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/__init__.py +0 -0
  204. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/hashers/__init__.py +0 -0
  205. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  206. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  207. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/password_helpers/__init__.py +0 -0
  208. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  209. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/strategies/__init__.py +0 -0
  210. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/strategies/test_config.py +0 -0
  211. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/strategies/test_db.py +0 -0
  212. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/strategies/test_jwt.py +0 -0
  213. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/auth/test_auth.py +0 -0
  214. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/__init__.py +0 -0
  215. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/__init__.py +0 -0
  216. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_column.py +0 -0
  217. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_db.py +0 -0
  218. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_exceptions.py +0 -0
  219. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_filters.py +0 -0
  220. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_interface.py +0 -0
  221. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_model.py +0 -0
  222. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/generic/test_session.py +0 -0
  223. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/__init__.py +0 -0
  224. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/conftest.py +0 -0
  225. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_audit.py +0 -0
  226. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  227. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_column.py +0 -0
  228. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_db.py +0 -0
  229. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_filters.py +0 -0
  230. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  231. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_interface.py +0 -0
  232. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_model.py +0 -0
  233. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/backends/sqla/test_session.py +0 -0
  234. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/__init__.py +0 -0
  235. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_db.py +0 -0
  236. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_file_manager.py +0 -0
  237. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_filter.py +0 -0
  238. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_interface.py +0 -0
  239. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_model.py +0 -0
  240. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/bases/test_session.py +0 -0
  241. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/__init__.py +0 -0
  242. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/__init__.py +0 -0
  243. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/conftest.py +0 -0
  244. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/test_db.py +0 -0
  245. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/test_export.py +0 -0
  246. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/test_security.py +0 -0
  247. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  248. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/commands/test_translate.py +0 -0
  249. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/conftest.py +0 -0
  250. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/test_cli.py +0 -0
  251. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/test_decorators.py +0 -0
  252. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/test_types.py +0 -0
  253. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/cli/test_utils.py +0 -0
  254. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/__init__.py +0 -0
  255. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/conftest.py +0 -0
  256. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/test_file_manager.py +0 -0
  257. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/test_image_manager.py +0 -0
  258. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  259. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  260. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/lang/__init__.py +0 -0
  261. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/lang/test_babel_cli.py +0 -0
  262. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/lang/test_babel_config.py +0 -0
  263. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/lang/test_lazy_text.py +0 -0
  264. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/__init__.py +0 -0
  265. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/sqla/__init__.py +0 -0
  266. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/sqla/conftest.py +0 -0
  267. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/sqla/test_apis.py +0 -0
  268. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/sqla/test_models.py +0 -0
  269. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/security/sqla/test_security_manager.py +0 -0
  270. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_caches.py +0 -0
  271. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_config.py +0 -0
  272. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_const.py +0 -0
  273. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_db.py +0 -0
  274. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_decorators.py +0 -0
  275. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_dependencies.py +0 -0
  276. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_exceptions.py +0 -0
  277. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  278. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_filters.py +0 -0
  279. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_globals.py +0 -0
  280. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_manager.py +0 -0
  281. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_middlewares.py +0 -0
  282. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_mixins.py +0 -0
  283. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_models.py +0 -0
  284. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_registry.py +0 -0
  285. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_routers.py +0 -0
  286. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_schemas.py +0 -0
  287. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_setting.py +0 -0
  288. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_types.py +0 -0
  289. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/test_version.py +0 -0
  290. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/__init__.py +0 -0
  291. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_async_task_runner.py +0 -0
  292. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_class_factory.py +0 -0
  293. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_csv_json_converter.py +0 -0
  294. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_deep_merge.py +0 -0
  295. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_extender_mixin.py +0 -0
  296. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  297. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_formatter.py +0 -0
  298. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_hooks.py +0 -0
  299. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_lazy.py +0 -0
  300. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_merge_schema.py +0 -0
  301. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  302. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_pydantic.py +0 -0
  303. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_run_utils.py +0 -0
  304. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_self_dependencies.py +0 -0
  305. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  306. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_sqla.py +0 -0
  307. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_timezone.py +0 -0
  308. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_update_signature.py +0 -0
  309. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/tests/unit/utils/test_use_default_when_none.py +0 -0
  310. {fastapi_rtk-2.12.0 → fastapi_rtk-2.12.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.12.0
3
+ Version: 2.12.2
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
@@ -161,6 +161,7 @@ __all__ = [
161
161
  "MissingGreenletError",
162
162
  "BasicModel",
163
163
  "AbstractSession",
164
+ "AbstractUserSerializer",
164
165
  # .caches
165
166
  "Cache",
166
167
  "JSONCacheSerializer",
@@ -246,8 +247,8 @@ __all__ = [
246
247
  "UsersApi",
247
248
  "RolesApi",
248
249
  # .security.sqla.user_cache
249
- "USER_CACHE_KEY_PREFIX",
250
- "CachedUser",
250
+ "UserSerializer",
251
+ "UserCache",
251
252
  "CachedUserDatabase",
252
253
  "get_cached_user",
253
254
  "set_cached_user",
@@ -0,0 +1 @@
1
+ __version__ = "2.12.2"
@@ -5,6 +5,7 @@ from .filter import *
5
5
  from .interface import *
6
6
  from .model import *
7
7
  from .session import *
8
+ from .user_cache import *
8
9
 
9
10
  __all__ = [
10
11
  "AbstractCacheSerializer",
@@ -23,4 +24,5 @@ __all__ = [
23
24
  "MissingGreenletError",
24
25
  "BasicModel",
25
26
  "AbstractSession",
27
+ "AbstractUserSerializer",
26
28
  ]
@@ -0,0 +1,41 @@
1
+ import abc
2
+ import typing
3
+
4
+ from .cache import AbstractCacheSerializer
5
+
6
+ if typing.TYPE_CHECKING:
7
+ from ..security.sqla.models import User
8
+
9
+ __all__ = ["AbstractUserSerializer"]
10
+
11
+
12
+ class AbstractUserSerializer(AbstractCacheSerializer):
13
+ """
14
+ Converts a user row to and from the form a user cache entry holds.
15
+
16
+ Narrows `AbstractCacheSerializer` to users, since `UserCache` is the only caller and hands it nothing else. Scheduling is unchanged: an `async def` is awaited on the event loop, while a plain `def` is treated as heavy and runs in a thread.
17
+ """
18
+
19
+ @abc.abstractmethod
20
+ async def dumps(self, value: "User") -> typing.Any:
21
+ """
22
+ Serialize a user row.
23
+
24
+ Args:
25
+ value (User): The user handed to `UserCache.set_user`.
26
+
27
+ Returns:
28
+ typing.Any: The value to store in the cache.
29
+ """
30
+
31
+ @abc.abstractmethod
32
+ async def loads(self, value: typing.Any) -> "User":
33
+ """
34
+ Rebuild a detached user from a stored entry.
35
+
36
+ Args:
37
+ value (typing.Any): The value read from the cache.
38
+
39
+ Returns:
40
+ User: The user to hand back to the caller.
41
+ """
@@ -83,6 +83,7 @@ DEFAULT_PROFILER_FOLDER = DEFAULT_STATIC_FOLDER + "/profiler"
83
83
  DEFAULT_LANG_FOLDER = DEFAULT_BASEDIR + "/lang"
84
84
  DEFAULT_LANGUAGES = "en,de"
85
85
  DEFAULT_TRANSLATIONS_KEY = "translations"
86
+ DEFAULT_USER_CACHE_KEY_PREFIX = "fastapi_rtk:user:"
86
87
  DEFAULT_SECWEB_PARAMS = {
87
88
  "Option": {
88
89
  "csp": {
@@ -59,6 +59,7 @@ if typing.TYPE_CHECKING:
59
59
  from .caches import Cache
60
60
  from .fastapi_react_toolkit import FastAPIReactToolkit
61
61
  from .security.sqla.models import User
62
+ from .security.sqla.user_cache import UserCache
62
63
 
63
64
  __all__ = ["g", "current_app", "current_user", "request", "background_tasks"]
64
65
 
@@ -116,6 +117,10 @@ class Globals:
116
117
  """
117
118
  The cache object used by caching features such as `AUTH_USER_CACHE_TTL`. Defaults to `Cache` from `fastapi_rtk.caches.cache`, which keeps entries in an `InMemoryCacheStorage`.
118
119
  """
120
+ user_cache: "UserCache"
121
+ """
122
+ The object that stores and rebuilds authenticated users in `g.cache`. Defaults to the `AUTH_USER_CACHE` config key, or `UserCache` from `fastapi_rtk.security.sqla.user_cache`.
123
+ """
119
124
  current_app: "FastAPIReactToolkit"
120
125
  """
121
126
  The current FastAPI React Toolkit application instance.
@@ -323,6 +328,19 @@ g.set_default(
323
328
  )
324
329
  ),
325
330
  )
331
+ g.set_default(
332
+ "user_cache",
333
+ lazy(
334
+ lambda: (
335
+ lazy_import(
336
+ "fastapi_rtk.setting", lambda mod: mod.Setting.AUTH_USER_CACHE
337
+ )()
338
+ or lazy_import(
339
+ "fastapi_rtk.security.sqla.user_cache", lambda mod: mod.UserCache()
340
+ )()
341
+ )
342
+ ),
343
+ )
326
344
  g.config.add_callback(basic_callback)
327
345
 
328
346
  # Local Proxies
@@ -188,7 +188,7 @@ class PermissionsApi(ModelRestApi):
188
188
 
189
189
 
190
190
  class PermissionViewApi(ModelRestApi):
191
- resource_name = "permissionview"
191
+ resource_name = "permissionviews"
192
192
  datamodel = SQLAInterface(PermissionApi)
193
193
  description = "Allows the view of which roles have which permissions on which APIs."
194
194
  max_page_size = 200
@@ -0,0 +1,238 @@
1
+ import typing
2
+
3
+ import sqlalchemy
4
+ import sqlalchemy.orm
5
+
6
+ from ...bases.user_cache import AbstractUserSerializer
7
+ from ...db import UserDatabase
8
+ from ...globals import g
9
+ from ...setting import Setting
10
+ from ...utils import lazy, smart_run, use_default_when_none
11
+ from .models import Role, User
12
+
13
+ __all__ = [
14
+ "UserSerializer",
15
+ "UserCache",
16
+ "CachedUserDatabase",
17
+ "get_cached_user",
18
+ "set_cached_user",
19
+ "invalidate_user_cache",
20
+ "clear_user_cache",
21
+ ]
22
+
23
+
24
+ class UserSerializer(AbstractUserSerializer):
25
+ """
26
+ Convert a user row to and from the form a user cache entry holds.
27
+
28
+ Set an instance of a subclass as the `AUTH_USER_SERIALIZER` config key to change what an entry stores and what a cached user is rebuilt from. It only ever sees users, so it belongs to `UserCache` and not to the `CACHE` it writes through.
29
+ """
30
+
31
+ async def dumps(self, value: User) -> dict[str, typing.Any]:
32
+ """
33
+ Serialize a user row into its mapped columns and the (id, name) pair of each role.
34
+
35
+ Args:
36
+ value (User): The user to serialize, with its roles loaded.
37
+
38
+ Returns:
39
+ dict[str, typing.Any]: The serialized user, under the keys `columns` and `roles`.
40
+ """
41
+ mapper = sqlalchemy.inspect(type(value)).mapper
42
+ return {
43
+ "columns": {
44
+ attr.key: getattr(value, attr.key) for attr in mapper.column_attrs
45
+ },
46
+ "roles": [(role.id, role.name) for role in value.roles],
47
+ }
48
+
49
+ async def loads(self, value: dict[str, typing.Any]) -> User:
50
+ """
51
+ Rebuild a detached user from its serialized form.
52
+
53
+ `make_transient_to_detached` gives the instance the identity of the stored row, so a later `Session.add` updates that row instead of inserting a new one, and `set_committed_value` attaches the roles without marking the collection dirty.
54
+
55
+ Args:
56
+ value (dict[str, typing.Any]): The serialized user, under the keys `columns` and `roles`.
57
+
58
+ Returns:
59
+ User: The rebuilt user, detached from any session.
60
+ """
61
+ user = User(**value["columns"])
62
+ sqlalchemy.orm.make_transient_to_detached(user)
63
+
64
+ roles = []
65
+ for role_id, role_name in value["roles"]:
66
+ role = Role(id=role_id, name=role_name)
67
+ sqlalchemy.orm.make_transient_to_detached(role)
68
+ roles.append(role)
69
+ sqlalchemy.orm.attributes.set_committed_value(user, "roles", roles)
70
+
71
+ return user
72
+
73
+
74
+ class UserCache:
75
+ """
76
+ Read and write authenticated users in `g.cache`.
77
+
78
+ Keys, TTL and storage access live here; what an entry holds lives in the serializer. Set an instance of a subclass as the `AUTH_USER_CACHE` config key to change the read and write paths.
79
+
80
+ Attributes:
81
+ serializer (AbstractUserSerializer): The `AUTH_USER_SERIALIZER` config key, or a `UserSerializer`, read on first use.
82
+ """
83
+
84
+ serializer: AbstractUserSerializer = lazy(
85
+ lambda self: use_default_when_none(
86
+ Setting.AUTH_USER_SERIALIZER, UserSerializer()
87
+ )
88
+ )
89
+
90
+ @property
91
+ def key_prefix(self) -> str:
92
+ """
93
+ Prefix of every key this cache writes, and the prefix `clear` drops.
94
+ """
95
+ return Setting.AUTH_USER_CACHE_KEY_PREFIX
96
+
97
+ @property
98
+ def ttl(self) -> int:
99
+ """
100
+ Seconds an entry stays cached, `0` or less disabling the cache.
101
+ """
102
+ return Setting.AUTH_USER_CACHE_TTL
103
+
104
+ async def get_user(self, user_id: typing.Any) -> User | None:
105
+ """
106
+ Rebuild the cached user with the given id.
107
+
108
+ The returned user is detached and behaves as if freshly loaded, so it can be handed to `Session.add` without inserting a second row.
109
+
110
+ Args:
111
+ user_id (typing.Any): The primary key of the user.
112
+
113
+ Returns:
114
+ User | None: The cached user, or `None` when caching is off or the entry is absent.
115
+ """
116
+ if self.ttl <= 0:
117
+ return None
118
+
119
+ cached = await g.cache.get(self.cache_key(user_id))
120
+ if cached is None:
121
+ return None
122
+
123
+ return await smart_run(self.serializer.loads, cached)
124
+
125
+ async def set_user(self, user: User) -> None:
126
+ """
127
+ Store the given user for `ttl` seconds.
128
+
129
+ Args:
130
+ user (User): The user to store, with its roles loaded.
131
+ """
132
+ ttl = self.ttl
133
+ if ttl <= 0:
134
+ return
135
+
136
+ value = await smart_run(self.serializer.dumps, user)
137
+ await g.cache.set(self.cache_key(user.id), value, ttl)
138
+
139
+ async def invalidate(self, user_id: typing.Any) -> None:
140
+ """
141
+ Drop the cached user with the given id.
142
+
143
+ Args:
144
+ user_id (typing.Any): The primary key of the user.
145
+ """
146
+ if self.ttl <= 0:
147
+ return
148
+
149
+ await g.cache.delete(self.cache_key(user_id))
150
+
151
+ async def clear(self) -> None:
152
+ """
153
+ Drop every cached user, leaving other cache entries alone.
154
+ """
155
+ if self.ttl <= 0:
156
+ return
157
+
158
+ await g.cache.clear(self.key_prefix)
159
+
160
+ def cache_key(self, user_id: typing.Any) -> str:
161
+ """
162
+ Build the cache key holding the user with the given id.
163
+
164
+ Args:
165
+ user_id (typing.Any): The primary key of the user.
166
+
167
+ Returns:
168
+ str: The key to read or write in `g.cache`.
169
+ """
170
+ return f"{self.key_prefix}{user_id}"
171
+
172
+
173
+ class CachedUserDatabase(UserDatabase):
174
+ """
175
+ User database that answers `get` from the user cache before querying.
176
+
177
+ The session it is built from stays untouched on a cache hit, so the request never checks out a database connection for the lookup.
178
+ """
179
+
180
+ async def get(self, id: typing.Any):
181
+ """
182
+ Read the user with the given id, from the cache when it holds one.
183
+
184
+ Args:
185
+ id (typing.Any): The primary key of the user.
186
+
187
+ Returns:
188
+ User | None: The user, or `None` when no row matches.
189
+ """
190
+ cached = await get_cached_user(id)
191
+ if cached is not None:
192
+ return cached
193
+
194
+ user = await super().get(id)
195
+ if user is not None:
196
+ await set_cached_user(user)
197
+
198
+ return user
199
+
200
+
201
+ async def get_cached_user(user_id: typing.Any) -> User | None:
202
+ """
203
+ Rebuild the cached user with the given id, through `g.user_cache`.
204
+
205
+ Args:
206
+ user_id (typing.Any): The primary key of the user.
207
+
208
+ Returns:
209
+ User | None: The cached user, or `None` when caching is off or the entry is absent.
210
+ """
211
+ return await g.user_cache.get_user(user_id)
212
+
213
+
214
+ async def set_cached_user(user: User) -> None:
215
+ """
216
+ Store the given user through `g.user_cache`.
217
+
218
+ Args:
219
+ user (User): The user to store, with its roles loaded.
220
+ """
221
+ await g.user_cache.set_user(user)
222
+
223
+
224
+ async def invalidate_user_cache(user_id: typing.Any) -> None:
225
+ """
226
+ Drop the cached user with the given id, through `g.user_cache`.
227
+
228
+ Args:
229
+ user_id (typing.Any): The primary key of the user.
230
+ """
231
+ await g.user_cache.invalidate(user_id)
232
+
233
+
234
+ async def clear_user_cache() -> None:
235
+ """
236
+ Drop every cached user, leaving other cache entries alone.
237
+ """
238
+ await g.user_cache.clear()
@@ -12,6 +12,7 @@ from .const import (
12
12
  DEFAULT_STATIC_FOLDER,
13
13
  DEFAULT_TEMPLATE_FOLDER,
14
14
  DEFAULT_TRANSLATIONS_KEY,
15
+ DEFAULT_USER_CACHE_KEY_PREFIX,
15
16
  AuthType,
16
17
  logger,
17
18
  )
@@ -20,7 +21,9 @@ from .globals import g
20
21
 
21
22
  if typing.TYPE_CHECKING:
22
23
  from .bases.file_manager import AbstractFileManager, AbstractImageManager
24
+ from .bases.user_cache import AbstractUserSerializer
23
25
  from .caches import Cache
26
+ from .security.sqla.user_cache import UserCache
24
27
 
25
28
 
26
29
  __all__ = ["Setting"]
@@ -299,6 +302,27 @@ class _Setting:
299
302
  """
300
303
  return g.config.get("AUTH_USER_CACHE_TTL", 0)
301
304
 
305
+ @property
306
+ def AUTH_USER_CACHE_KEY_PREFIX(self) -> str:
307
+ """
308
+ Prefix of every key the user cache writes, and the prefix it drops when clearing.
309
+ """
310
+ return g.config.get("AUTH_USER_CACHE_KEY_PREFIX", DEFAULT_USER_CACHE_KEY_PREFIX)
311
+
312
+ @property
313
+ def AUTH_USER_CACHE(self) -> "UserCache | None":
314
+ """
315
+ The user cache honoring `AUTH_USER_CACHE_TTL`. Also readable as `g.user_cache`.
316
+ """
317
+ return g.config.get("AUTH_USER_CACHE")
318
+
319
+ @property
320
+ def AUTH_USER_SERIALIZER(self) -> "AbstractUserSerializer | None":
321
+ """
322
+ The serializer deciding what a cached user entry holds, applied by `g.user_cache` before the value reaches `CACHE`.
323
+ """
324
+ return g.config.get("AUTH_USER_SERIALIZER")
325
+
302
326
  @property
303
327
  def AUTH_ROLE_ADMIN(self) -> str | None:
304
328
  return g.config.get("AUTH_ROLE_ADMIN", DEFAULT_ADMIN_ROLE)
@@ -4,14 +4,15 @@ import unittest.mock
4
4
  import pytest
5
5
  import sqlalchemy
6
6
 
7
+ from fastapi_rtk.bases.user_cache import AbstractUserSerializer
7
8
  from fastapi_rtk.caches import Cache, InMemoryCacheStorage, PickleCacheSerializer
9
+ from fastapi_rtk.const import DEFAULT_USER_CACHE_KEY_PREFIX
8
10
  from fastapi_rtk.db import UserDatabase
9
11
  from fastapi_rtk.security.sqla.models import Role, User
10
12
  from fastapi_rtk.security.sqla.user_cache import (
11
- USER_CACHE_KEY_PREFIX,
12
13
  CachedUserDatabase,
13
- _dump_user,
14
- _rebuild_user,
14
+ UserCache,
15
+ UserSerializer,
15
16
  clear_user_cache,
16
17
  get_cached_user,
17
18
  invalidate_user_cache,
@@ -19,6 +20,25 @@ from fastapi_rtk.security.sqla.user_cache import (
19
20
  )
20
21
 
21
22
 
23
+ class _PrefixedUserCache(UserCache):
24
+ key_prefix = "custom:user:"
25
+
26
+
27
+ class _PasswordlessUserSerializer(UserSerializer):
28
+ async def dumps(self, user):
29
+ dumped = await super().dumps(user)
30
+ dumped["columns"].pop("password", None)
31
+ return dumped
32
+
33
+
34
+ async def _dump_user(user):
35
+ return await UserSerializer().dumps(user)
36
+
37
+
38
+ async def _rebuild_user(cached):
39
+ return await UserSerializer().loads(cached)
40
+
41
+
22
42
  def _make_user(user_id=7, roles=(("3", "Admin"),)):
23
43
  user = User(
24
44
  id=user_id,
@@ -39,35 +59,115 @@ def cache(global_setter):
39
59
  return cache
40
60
 
41
61
 
62
+ @pytest.mark.asyncio
42
63
  class TestDumpAndRebuild:
43
- def test_dump_keeps_columns_and_roles(self):
44
- dumped = _dump_user(_make_user())
64
+ async def test_dump_keeps_columns_and_roles(self):
65
+ dumped = await _dump_user(_make_user())
45
66
  assert dumped["columns"]["id"] == 7
46
67
  assert dumped["columns"]["username"] == "user7"
47
68
  assert dumped["roles"] == [(3, "Admin")]
48
69
 
49
- def test_rebuilt_user_is_detached_with_identity(self):
50
- rebuilt = _rebuild_user(_dump_user(_make_user()))
70
+ async def test_rebuilt_user_is_detached_with_identity(self):
71
+ rebuilt = await _rebuild_user(await _dump_user(_make_user()))
51
72
  state = sqlalchemy.inspect(rebuilt)
52
73
  assert state.detached is True
53
74
  assert state.key == (User, (7,), None)
54
75
 
55
- def test_rebuilt_roles_are_loaded_without_pending_changes(self):
56
- rebuilt = _rebuild_user(_dump_user(_make_user()))
76
+ async def test_rebuilt_roles_are_loaded_without_pending_changes(self):
77
+ rebuilt = await _rebuild_user(await _dump_user(_make_user()))
57
78
  assert [(role.id, role.name) for role in rebuilt.roles] == [(3, "Admin")]
58
79
  assert sqlalchemy.inspect(rebuilt).attrs.roles.history.has_changes() is False
59
80
 
60
- def test_rebuilt_columns_keep_their_types(self):
61
- rebuilt = _rebuild_user(_dump_user(_make_user()))
81
+ async def test_rebuilt_columns_keep_their_types(self):
82
+ rebuilt = await _rebuild_user(await _dump_user(_make_user()))
62
83
  assert rebuilt.last_login == datetime.datetime(2026, 1, 2, 3, 4)
63
84
  assert rebuilt.active is True
64
85
 
65
86
 
87
+ class TestSettings:
88
+ def test_default_key_prefix(self):
89
+ assert UserCache().key_prefix == DEFAULT_USER_CACHE_KEY_PREFIX
90
+
91
+ def test_key_prefix_reads_the_config(self, config_setter):
92
+ config_setter("AUTH_USER_CACHE_KEY_PREFIX", "myapp:user:")
93
+ assert UserCache().cache_key(7) == "myapp:user:7"
94
+
95
+ def test_ttl_reads_the_config(self, config_setter):
96
+ config_setter("AUTH_USER_CACHE_TTL", 42)
97
+ assert UserCache().ttl == 42
98
+
99
+ def test_serializer_defaults_to_user_serializer(self):
100
+ assert type(UserCache().serializer) is UserSerializer
101
+
102
+ def test_configured_serializer_is_used_as_is(self, config_setter):
103
+ configured = _PasswordlessUserSerializer()
104
+ config_setter("AUTH_USER_SERIALIZER", configured)
105
+ assert UserCache().serializer is configured
106
+
107
+ def test_serializer_implements_the_abstract_contract(self):
108
+ assert isinstance(UserSerializer(), AbstractUserSerializer)
109
+
110
+
111
+ @pytest.mark.asyncio
112
+ class TestUserCacheOverride:
113
+ async def test_module_functions_use_the_configured_cache(
114
+ self, cache, config_setter, global_setter
115
+ ):
116
+ config_setter("AUTH_USER_CACHE_TTL", 30)
117
+ global_setter("user_cache", _PrefixedUserCache())
118
+
119
+ await set_cached_user(_make_user())
120
+
121
+ assert "custom:user:7" in cache.storage._entries
122
+ assert (await get_cached_user(7)).username == "user7"
123
+
124
+ async def test_configured_serializer_decides_what_is_stored(
125
+ self, cache, config_setter, global_setter
126
+ ):
127
+ config_setter("AUTH_USER_CACHE_TTL", 30)
128
+ config_setter("AUTH_USER_SERIALIZER", _PasswordlessUserSerializer())
129
+ global_setter("user_cache", UserCache())
130
+
131
+ await set_cached_user(_make_user())
132
+
133
+ stored = await cache.get(f"{DEFAULT_USER_CACHE_KEY_PREFIX}7")
134
+ assert "password" not in stored["columns"]
135
+ rebuilt = await get_cached_user(7)
136
+ assert "password" not in sqlalchemy.inspect(rebuilt).dict
137
+ assert rebuilt.username == "user7"
138
+
139
+ async def test_clear_only_drops_the_configured_prefix(
140
+ self, cache, config_setter, global_setter
141
+ ):
142
+ config_setter("AUTH_USER_CACHE_TTL", 30)
143
+ global_setter("user_cache", _PrefixedUserCache())
144
+ await set_cached_user(_make_user())
145
+ await cache.set(f"{DEFAULT_USER_CACHE_KEY_PREFIX}9", "keep", 60)
146
+
147
+ await clear_user_cache()
148
+
149
+ assert await get_cached_user(7) is None
150
+ assert await cache.get(f"{DEFAULT_USER_CACHE_KEY_PREFIX}9") == "keep"
151
+
152
+ async def test_invalidate_uses_the_configured_key(
153
+ self, cache, config_setter, global_setter
154
+ ):
155
+ config_setter("AUTH_USER_CACHE_TTL", 30)
156
+ global_setter("user_cache", _PrefixedUserCache())
157
+ await set_cached_user(_make_user())
158
+
159
+ await invalidate_user_cache(7)
160
+
161
+ assert cache.storage._entries == {}
162
+
163
+
66
164
  @pytest.mark.asyncio
67
165
  class TestUserCacheDisabled:
68
166
  async def test_get_returns_none(self, cache, config_setter):
69
167
  config_setter("AUTH_USER_CACHE_TTL", 0)
70
- await cache.set(f"{USER_CACHE_KEY_PREFIX}7", _dump_user(_make_user()), 60)
168
+ await cache.set(
169
+ f"{DEFAULT_USER_CACHE_KEY_PREFIX}7", await _dump_user(_make_user()), 60
170
+ )
71
171
  assert await get_cached_user(7) is None
72
172
 
73
173
  async def test_set_stores_nothing(self, cache, config_setter):
@@ -77,19 +177,19 @@ class TestUserCacheDisabled:
77
177
 
78
178
  async def test_invalidate_leaves_the_cache_alone(self, cache, config_setter):
79
179
  config_setter("AUTH_USER_CACHE_TTL", 0)
80
- await cache.set(f"{USER_CACHE_KEY_PREFIX}7", "raw", 60)
180
+ await cache.set(f"{DEFAULT_USER_CACHE_KEY_PREFIX}7", "raw", 60)
81
181
 
82
182
  await invalidate_user_cache(7)
83
183
 
84
- assert await cache.get(f"{USER_CACHE_KEY_PREFIX}7") == "raw"
184
+ assert await cache.get(f"{DEFAULT_USER_CACHE_KEY_PREFIX}7") == "raw"
85
185
 
86
186
  async def test_clear_leaves_the_cache_alone(self, cache, config_setter):
87
187
  config_setter("AUTH_USER_CACHE_TTL", 0)
88
- await cache.set(f"{USER_CACHE_KEY_PREFIX}7", "raw", 60)
188
+ await cache.set(f"{DEFAULT_USER_CACHE_KEY_PREFIX}7", "raw", 60)
89
189
 
90
190
  await clear_user_cache()
91
191
 
92
- assert await cache.get(f"{USER_CACHE_KEY_PREFIX}7") == "raw"
192
+ assert await cache.get(f"{DEFAULT_USER_CACHE_KEY_PREFIX}7") == "raw"
93
193
 
94
194
 
95
195
  @pytest.mark.asyncio
@@ -109,7 +209,7 @@ class TestUserCacheEnabled:
109
209
  async def test_entry_uses_prefixed_key(self, cache, config_setter):
110
210
  config_setter("AUTH_USER_CACHE_TTL", 30)
111
211
  await set_cached_user(_make_user())
112
- assert f"{USER_CACHE_KEY_PREFIX}7" in cache.storage._entries
212
+ assert f"{DEFAULT_USER_CACHE_KEY_PREFIX}7" in cache.storage._entries
113
213
 
114
214
  async def test_invalidate_drops_single_user(self, cache, config_setter):
115
215
  config_setter("AUTH_USER_CACHE_TTL", 30)
@@ -1 +0,0 @@
1
- __version__ = "2.12.0"