amsdal 0.2.5__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of amsdal might be problematic. Click here for more details.

Files changed (254) hide show
  1. amsdal/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1308 -0
  2. amsdal/__about__.py +4 -0
  3. amsdal/__about__.pyi +1 -0
  4. amsdal/__init__.py +3 -0
  5. amsdal/__init__.pyi +0 -0
  6. amsdal/__migrations__/0000_initial.py +203 -0
  7. amsdal/__migrations__/0001_datetime_type.py +18 -0
  8. amsdal/__migrations__/0002_fixture_order.py +40 -0
  9. amsdal/cloud/__init__.cp312-win_amd64.pyd +0 -0
  10. amsdal/cloud/__init__.pyi +0 -0
  11. amsdal/cloud/client.cp312-win_amd64.pyd +0 -0
  12. amsdal/cloud/client.pyi +57 -0
  13. amsdal/cloud/constants.cp312-win_amd64.pyd +0 -0
  14. amsdal/cloud/constants.pyi +13 -0
  15. amsdal/cloud/enums.cp312-win_amd64.pyd +0 -0
  16. amsdal/cloud/enums.pyi +68 -0
  17. amsdal/cloud/models/__init__.cp312-win_amd64.pyd +0 -0
  18. amsdal/cloud/models/__init__.pyi +0 -0
  19. amsdal/cloud/models/base.cp312-win_amd64.pyd +0 -0
  20. amsdal/cloud/models/base.pyi +247 -0
  21. amsdal/cloud/services/__init__.cp312-win_amd64.pyd +0 -0
  22. amsdal/cloud/services/__init__.pyi +0 -0
  23. amsdal/cloud/services/actions/__init__.cp312-win_amd64.pyd +0 -0
  24. amsdal/cloud/services/actions/__init__.pyi +0 -0
  25. amsdal/cloud/services/actions/add_allowlist_ip.cp312-win_amd64.pyd +0 -0
  26. amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
  27. amsdal/cloud/services/actions/add_basic_auth.cp312-win_amd64.pyd +0 -0
  28. amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
  29. amsdal/cloud/services/actions/add_dependency.cp312-win_amd64.pyd +0 -0
  30. amsdal/cloud/services/actions/add_dependency.pyi +19 -0
  31. amsdal/cloud/services/actions/add_secret.cp312-win_amd64.pyd +0 -0
  32. amsdal/cloud/services/actions/add_secret.pyi +20 -0
  33. amsdal/cloud/services/actions/base.cp312-win_amd64.pyd +0 -0
  34. amsdal/cloud/services/actions/base.pyi +122 -0
  35. amsdal/cloud/services/actions/create_deploy.cp312-win_amd64.pyd +0 -0
  36. amsdal/cloud/services/actions/create_deploy.pyi +41 -0
  37. amsdal/cloud/services/actions/create_env.cp312-win_amd64.pyd +0 -0
  38. amsdal/cloud/services/actions/create_env.pyi +19 -0
  39. amsdal/cloud/services/actions/create_session.cp312-win_amd64.pyd +0 -0
  40. amsdal/cloud/services/actions/create_session.pyi +17 -0
  41. amsdal/cloud/services/actions/delete_allowlist_ip.cp312-win_amd64.pyd +0 -0
  42. amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
  43. amsdal/cloud/services/actions/delete_basic_auth.cp312-win_amd64.pyd +0 -0
  44. amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
  45. amsdal/cloud/services/actions/delete_dependency.cp312-win_amd64.pyd +0 -0
  46. amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
  47. amsdal/cloud/services/actions/delete_env.cp312-win_amd64.pyd +0 -0
  48. amsdal/cloud/services/actions/delete_env.pyi +21 -0
  49. amsdal/cloud/services/actions/delete_secret.cp312-win_amd64.pyd +0 -0
  50. amsdal/cloud/services/actions/delete_secret.pyi +21 -0
  51. amsdal/cloud/services/actions/destroy_deploy.cp312-win_amd64.pyd +0 -0
  52. amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
  53. amsdal/cloud/services/actions/expose_db.cp312-win_amd64.pyd +0 -0
  54. amsdal/cloud/services/actions/expose_db.pyi +22 -0
  55. amsdal/cloud/services/actions/get_basic_auth_credentials.cp312-win_amd64.pyd +0 -0
  56. amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
  57. amsdal/cloud/services/actions/get_monitoring_info.cp312-win_amd64.pyd +0 -0
  58. amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
  59. amsdal/cloud/services/actions/list_dependencies.cp312-win_amd64.pyd +0 -0
  60. amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
  61. amsdal/cloud/services/actions/list_deploys.cp312-win_amd64.pyd +0 -0
  62. amsdal/cloud/services/actions/list_deploys.pyi +19 -0
  63. amsdal/cloud/services/actions/list_envs.cp312-win_amd64.pyd +0 -0
  64. amsdal/cloud/services/actions/list_envs.pyi +20 -0
  65. amsdal/cloud/services/actions/list_secrets.cp312-win_amd64.pyd +0 -0
  66. amsdal/cloud/services/actions/list_secrets.pyi +22 -0
  67. amsdal/cloud/services/actions/manager.cp312-win_amd64.pyd +0 -0
  68. amsdal/cloud/services/actions/manager.pyi +278 -0
  69. amsdal/cloud/services/actions/signup_action.cp312-win_amd64.pyd +0 -0
  70. amsdal/cloud/services/actions/signup_action.pyi +20 -0
  71. amsdal/cloud/services/actions/update_deploy.cp312-win_amd64.pyd +0 -0
  72. amsdal/cloud/services/actions/update_deploy.pyi +19 -0
  73. amsdal/cloud/services/auth/__init__.cp312-win_amd64.pyd +0 -0
  74. amsdal/cloud/services/auth/__init__.pyi +0 -0
  75. amsdal/cloud/services/auth/base.cp312-win_amd64.pyd +0 -0
  76. amsdal/cloud/services/auth/base.pyi +6 -0
  77. amsdal/cloud/services/auth/credentials.cp312-win_amd64.pyd +0 -0
  78. amsdal/cloud/services/auth/credentials.pyi +31 -0
  79. amsdal/cloud/services/auth/manager.cp312-win_amd64.pyd +0 -0
  80. amsdal/cloud/services/auth/manager.pyi +26 -0
  81. amsdal/cloud/services/auth/signup_service.cp312-win_amd64.pyd +0 -0
  82. amsdal/cloud/services/auth/signup_service.pyi +32 -0
  83. amsdal/cloud/services/auth/token.cp312-win_amd64.pyd +0 -0
  84. amsdal/cloud/services/auth/token.pyi +28 -0
  85. amsdal/configs/__init__.py +0 -0
  86. amsdal/configs/__init__.pyi +0 -0
  87. amsdal/configs/constants.py +33 -0
  88. amsdal/configs/constants.pyi +22 -0
  89. amsdal/configs/main.py +254 -0
  90. amsdal/configs/main.pyi +177 -0
  91. amsdal/context/__init__.py +0 -0
  92. amsdal/context/__init__.pyi +0 -0
  93. amsdal/context/manager.py +69 -0
  94. amsdal/context/manager.pyi +50 -0
  95. amsdal/contrib/__init__.cp312-win_amd64.pyd +0 -0
  96. amsdal/contrib/__init__.pyi +0 -0
  97. amsdal/contrib/app_config.py +7 -0
  98. amsdal/contrib/app_config.pyi +6 -0
  99. amsdal/contrib/auth/__init__.py +0 -0
  100. amsdal/contrib/auth/__init__.pyi +0 -0
  101. amsdal/contrib/auth/app.py +27 -0
  102. amsdal/contrib/auth/app.pyi +15 -0
  103. amsdal/contrib/auth/decorators/__init__.py +20 -0
  104. amsdal/contrib/auth/decorators/__init__.pyi +6 -0
  105. amsdal/contrib/auth/errors.py +7 -0
  106. amsdal/contrib/auth/errors.pyi +4 -0
  107. amsdal/contrib/auth/lifecycle/__init__.py +0 -0
  108. amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
  109. amsdal/contrib/auth/lifecycle/consumer.py +222 -0
  110. amsdal/contrib/auth/lifecycle/consumer.pyi +69 -0
  111. amsdal/contrib/auth/migrations/0000_initial.py +49 -0
  112. amsdal/contrib/auth/models/login_session/hooks/pre_init.py +64 -0
  113. amsdal/contrib/auth/models/login_session/model.json +23 -0
  114. amsdal/contrib/auth/models/login_session/modifiers/display_name.py +11 -0
  115. amsdal/contrib/auth/models/permission/fixtures/basic_permissions.json +62 -0
  116. amsdal/contrib/auth/models/permission/model.json +18 -0
  117. amsdal/contrib/auth/models/permission/modifiers/display_name.py +11 -0
  118. amsdal/contrib/auth/models/user/hooks/post_init.py +58 -0
  119. amsdal/contrib/auth/models/user/hooks/pre_create.py +8 -0
  120. amsdal/contrib/auth/models/user/model.json +25 -0
  121. amsdal/contrib/auth/models/user/modifiers/display_name.py +19 -0
  122. amsdal/contrib/auth/settings.py +36 -0
  123. amsdal/contrib/auth/settings.pyi +26 -0
  124. amsdal/contrib/frontend_configs/__init__.py +0 -0
  125. amsdal/contrib/frontend_configs/__init__.pyi +0 -0
  126. amsdal/contrib/frontend_configs/app.py +24 -0
  127. amsdal/contrib/frontend_configs/app.pyi +19 -0
  128. amsdal/contrib/frontend_configs/constants.py +1 -0
  129. amsdal/contrib/frontend_configs/constants.pyi +1 -0
  130. amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
  131. amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
  132. amsdal/contrib/frontend_configs/conversion/convert.py +250 -0
  133. amsdal/contrib/frontend_configs/conversion/convert.pyi +23 -0
  134. amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
  135. amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
  136. amsdal/contrib/frontend_configs/lifecycle/consumer.py +242 -0
  137. amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +86 -0
  138. amsdal/contrib/frontend_configs/migrations/0000_initial.py +255 -0
  139. amsdal/contrib/frontend_configs/models/frontend_activator_config/model.json +11 -0
  140. amsdal/contrib/frontend_configs/models/frontend_config_async_validator/model.json +11 -0
  141. amsdal/contrib/frontend_configs/models/frontend_config_group_validator/model.json +52 -0
  142. amsdal/contrib/frontend_configs/models/frontend_config_option/model.json +15 -0
  143. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/model.json +6 -0
  144. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/properties/model_dump.py +13 -0
  145. amsdal/contrib/frontend_configs/models/frontend_config_slider_option/model.json +19 -0
  146. amsdal/contrib/frontend_configs/models/frontend_config_text_mask/model.json +26 -0
  147. amsdal/contrib/frontend_configs/models/frontend_config_validator/model.json +41 -0
  148. amsdal/contrib/frontend_configs/models/frontend_control_config/model.json +250 -0
  149. amsdal/contrib/frontend_configs/models/frontend_model_config/fixtures/permissions.json +24 -0
  150. amsdal/contrib/frontend_configs/models/frontend_model_config/model.json +17 -0
  151. amsdal/contrib/frontend_configs/models/frontent_config_control_action/model.json +54 -0
  152. amsdal/contrib/frontend_configs/models/frontent_config_control_action/properties/action_validate.py +33 -0
  153. amsdal/contrib/frontend_configs/utils.py +29 -0
  154. amsdal/contrib/frontend_configs/utils.pyi +17 -0
  155. amsdal/errors.py +31 -0
  156. amsdal/errors.pyi +12 -0
  157. amsdal/fixtures/__init__.cp312-win_amd64.pyd +0 -0
  158. amsdal/fixtures/__init__.pyi +0 -0
  159. amsdal/fixtures/manager.cp312-win_amd64.pyd +0 -0
  160. amsdal/fixtures/manager.pyi +118 -0
  161. amsdal/manager.cp312-win_amd64.pyd +0 -0
  162. amsdal/manager.pyi +183 -0
  163. amsdal/migration/__init__.cp312-win_amd64.pyd +0 -0
  164. amsdal/migration/__init__.pyi +0 -0
  165. amsdal/migration/base_migration_schemas.cp312-win_amd64.pyd +0 -0
  166. amsdal/migration/base_migration_schemas.pyi +120 -0
  167. amsdal/migration/data_classes.cp312-win_amd64.pyd +0 -0
  168. amsdal/migration/data_classes.pyi +178 -0
  169. amsdal/migration/executors/__init__.cp312-win_amd64.pyd +0 -0
  170. amsdal/migration/executors/__init__.pyi +0 -0
  171. amsdal/migration/executors/base.cp312-win_amd64.pyd +0 -0
  172. amsdal/migration/executors/base.pyi +106 -0
  173. amsdal/migration/executors/default_executor.cp312-win_amd64.pyd +0 -0
  174. amsdal/migration/executors/default_executor.pyi +96 -0
  175. amsdal/migration/executors/state_executor.cp312-win_amd64.pyd +0 -0
  176. amsdal/migration/executors/state_executor.pyi +78 -0
  177. amsdal/migration/file_migration_executor.cp312-win_amd64.pyd +0 -0
  178. amsdal/migration/file_migration_executor.pyi +68 -0
  179. amsdal/migration/file_migration_generator.cp312-win_amd64.pyd +0 -0
  180. amsdal/migration/file_migration_generator.pyi +139 -0
  181. amsdal/migration/file_migration_store.cp312-win_amd64.pyd +0 -0
  182. amsdal/migration/file_migration_store.pyi +61 -0
  183. amsdal/migration/file_migration_writer.cp312-win_amd64.pyd +0 -0
  184. amsdal/migration/file_migration_writer.pyi +73 -0
  185. amsdal/migration/migrations.cp312-win_amd64.pyd +0 -0
  186. amsdal/migration/migrations.pyi +166 -0
  187. amsdal/migration/migrations_loader.cp312-win_amd64.pyd +0 -0
  188. amsdal/migration/migrations_loader.pyi +32 -0
  189. amsdal/migration/schemas_loaders.cp312-win_amd64.pyd +0 -0
  190. amsdal/migration/schemas_loaders.pyi +37 -0
  191. amsdal/migration/templates/data_migration.tmpl +18 -0
  192. amsdal/migration/templates/dict_validator.tmpl +4 -0
  193. amsdal/migration/templates/migration.tmpl +6 -0
  194. amsdal/migration/templates/model_class.tmpl +8 -0
  195. amsdal/migration/templates/model_class_layout.tmpl +24 -0
  196. amsdal/migration/templates/options_validator.tmpl +4 -0
  197. amsdal/migration/utils.cp312-win_amd64.pyd +0 -0
  198. amsdal/migration/utils.pyi +58 -0
  199. amsdal/mixins/__init__.cp312-win_amd64.pyd +0 -0
  200. amsdal/mixins/__init__.pyi +0 -0
  201. amsdal/mixins/build_mixin.cp312-win_amd64.pyd +0 -0
  202. amsdal/mixins/build_mixin.pyi +78 -0
  203. amsdal/mixins/class_versions_mixin.cp312-win_amd64.pyd +0 -0
  204. amsdal/mixins/class_versions_mixin.pyi +6 -0
  205. amsdal/py.typed +0 -0
  206. amsdal/schemas/__init__.py +0 -0
  207. amsdal/schemas/__init__.pyi +0 -0
  208. amsdal/schemas/core/class_object/model.json +31 -0
  209. amsdal/schemas/core/class_object/properties/display_name.py +9 -0
  210. amsdal/schemas/core/class_object_meta/model.json +55 -0
  211. amsdal/schemas/core/class_property/model.json +22 -0
  212. amsdal/schemas/core/class_property_meta/model.json +23 -0
  213. amsdal/schemas/core/file/hooks/pre_create.py +11 -0
  214. amsdal/schemas/core/file/hooks/pre_update.py +11 -0
  215. amsdal/schemas/core/file/model.json +23 -0
  216. amsdal/schemas/core/file/properties/from_file.py +34 -0
  217. amsdal/schemas/core/file/properties/mimetype.py +13 -0
  218. amsdal/schemas/core/file/properties/str.py +6 -0
  219. amsdal/schemas/core/file/properties/to_file.py +24 -0
  220. amsdal/schemas/core/file/properties/validate_data.py +32 -0
  221. amsdal/schemas/core/fixture/model.json +35 -0
  222. amsdal/schemas/core/option/model.json +19 -0
  223. amsdal/schemas/core/validator/model.json +19 -0
  224. amsdal/schemas/manager.cp312-win_amd64.pyd +0 -0
  225. amsdal/schemas/manager.py +116 -0
  226. amsdal/schemas/manager.pyi +65 -0
  227. amsdal/schemas/types/anything/model.json +7 -0
  228. amsdal/schemas/types/array/model.json +7 -0
  229. amsdal/schemas/types/binary/model.json +7 -0
  230. amsdal/schemas/types/boolean/model.json +17 -0
  231. amsdal/schemas/types/date/model.json +7 -0
  232. amsdal/schemas/types/datetime/model.json +7 -0
  233. amsdal/schemas/types/dictionary/model.json +8 -0
  234. amsdal/schemas/types/number/model.json +8 -0
  235. amsdal/schemas/types/object/model.json +53 -0
  236. amsdal/schemas/types/string/model.json +8 -0
  237. amsdal/services/__init__.cp312-win_amd64.pyd +0 -0
  238. amsdal/services/__init__.pyi +0 -0
  239. amsdal/services/transaction_execution.cp312-win_amd64.pyd +0 -0
  240. amsdal/services/transaction_execution.pyi +76 -0
  241. amsdal/utils/__init__.py +0 -0
  242. amsdal/utils/__init__.pyi +0 -0
  243. amsdal/utils/contrib_paths.py +23 -0
  244. amsdal/utils/contrib_paths.pyi +14 -0
  245. amsdal/utils/tests/__init__.py +0 -0
  246. amsdal/utils/tests/enums.py +18 -0
  247. amsdal/utils/tests/factories.py +49 -0
  248. amsdal/utils/tests/helpers.py +309 -0
  249. amsdal-0.2.5.dist-info/LICENSE.txt +107 -0
  250. amsdal-0.2.5.dist-info/METADATA +366 -0
  251. amsdal-0.2.5.dist-info/RECORD +254 -0
  252. amsdal-0.2.5.dist-info/WHEEL +5 -0
  253. amsdal-0.2.5.dist-info/license_check.py +35 -0
  254. amsdal-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ from abc import ABC
2
+ from abc import abstractmethod
3
+
4
+
5
+ class AppConfig(ABC):
6
+ @abstractmethod
7
+ def on_ready(self) -> None: ...
@@ -0,0 +1,6 @@
1
+ import abc
2
+ from abc import ABC, abstractmethod
3
+
4
+ class AppConfig(ABC, metaclass=abc.ABCMeta):
5
+ @abstractmethod
6
+ def on_ready(self) -> None: ...
File without changes
File without changes
@@ -0,0 +1,27 @@
1
+ from amsdal_utils.lifecycle.enum import LifecycleEvent
2
+ from amsdal_utils.lifecycle.producer import LifecycleProducer
3
+
4
+ from amsdal.contrib.app_config import AppConfig
5
+
6
+
7
+ class AuthAppConfig(AppConfig):
8
+ """
9
+ Configuration class for the authentication application.
10
+
11
+ This class sets up the necessary listeners for various lifecycle events
12
+ related to authentication and permission checks.
13
+ """
14
+
15
+ def on_ready(self) -> None:
16
+ """
17
+ Sets up listeners for various lifecycle events.
18
+
19
+ This method adds listeners for server startup, user authentication, and permission checks.
20
+ """
21
+ from amsdal.contrib.auth.lifecycle.consumer import AuthenticateUserConsumer
22
+ from amsdal.contrib.auth.lifecycle.consumer import CheckAndCreateSuperUserConsumer
23
+ from amsdal.contrib.auth.lifecycle.consumer import CheckPermissionConsumer
24
+
25
+ LifecycleProducer.add_listener(LifecycleEvent.ON_SERVER_STARTUP, CheckAndCreateSuperUserConsumer)
26
+ LifecycleProducer.add_listener(LifecycleEvent.ON_AUTHENTICATE, AuthenticateUserConsumer)
27
+ LifecycleProducer.add_listener(LifecycleEvent.ON_PERMISSION_CHECK, CheckPermissionConsumer)
@@ -0,0 +1,15 @@
1
+ from amsdal.contrib.app_config import AppConfig as AppConfig
2
+
3
+ class AuthAppConfig(AppConfig):
4
+ """
5
+ Configuration class for the authentication application.
6
+
7
+ This class sets up the necessary listeners for various lifecycle events
8
+ related to authentication and permission checks.
9
+ """
10
+ def on_ready(self) -> None:
11
+ """
12
+ Sets up listeners for various lifecycle events.
13
+
14
+ This method adds listeners for server startup, user authentication, and permission checks.
15
+ """
@@ -0,0 +1,20 @@
1
+ from collections.abc import Callable
2
+ from functools import wraps
3
+ from typing import Any
4
+
5
+ from amsdal.context.manager import AmsdalContextManager
6
+ from amsdal.contrib.auth.errors import AuthenticationError
7
+
8
+
9
+ def require_auth(func: Callable[..., Any]) -> Callable[..., Any]:
10
+ @wraps(func)
11
+ def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]:
12
+ request = AmsdalContextManager().get_context().get('request', None)
13
+
14
+ if not request or not request.user:
15
+ msg = 'Authentication required'
16
+ raise AuthenticationError(msg)
17
+
18
+ return func(*args, **kwargs)
19
+
20
+ return wrapper
@@ -0,0 +1,6 @@
1
+ from amsdal.context.manager import AmsdalContextManager as AmsdalContextManager
2
+ from amsdal.contrib.auth.errors import AuthenticationError as AuthenticationError
3
+ from collections.abc import Callable as Callable
4
+ from typing import Any
5
+
6
+ def require_auth(func: Callable[..., Any]) -> Callable[..., Any]: ...
@@ -0,0 +1,7 @@
1
+ from amsdal_utils.errors import AmsdalError
2
+
3
+
4
+ class UserCreationError(AmsdalError): ...
5
+
6
+
7
+ class AuthenticationError(AmsdalError): ...
@@ -0,0 +1,4 @@
1
+ from amsdal_utils.errors import AmsdalError
2
+
3
+ class UserCreationError(AmsdalError): ...
4
+ class AuthenticationError(AmsdalError): ...
File without changes
File without changes
@@ -0,0 +1,222 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import jwt
5
+ from amsdal_data.transactions.decorators import transaction
6
+ from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
7
+ from amsdal_models.classes.model import Model
8
+ from amsdal_utils.lifecycle.consumer import LifecycleConsumer
9
+ from amsdal_utils.models.data_models.reference import Reference
10
+ from amsdal_utils.models.enums import Versions
11
+
12
+ from amsdal.contrib.auth.errors import AuthenticationError
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
18
+ """
19
+ Ensures the existence of a super user in the system.
20
+
21
+ This consumer checks if a super user exists based on the provided email and password
22
+ in the authentication settings. If the super user does not exist, it creates one.
23
+ """
24
+
25
+ @transaction
26
+ def on_event(self) -> None:
27
+ """
28
+ Checks for the existence of a super user and creates one if necessary.
29
+
30
+ This method ensures that a super user exists by checking the email and password
31
+ in the authentication settings. If the super user does not exist, it creates one
32
+ with the necessary permissions.
33
+ """
34
+ from amsdal.contrib.auth.settings import auth_settings
35
+ from models.contrib.permission import Permission # type: ignore[import-not-found]
36
+ from models.contrib.user import User # type: ignore[import-not-found]
37
+
38
+ logger.info('Ensure super user exists')
39
+
40
+ if not (auth_settings.ADMIN_USER_EMAIL and auth_settings.ADMIN_USER_PASSWORD):
41
+ logger.info('Email / password missing for super user - skipping')
42
+ return
43
+
44
+ user = (
45
+ User.objects.filter(email=auth_settings.ADMIN_USER_EMAIL, _address__object_version=Versions.LATEST)
46
+ .get_or_none()
47
+ .execute()
48
+ )
49
+ if user is not None:
50
+ logger.info('Super user already exists - skipping')
51
+ return
52
+
53
+ logger.info("Super user doesn't exist - creating now")
54
+
55
+ access_all_permission = (
56
+ Permission.objects.filter(
57
+ model='*',
58
+ action='*',
59
+ _address__object_version=Versions.LATEST,
60
+ )
61
+ .get()
62
+ .execute()
63
+ )
64
+
65
+ instance = User(
66
+ email=auth_settings.ADMIN_USER_EMAIL,
67
+ password=auth_settings.ADMIN_USER_PASSWORD,
68
+ permissions=[access_all_permission],
69
+ )
70
+ instance.save(force_insert=True)
71
+ logger.info('Super user created successfully')
72
+
73
+
74
+ class AuthenticateUserConsumer(LifecycleConsumer):
75
+ """
76
+ Authenticates a user based on a provided JWT token.
77
+
78
+ This consumer decodes the JWT token from the authorization header and retrieves
79
+ the corresponding user from the database. If the token is invalid or expired,
80
+ it raises an `AuthenticationError`.
81
+ """
82
+
83
+ def on_event(self, auth_header: str, authentication_info: Any) -> None:
84
+ """
85
+ Authenticates the user using the provided JWT token.
86
+
87
+ This method decodes the JWT token from the authorization header and retrieves
88
+ the corresponding user from the database. If the token is invalid or expired,
89
+ it raises an `AuthenticationError`.
90
+
91
+ Args:
92
+ auth_header (str): The JWT token from the authorization header.
93
+ authentication_info (Any): The authentication information object to update with the user.
94
+ """
95
+ from amsdal.contrib.auth.settings import auth_settings
96
+ from models.contrib.user import User # type: ignore[import-not-found]
97
+
98
+ authentication_info.user = None
99
+ email: str | None
100
+
101
+ try:
102
+ jwt_payload = jwt.decode(
103
+ auth_header,
104
+ auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
105
+ algorithms=['HS256'],
106
+ )
107
+ email = jwt_payload['email']
108
+ except jwt.ExpiredSignatureError as exc:
109
+ logger.error('Auth token expired. Defaulting to anonymous user.')
110
+
111
+ msg = 'Auth token has expired.'
112
+ raise AuthenticationError(msg) from exc
113
+ except Exception as exc:
114
+ logger.error('Auth token decode failure. Defaulting to anonymous user.')
115
+
116
+ msg = 'Failed to decode auth token.'
117
+ raise AuthenticationError(msg) from exc
118
+
119
+ user = User.objects.filter(email=email, _address__object_version=Versions.LATEST).get_or_none().execute()
120
+
121
+ authentication_info.user = user
122
+
123
+
124
+ class CheckPermissionConsumer(LifecycleConsumer):
125
+ """
126
+ Checks and manages permissions for a given user and object.
127
+
128
+ This consumer prepopulates default permissions, checks class-level permissions,
129
+ and object-level permissions for a given user and object.
130
+ """
131
+
132
+ def _prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None:
133
+ from amsdal.contrib.auth.settings import auth_settings
134
+ from models.contrib.permission import Permission # type: ignore[import-not-found]
135
+
136
+ permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
137
+ permissions_info.has_create_permission = (
138
+ (not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION) if object_class.__name__ != 'LoginSession' else True
139
+ )
140
+ permissions_info.has_update_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
141
+ permissions_info.has_delete_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
142
+
143
+ required_permissions = Permission.objects.filter(
144
+ model=object_class.__name__,
145
+ _address__object_version=Versions.LATEST,
146
+ ).execute()
147
+
148
+ for required_permission in required_permissions:
149
+ if required_permission.action == 'read':
150
+ permissions_info.has_read_permission = False
151
+ elif required_permission.action == 'create':
152
+ permissions_info.has_create_permission = False
153
+ elif required_permission.action == 'update':
154
+ permissions_info.has_update_permission = False
155
+ elif required_permission.action == 'delete':
156
+ permissions_info.has_delete_permission = False
157
+
158
+ def _check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None:
159
+ if hasattr(object_class, 'has_permission'):
160
+ for action in ['read', 'create', 'update', 'delete']:
161
+ setattr(permissions_info, f'has_{action}_permission', object_class.has_permission(user, action))
162
+
163
+ if not user or not getattr(user, 'permissions', None):
164
+ return
165
+
166
+ user_permissions = [
167
+ ReferenceLoader(p).load_reference() if isinstance(p, Reference) else p for p in user.permissions
168
+ ]
169
+
170
+ for user_permission in user_permissions:
171
+ if user_permission.model not in [object_class.__name__, '*']:
172
+ continue
173
+
174
+ if user_permission.action == 'read':
175
+ permissions_info.has_read_permission = True
176
+ elif user_permission.action == 'create':
177
+ permissions_info.has_create_permission = True
178
+ elif user_permission.action == 'update':
179
+ permissions_info.has_update_permission = True
180
+ elif user_permission.action == 'delete':
181
+ permissions_info.has_delete_permission = True
182
+ elif user_permission.action == '*':
183
+ permissions_info.has_read_permission = True
184
+ permissions_info.has_create_permission = True
185
+ permissions_info.has_update_permission = True
186
+ permissions_info.has_delete_permission = True
187
+
188
+ def _check_object_permissions(self, obj: Model, user: Any, permissions_info: Any) -> None:
189
+ if hasattr(obj, 'has_object_permission'):
190
+ for action in ['read', 'update', 'delete']:
191
+ setattr(
192
+ permissions_info,
193
+ f'has_{action}_permission',
194
+ getattr(permissions_info, f'has_{action}_permission') and obj.has_object_permission(user, action),
195
+ )
196
+
197
+ def on_event(
198
+ self,
199
+ object_class: type[Model],
200
+ user: Any,
201
+ access_types: list[Any], # noqa: ARG002
202
+ permissions_info: Any,
203
+ obj: Model | None = None,
204
+ ) -> None:
205
+ """
206
+ Main method to check permissions for a given user and object.
207
+
208
+ This method prepopulates default permissions, checks class-level permissions,
209
+ and object-level permissions for the given user and object.
210
+
211
+ Args:
212
+ object_class (type[Model]): The class of the object to check permissions for.
213
+ user (Any): The user to check permissions for.
214
+ access_types (list[Any]): The list of access types to check.
215
+ permissions_info (Any): The permissions information object to update.
216
+ obj (Model | None): The object to check permissions for, if any.
217
+ """
218
+ self._prepopulate_default_permissions(object_class, permissions_info)
219
+ self._check_class_permissions(object_class, user, permissions_info)
220
+
221
+ if obj:
222
+ self._check_object_permissions(obj, user, permissions_info)
@@ -0,0 +1,69 @@
1
+ from _typeshed import Incomplete
2
+ from amsdal.contrib.auth.errors import AuthenticationError as AuthenticationError
3
+ from amsdal_models.classes.model import Model
4
+ from amsdal_utils.lifecycle.consumer import LifecycleConsumer
5
+ from typing import Any
6
+
7
+ logger: Incomplete
8
+
9
+ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
10
+ """
11
+ Ensures the existence of a super user in the system.
12
+
13
+ This consumer checks if a super user exists based on the provided email and password
14
+ in the authentication settings. If the super user does not exist, it creates one.
15
+ """
16
+ def on_event(self) -> None:
17
+ """
18
+ Checks for the existence of a super user and creates one if necessary.
19
+
20
+ This method ensures that a super user exists by checking the email and password
21
+ in the authentication settings. If the super user does not exist, it creates one
22
+ with the necessary permissions.
23
+ """
24
+
25
+ class AuthenticateUserConsumer(LifecycleConsumer):
26
+ """
27
+ Authenticates a user based on a provided JWT token.
28
+
29
+ This consumer decodes the JWT token from the authorization header and retrieves
30
+ the corresponding user from the database. If the token is invalid or expired,
31
+ it raises an `AuthenticationError`.
32
+ """
33
+ def on_event(self, auth_header: str, authentication_info: Any) -> None:
34
+ """
35
+ Authenticates the user using the provided JWT token.
36
+
37
+ This method decodes the JWT token from the authorization header and retrieves
38
+ the corresponding user from the database. If the token is invalid or expired,
39
+ it raises an `AuthenticationError`.
40
+
41
+ Args:
42
+ auth_header (str): The JWT token from the authorization header.
43
+ authentication_info (Any): The authentication information object to update with the user.
44
+ """
45
+
46
+ class CheckPermissionConsumer(LifecycleConsumer):
47
+ """
48
+ Checks and manages permissions for a given user and object.
49
+
50
+ This consumer prepopulates default permissions, checks class-level permissions,
51
+ and object-level permissions for a given user and object.
52
+ """
53
+ def _prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None: ...
54
+ def _check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None: ...
55
+ def _check_object_permissions(self, obj: Model, user: Any, permissions_info: Any) -> None: ...
56
+ def on_event(self, object_class: type[Model], user: Any, access_types: list[Any], permissions_info: Any, obj: Model | None = None) -> None:
57
+ """
58
+ Main method to check permissions for a given user and object.
59
+
60
+ This method prepopulates default permissions, checks class-level permissions,
61
+ and object-level permissions for the given user and object.
62
+
63
+ Args:
64
+ object_class (type[Model]): The class of the object to check permissions for.
65
+ user (Any): The user to check permissions for.
66
+ access_types (list[Any]): The list of access types to check.
67
+ permissions_info (Any): The permissions information object to update.
68
+ obj (Model | None): The object to check permissions for, if any.
69
+ """
@@ -0,0 +1,49 @@
1
+ from amsdal_utils.models.enums import SchemaTypes
2
+
3
+ from amsdal.migration import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ operations: list[migrations.Operation] = [
8
+ migrations.CreateClass(
9
+ schema_type=SchemaTypes.CONTRIB,
10
+ class_name='Permission',
11
+ new_schema={
12
+ 'title': 'Permission',
13
+ 'required': ['model', 'action'],
14
+ 'properties': {
15
+ 'model': {'type': 'string', 'title': 'Model'},
16
+ 'action': {'type': 'string', 'title': 'Action'},
17
+ },
18
+ 'custom_code': "@property # type: ignore[misc]\ndef display_name(self) -> str: # type: ignore[no-untyped-def]\n return f'{self.model}:{self.action}'",
19
+ },
20
+ ),
21
+ migrations.CreateClass(
22
+ schema_type=SchemaTypes.CONTRIB,
23
+ class_name='User',
24
+ new_schema={
25
+ 'title': 'User',
26
+ 'required': ['email', 'password'],
27
+ 'properties': {
28
+ 'email': {'type': 'string', 'title': 'Email'},
29
+ 'password': {'type': 'binary', 'title': 'Password (hash)'},
30
+ 'permissions': {'type': 'array', 'items': {'type': 'Permission'}, 'title': 'Permissions'},
31
+ },
32
+ 'custom_code': "from typing import Any\n\n\ndef pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # type: ignore[no-untyped-def] # noqa: ARG001\n import bcrypt\n\n from amsdal.contrib.auth.errors import UserCreationError\n\n email = kwargs.get('email', None)\n password = kwargs.get('password', None)\n\n if email is None or email == '':\n msg = \"Email can't be empty\"\n raise UserCreationError(msg)\n\n if password is None or password == '':\n msg = \"Password can't be empty\"\n raise UserCreationError(msg)\n\n kwargs['email'] = email.lower()\n\n if is_new_object and '_metadata' not in kwargs:\n hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())\n kwargs['password'] = hashed_password\n kwargs['_object_id'] = email.lower()\n\ndef pre_create(self) -> None: # type: ignore[no-untyped-def] # noqa: ARG001\n pass\n\n@property # type: ignore[misc]\ndef display_name(self) -> str: # type: ignore[no-untyped-def]\n return self.email",
33
+ },
34
+ ),
35
+ migrations.CreateClass(
36
+ schema_type=SchemaTypes.CONTRIB,
37
+ class_name='LoginSession',
38
+ new_schema={
39
+ 'title': 'LoginSession',
40
+ 'required': ['email', 'password'],
41
+ 'properties': {
42
+ 'email': {'type': 'string', 'title': 'Email'},
43
+ 'password': {'type': 'string', 'title': 'Password (hash)'},
44
+ 'token': {'type': 'string', 'title': 'Token', 'mark_as_read_only': True},
45
+ },
46
+ 'custom_code': "from datetime import datetime\nfrom datetime import timedelta\nfrom datetime import timezone\nfrom typing import Any\n\nimport bcrypt\nimport jwt\nfrom amsdal_utils.models.enums import Versions\n\n\ndef pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # type: ignore[no-untyped-def] # noqa: ARG001\n if not is_new_object or '_metadata' in kwargs:\n return\n\n from amsdal.contrib.auth.errors import AuthenticationError\n from amsdal.contrib.auth.settings import auth_settings\n\n email = kwargs.get('email', None)\n password = kwargs.get('password', None)\n\n if not email:\n msg = \"Email can't be empty\"\n raise AuthenticationError(msg)\n\n if not password:\n msg = \"Password can't be empty\"\n raise AuthenticationError(msg)\n\n lowercased_email = email.lower()\n\n from models.contrib.user import User # type: ignore[import-not-found]\n\n user = User.objects.filter(email=lowercased_email, _address__object_version=Versions.LATEST).get_or_none().execute()\n\n if not user:\n msg = 'Invalid email / password'\n raise AuthenticationError(msg)\n\n if not bcrypt.checkpw(password.encode('utf-8') if isinstance(password, str) else password, user.password):\n msg = 'Invalid email / password'\n raise AuthenticationError(msg)\n\n kwargs['password'] = 'validated'\n expiration_time = datetime.now(tz=timezone.utc) + timedelta(seconds=1200)\n token = jwt.encode(\n {'email': lowercased_email, 'exp': expiration_time},\n key=auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]\n algorithm='HS256',\n )\n\n kwargs['token'] = token\n\n@property # type: ignore[misc]\ndef display_name(self) -> str: # type: ignore[no-untyped-def]\n return self.email",
47
+ },
48
+ ),
49
+ ]
@@ -0,0 +1,64 @@
1
+ from datetime import datetime
2
+ from datetime import timedelta
3
+ from datetime import timezone
4
+ from typing import Any
5
+
6
+ import bcrypt
7
+ import jwt
8
+ from amsdal_utils.models.enums import Versions
9
+
10
+
11
+ def pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # type: ignore[no-untyped-def] # noqa: ARG001
12
+ """
13
+ Pre-initializes a user object by validating email and password, and generating a JWT token.
14
+
15
+ This method checks if the object is new and validates the provided email and password.
16
+ If the email and password are valid, it generates a JWT token and adds it to the kwargs.
17
+
18
+ Args:
19
+ is_new_object (bool): Indicates if the object is new.
20
+ kwargs (dict[str, Any]): The keyword arguments containing user details.
21
+
22
+ Raises:
23
+ AuthenticationError: If the email or password is invalid.
24
+ """
25
+ if not is_new_object or '_metadata' in kwargs:
26
+ return
27
+
28
+ from amsdal.contrib.auth.errors import AuthenticationError
29
+ from amsdal.contrib.auth.settings import auth_settings
30
+
31
+ email = kwargs.get('email', None)
32
+ password = kwargs.get('password', None)
33
+
34
+ if not email:
35
+ msg = "Email can't be empty"
36
+ raise AuthenticationError(msg)
37
+
38
+ if not password:
39
+ msg = "Password can't be empty"
40
+ raise AuthenticationError(msg)
41
+
42
+ lowercased_email = email.lower()
43
+
44
+ from models.contrib.user import User # type: ignore[import-not-found]
45
+
46
+ user = User.objects.filter(email=lowercased_email, _address__object_version=Versions.LATEST).get_or_none().execute()
47
+
48
+ if not user:
49
+ msg = 'Invalid email / password'
50
+ raise AuthenticationError(msg)
51
+
52
+ if not bcrypt.checkpw(password.encode('utf-8') if isinstance(password, str) else password, user.password):
53
+ msg = 'Invalid email / password'
54
+ raise AuthenticationError(msg)
55
+
56
+ kwargs['password'] = 'validated'
57
+ expiration_time = datetime.now(tz=timezone.utc) + timedelta(seconds=auth_settings.AUTH_TOKEN_EXPIRATION)
58
+ token = jwt.encode(
59
+ {'email': lowercased_email, 'exp': expiration_time},
60
+ key=auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
61
+ algorithm='HS256',
62
+ )
63
+
64
+ kwargs['token'] = token
@@ -0,0 +1,23 @@
1
+ {
2
+ "title": "LoginSession",
3
+ "type": "object",
4
+ "properties": {
5
+ "email": {
6
+ "title": "Email",
7
+ "type": "string"
8
+ },
9
+ "password": {
10
+ "title": "Password (hash)",
11
+ "type": "string"
12
+ },
13
+ "token": {
14
+ "title": "Token",
15
+ "type": "string",
16
+ "mark_as_read_only": true
17
+ }
18
+ },
19
+ "required": [
20
+ "email",
21
+ "password"
22
+ ]
23
+ }
@@ -0,0 +1,11 @@
1
+ @property # type: ignore[misc]
2
+ def display_name(self) -> str: # type: ignore[no-untyped-def]
3
+ """
4
+ Returns the display name of the user.
5
+
6
+ This method returns the email of the user as their display name.
7
+
8
+ Returns:
9
+ str: The email of the user.
10
+ """
11
+ return self.email
@@ -0,0 +1,62 @@
1
+ [
2
+ {
3
+ "external_id": "user_delete",
4
+ "model": "User",
5
+ "action": "delete"
6
+ },
7
+ {
8
+ "external_id": "user_read",
9
+ "model": "User",
10
+ "action": "read"
11
+ },
12
+ {
13
+ "external_id": "user_update",
14
+ "model": "User",
15
+ "action": "update"
16
+ },
17
+ {
18
+ "external_id": "user_all_actions",
19
+ "model": "User",
20
+ "action": "*"
21
+ },
22
+ {
23
+ "external_id": "all_models_all_actions",
24
+ "model": "*",
25
+ "action": "*"
26
+ },
27
+ {
28
+ "external_id": "login_session_delete",
29
+ "model": "LoginSession",
30
+ "action": "delete"
31
+ },
32
+ {
33
+ "external_id": "login_session_read",
34
+ "model": "LoginSession",
35
+ "action": "read"
36
+ },
37
+ {
38
+ "external_id": "login_session_update",
39
+ "model": "LoginSession",
40
+ "action": "update"
41
+ },
42
+ {
43
+ "external_id": "permission_delete",
44
+ "model": "Permission",
45
+ "action": "delete"
46
+ },
47
+ {
48
+ "external_id": "permission_read",
49
+ "model": "Permission",
50
+ "action": "read"
51
+ },
52
+ {
53
+ "external_id": "permission_update",
54
+ "model": "Permission",
55
+ "action": "update"
56
+ },
57
+ {
58
+ "external_id": "permission_create",
59
+ "model": "Permission",
60
+ "action": "create"
61
+ }
62
+ ]
@@ -0,0 +1,18 @@
1
+ {
2
+ "title": "Permission",
3
+ "type": "object",
4
+ "properties": {
5
+ "model": {
6
+ "title": "Model",
7
+ "type": "string"
8
+ },
9
+ "action": {
10
+ "title": "Action",
11
+ "type": "string"
12
+ }
13
+ },
14
+ "required": [
15
+ "model",
16
+ "action"
17
+ ]
18
+ }
@@ -0,0 +1,11 @@
1
+ @property # type: ignore[misc]
2
+ def display_name(self) -> str: # type: ignore[no-untyped-def]
3
+ """
4
+ Returns the display name of the user.
5
+
6
+ This method returns a formatted string combining the model and action of the user.
7
+
8
+ Returns:
9
+ str: The formatted display name in the format 'model:action'.
10
+ """
11
+ return f'{self.model}:{self.action}'