fastapi-rtk 1.0.8__tar.gz → 1.0.10__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 (135) hide show
  1. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/PKG-INFO +1 -1
  2. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/__init__.py +0 -1
  3. fastapi_rtk-1.0.10/fastapi_rtk/_version.py +1 -0
  4. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/auth.py +0 -9
  5. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/file_manager.py +12 -0
  6. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/security.py +6 -6
  7. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/db.py +1 -0
  8. fastapi_rtk-1.0.10/fastapi_rtk/dependencies.py +256 -0
  9. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/fastapi_react_toolkit.py +109 -161
  10. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/file_managers/s3_file_manager.py +63 -32
  11. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/security/sqla/apis.py +20 -5
  12. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/security/sqla/models.py +8 -23
  13. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/security/sqla/security_manager.py +367 -10
  14. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/hooks.py +7 -4
  15. fastapi_rtk-1.0.8/fastapi_rtk/_version.py +0 -1
  16. fastapi_rtk-1.0.8/fastapi_rtk/dependencies.py +0 -210
  17. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/.gitignore +0 -0
  18. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/LICENSE +0 -0
  19. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/README.md +0 -0
  20. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/api/__init__.py +0 -0
  21. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/api/base_api.py +0 -0
  22. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/api/model_rest_api.py +0 -0
  23. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/apis.py +0 -0
  24. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/__init__.py +0 -0
  25. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  26. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  27. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  28. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/hashers/utils.py +0 -0
  29. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  30. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  31. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  32. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/strategies/config.py +0 -0
  33. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/strategies/db.py +0 -0
  34. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  35. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/__init__.py +0 -0
  36. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/__init__.py +0 -0
  37. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/column.py +0 -0
  38. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/db.py +0 -0
  39. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  40. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/filters.py +0 -0
  41. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/interface.py +0 -0
  42. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/model.py +0 -0
  43. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/generic/session.py +0 -0
  44. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  45. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/column.py +0 -0
  46. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/db.py +0 -0
  47. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  48. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  49. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  50. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  51. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  52. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  53. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  54. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  55. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/filters.py +0 -0
  56. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/interface.py +0 -0
  57. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/model.py +0 -0
  58. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/backends/sqla/session.py +0 -0
  59. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/__init__.py +0 -0
  60. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/db.py +0 -0
  61. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/filter.py +0 -0
  62. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/interface.py +0 -0
  63. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/model.py +0 -0
  64. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/bases/session.py +0 -0
  65. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/__init__.py +0 -0
  66. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/cli.py +0 -0
  67. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/__init__.py +0 -0
  68. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  69. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  70. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  71. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  72. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  73. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  74. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  75. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  76. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  77. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/export.py +0 -0
  78. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/commands/translate.py +0 -0
  79. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/const.py +0 -0
  80. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/decorators.py +0 -0
  81. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/types.py +0 -0
  82. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/cli/utils.py +0 -0
  83. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/config.py +0 -0
  84. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/const.py +0 -0
  85. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/decorators.py +0 -0
  86. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/exceptions.py +0 -0
  87. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/file_managers/__init__.py +0 -0
  88. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/file_managers/file_manager.py +0 -0
  89. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/file_managers/image_manager.py +0 -0
  90. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  91. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/filters.py +0 -0
  92. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/globals.py +0 -0
  93. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/__init__.py +0 -0
  94. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/babel/__init__.py +0 -0
  95. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/babel/cli.py +0 -0
  96. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/babel/config.py +0 -0
  97. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/babel.cfg +0 -0
  98. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/lazy_text.py +0 -0
  99. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/messages.pot +0 -0
  100. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  101. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  102. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  103. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  104. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/manager.py +0 -0
  105. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/middlewares.py +0 -0
  106. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/mixins.py +0 -0
  107. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/models.py +0 -0
  108. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/routers.py +0 -0
  109. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/schemas.py +0 -0
  110. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/security/__init__.py +0 -0
  111. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/security/sqla/__init__.py +0 -0
  112. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/setting.py +0 -0
  113. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/types.py +0 -0
  114. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/__init__.py +0 -0
  115. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/async_task_runner.py +0 -0
  116. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/class_factory.py +0 -0
  117. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  118. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/deep_merge.py +0 -0
  119. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/extender_mixin.py +0 -0
  120. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  121. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/lazy.py +0 -0
  122. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/merge_schema.py +0 -0
  123. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  124. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/prettify_dict.py +0 -0
  125. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/pydantic.py +0 -0
  126. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/run_utils.py +0 -0
  127. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/self_dependencies.py +0 -0
  128. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  129. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/sqla.py +0 -0
  130. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/timezone.py +0 -0
  131. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/update_signature.py +0 -0
  132. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  133. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/utils/werkzeug.py +0 -0
  134. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/fastapi_rtk/version.py +0 -0
  135. {fastapi_rtk-1.0.8 → fastapi_rtk-1.0.10}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-rtk
3
- Version: 1.0.8
3
+ Version: 1.0.10
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
@@ -156,7 +156,6 @@ __all__ = [
156
156
  "docs",
157
157
  # .dependencies
158
158
  "set_global_user",
159
- "permissions",
160
159
  "current_permissions",
161
160
  "has_access_dependency",
162
161
  # .exceptions
@@ -0,0 +1 @@
1
+ __version__ = "1.0.10"
@@ -142,15 +142,6 @@ class Authenticator(BaseAuthenticator):
142
142
  except HTTPException as e:
143
143
  if not default_to_none:
144
144
  raise e
145
-
146
- # Retrieve list of apis, that user has access to
147
- if user:
148
- user.permissions = []
149
- for role in user.roles:
150
- for permission_api in role.permissions:
151
- user.permissions.append(permission_api.api.name)
152
- user.permissions = list(set(user.permissions))
153
-
154
145
  return user, token
155
146
 
156
147
 
@@ -35,6 +35,18 @@ class AbstractFileManager(abc.ABC):
35
35
  namegen: typing.Callable[[str], str] | None = None,
36
36
  permission: int | None = None,
37
37
  ):
38
+ """
39
+ Initializes the AbstractFileManager.
40
+
41
+ Args:
42
+ base_path (str | None, optional): Base path for file storage. Defaults to None.
43
+ allowed_extensions (list[str] | None, optional): Allowed file extensions. Defaults to None.
44
+ namegen (typing.Callable[[str], str] | None, optional): Callable for generating file names. Defaults to None.
45
+ permission (int | None, optional): File permission settings. Defaults to None.
46
+
47
+ Raises:
48
+ ValueError: If `base_path` is not set.
49
+ """
38
50
  if base_path is not None:
39
51
  self.base_path = base_path
40
52
  if allowed_extensions is not None:
@@ -238,7 +238,7 @@ async def _check_roles(name: str, create: bool = False):
238
238
  if not role:
239
239
  if not create:
240
240
  raise Exception(f"Role {name} does not exist")
241
- await g.current_app.security.create_role(name=name, session=session)
241
+ await g.current_app.sm.create_role(name=name, session=session)
242
242
 
243
243
 
244
244
  async def _create_user(
@@ -255,7 +255,7 @@ async def _create_user(
255
255
  """
256
256
  if role:
257
257
  await _check_roles(role, create=create_role)
258
- return await g.current_app.security.create_user(
258
+ return await g.current_app.sm.create_user(
259
259
  email=email,
260
260
  username=username,
261
261
  password=password,
@@ -269,8 +269,8 @@ async def _reset_password(email_or_username: str, password: str):
269
269
  """
270
270
  Reset user password.
271
271
  """
272
- user = await g.current_app.security.get_user(email_or_username)
273
- return await g.current_app.security.reset_password(user, password)
272
+ user = await g.current_app.sm.get_user(email_or_username)
273
+ return await g.current_app.sm.reset_password(user, password)
274
274
 
275
275
 
276
276
  async def export_data(
@@ -281,7 +281,7 @@ async def export_data(
281
281
  """
282
282
  Export data.
283
283
  """
284
- data = await g.current_app.security.export_data(data, type)
284
+ data = await g.current_app.sm.export_data(data, type)
285
285
  with open(file_path, "w") as f:
286
286
  f.write(data)
287
287
 
@@ -290,4 +290,4 @@ async def _cleanup():
290
290
  """
291
291
  Cleanup unused permissions from apis and roles.
292
292
  """
293
- await g.current_app.security.cleanup()
293
+ await g.current_app.sm.cleanup()
@@ -121,6 +121,7 @@ class UserDatabase(SQLAlchemyUserDatabase[UP, ID]):
121
121
  raise NotImplementedError()
122
122
 
123
123
  await safe_call(self.session.refresh(user))
124
+ await user.load("oauth_accounts")
124
125
  oauth_account = self.oauth_account_table(**create_dict)
125
126
  self.session.add(oauth_account)
126
127
  user.oauth_accounts.append(oauth_account)
@@ -0,0 +1,256 @@
1
+ import fastapi
2
+ import sqlalchemy
3
+ from fastapi import Depends, HTTPException
4
+
5
+ from .api.base_api import BaseApi
6
+ from .const import PERMISSION_PREFIX, ErrorCode, logger
7
+ from .db import db
8
+ from .globals import g
9
+ from .security.sqla.models import Api, Permission, PermissionApi, Role, User
10
+ from .utils import smart_run
11
+
12
+ __all__ = [
13
+ "set_global_user",
14
+ "current_permissions",
15
+ "has_access_dependency",
16
+ ]
17
+
18
+
19
+ def set_global_user():
20
+ """
21
+ A dependency for FastAPI that will set the current user to the global variable `g.user`.
22
+
23
+ Usage:
24
+ ```python
25
+ async def get_info(
26
+ *,
27
+ session: AsyncSession | Session = Depends(get_async_session),
28
+ none: None = Depends(set_global_user()),
29
+ ):
30
+ ...more code
31
+ """
32
+
33
+ async def set_global_user_dependency(
34
+ user: User | None = Depends(
35
+ g.auth.fastapi_users.current_user(active=True, default_to_none=True)
36
+ ),
37
+ ):
38
+ g.user = user
39
+
40
+ return set_global_user_dependency
41
+
42
+
43
+ def check_g_user():
44
+ """
45
+ A dependency for FastAPI that will check if the current user is set to the global variable `g.user`.
46
+
47
+ Usage:
48
+ ```python
49
+ async def get_info(
50
+ *,
51
+ session: AsyncSession | Session = Depends(get_async_session),
52
+ none: None = Depends(check_g_user()),
53
+ ):
54
+ ...more code
55
+ """
56
+
57
+ async def check_g_user_dependency():
58
+ if not g.user:
59
+ raise HTTPException(
60
+ fastapi.status.HTTP_401_UNAUTHORIZED,
61
+ ErrorCode.GET_USER_MISSING_TOKEN_OR_INACTIVE_USER,
62
+ )
63
+
64
+ return check_g_user_dependency
65
+
66
+
67
+ def current_permissions(api: BaseApi):
68
+ """
69
+ A dependency for FastAPI that will return all permissions of the current user for the specified API.
70
+
71
+ Because it will implicitly check whether the user is authenticated, it can return `401 Unauthorized` or `403 Forbidden`.
72
+
73
+ Args:
74
+ api (BaseApi): The API to be checked.
75
+
76
+ Usage:
77
+ ```python
78
+ async def get_info(
79
+ *,
80
+ permissions: List[str] = Depends(current_permissions(self)),
81
+ session: AsyncSession | Session = Depends(get_async_session),
82
+ ):
83
+ ...more code
84
+ ```
85
+ """
86
+
87
+ async def current_permissions_depedency(_=Depends(_ensure_roles)):
88
+ sm = g.current_app.sm
89
+ api_name = api.__class__.__name__
90
+ permissions = set[str]()
91
+ db_role_ids = list[int]()
92
+
93
+ # Retrieve permissions from built-in roles
94
+ for role in g.user.roles:
95
+ if role.name not in sm.builtin_roles:
96
+ db_role_ids.append(role.id)
97
+ continue
98
+
99
+ api_permission_tuples = sm.get_api_permission_tuples_from_builtin_roles(
100
+ role.name
101
+ )
102
+ for multi_apis_str, multi_perms_str in api_permission_tuples:
103
+ api_names = multi_apis_str.split("|")
104
+ perm_names = multi_perms_str.split("|")
105
+ if api_name in api_names:
106
+ permissions.update(perm_names)
107
+
108
+ if db_role_ids:
109
+ query = (
110
+ sqlalchemy.select(Permission)
111
+ .join(PermissionApi)
112
+ .join(PermissionApi.roles)
113
+ .join(Api)
114
+ .where(Api.name == api_name, Role.id.in_(db_role_ids))
115
+ )
116
+ if api.base_permissions:
117
+ query = query.where(Permission.name.in_(api.base_permissions))
118
+
119
+ permissions_in_db = await smart_run(db.current_session.scalars, query)
120
+ permissions.update(perm.name for perm in permissions_in_db.all())
121
+
122
+ if api.base_permissions:
123
+ permissions = permissions.intersection(set(api.base_permissions))
124
+
125
+ return list(permissions)
126
+
127
+ return current_permissions_depedency
128
+
129
+
130
+ def has_access_dependency(
131
+ api: BaseApi,
132
+ permission: str,
133
+ ):
134
+ """
135
+ A dependency for FastAPI to check whether current user has access to the specified API and permission.
136
+
137
+ Because it will implicitly check whether the user is authenticated, it can return `401 Unauthorized` or `403 Forbidden`.
138
+
139
+ Usage:
140
+ ```python
141
+ @self.router.get(
142
+ "/_info",
143
+ response_model=self.info_return_schema,
144
+ dependencies=[Depends(has_access(self, "info"))],
145
+ )
146
+ ...more code
147
+ ```
148
+
149
+ Args:
150
+ api (BaseApi): The API to be checked.
151
+ permission (str): The permission to check.
152
+ """
153
+ permission = f"{PERMISSION_PREFIX}{permission}"
154
+
155
+ async def check_permission():
156
+ _ensure_roles(ErrorCode.PERMISSION_DENIED)
157
+ sm = g.current_app.sm
158
+
159
+ # First, check built-in roles (avoiding unnecessary DB queries)
160
+ # This also covers the case for API and permission name with pipes
161
+ if any(
162
+ sm.has_access_in_builtin_roles(
163
+ role.name, api.__class__.__name__, permission
164
+ )
165
+ for role in g.user.roles
166
+ ):
167
+ logger.debug(
168
+ f"User {g.user} has access to {api.__class__.__name__} with permission {permission} via built-in roles."
169
+ )
170
+ return
171
+
172
+ db_role_ids = [
173
+ role.id for role in g.user.roles if role.name not in sm.builtin_roles
174
+ ]
175
+ if not db_role_ids:
176
+ raise HTTPException(
177
+ fastapi.status.HTTP_403_FORBIDDEN, ErrorCode.PERMISSION_DENIED
178
+ )
179
+
180
+ api_name = api.__class__.__name__
181
+ stmt = (
182
+ sqlalchemy.select(sqlalchemy.literal(True))
183
+ .select_from(Permission)
184
+ .join(PermissionApi)
185
+ .join(PermissionApi.roles)
186
+ .join(Api)
187
+ .where(
188
+ Api.name == api_name,
189
+ Permission.name == permission,
190
+ Role.id.in_(db_role_ids),
191
+ )
192
+ .limit(1)
193
+ )
194
+ result = await smart_run(db.current_session.scalar, stmt)
195
+ result = bool(result)
196
+ if result:
197
+ return
198
+
199
+ raise HTTPException(
200
+ fastapi.status.HTTP_403_FORBIDDEN, ErrorCode.PERMISSION_DENIED
201
+ )
202
+
203
+ return check_permission
204
+
205
+
206
+ async def set_global_background_tasks(background_tasks: fastapi.BackgroundTasks):
207
+ """
208
+ A dependency for FastAPI that will set the `background_tasks` to the global variable `g.background_tasks`.
209
+
210
+ Usage:
211
+ ```python
212
+ async def get_info(
213
+ *,
214
+ session: AsyncSession | Session = Depends(get_async_session),
215
+ none: None = Depends(set_global_background_tasks),
216
+ ):
217
+ ...more code
218
+ """
219
+ g.background_tasks = background_tasks
220
+
221
+
222
+ async def set_global_request(request: fastapi.Request):
223
+ """
224
+ A dependency for FastAPI that will set the `request` to the global variable `g.request`.
225
+
226
+ Usage:
227
+ ```python
228
+ async def get_info(
229
+ *,
230
+ session: AsyncSession | Session = Depends(get_async_session),
231
+ none: None = Depends(set_global_request),
232
+ ):
233
+ ...more code
234
+ """
235
+ g.request = request
236
+
237
+
238
+ def _ensure_roles(err_forbidden_message=ErrorCode.GET_USER_NO_ROLES):
239
+ """
240
+ A dependency for FastAPI that will ensure the current user has roles assigned.
241
+
242
+ Args:
243
+ err_forbidden_message (str): The error message to be used when raising `403 Forbidden`. Defaults to `ErrorCode.GET_USER_NO_ROLES`.
244
+
245
+ Raises:
246
+ HTTPException: Raised when the user is not authenticated.
247
+ HTTPException: Raised when the user has no roles assigned.
248
+ """
249
+ if not g.user:
250
+ raise HTTPException(
251
+ fastapi.status.HTTP_401_UNAUTHORIZED,
252
+ ErrorCode.GET_USER_MISSING_TOKEN_OR_INACTIVE_USER,
253
+ )
254
+
255
+ if not g.user.roles:
256
+ raise HTTPException(fastapi.status.HTTP_403_FORBIDDEN, err_forbidden_message)