amsdal 0.4.13__cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
- amsdal/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1334 -0
- amsdal/__about__.py +4 -0
- amsdal/__about__.pyi +1 -0
- amsdal/__init__.py +23 -0
- amsdal/__init__.pyi +9 -0
- amsdal/__migrations__/0000_initial.py +217 -0
- amsdal/__migrations__/0001_datetime_type.py +18 -0
- amsdal/__migrations__/0002_fixture_order.py +44 -0
- amsdal/__migrations__/0003_schema_type_in_class_meta.py +44 -0
- amsdal/cloud/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/__init__.pyi +0 -0
- amsdal/cloud/client.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/client.pyi +57 -0
- amsdal/cloud/constants.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/constants.pyi +13 -0
- amsdal/cloud/enums.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/enums.pyi +68 -0
- amsdal/cloud/models/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/__init__.pyi +0 -0
- amsdal/cloud/models/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/base.pyi +247 -0
- amsdal/cloud/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/__init__.pyi +0 -0
- amsdal/cloud/services/actions/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/__init__.pyi +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
- amsdal/cloud/services/actions/add_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_dependency.pyi +19 -0
- amsdal/cloud/services/actions/add_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_secret.pyi +20 -0
- amsdal/cloud/services/actions/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/base.pyi +122 -0
- amsdal/cloud/services/actions/create_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_deploy.pyi +41 -0
- amsdal/cloud/services/actions/create_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_env.pyi +19 -0
- amsdal/cloud/services/actions/create_session.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_session.pyi +17 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
- amsdal/cloud/services/actions/delete_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_env.pyi +21 -0
- amsdal/cloud/services/actions/delete_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_secret.pyi +21 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
- amsdal/cloud/services/actions/expose_db.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/expose_db.pyi +22 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
- amsdal/cloud/services/actions/list_deploys.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_deploys.pyi +19 -0
- amsdal/cloud/services/actions/list_envs.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_envs.pyi +20 -0
- amsdal/cloud/services/actions/list_secrets.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_secrets.pyi +22 -0
- amsdal/cloud/services/actions/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/manager.pyi +278 -0
- amsdal/cloud/services/actions/signup_action.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/signup_action.pyi +20 -0
- amsdal/cloud/services/actions/update_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/update_deploy.pyi +19 -0
- amsdal/cloud/services/auth/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/__init__.pyi +0 -0
- amsdal/cloud/services/auth/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/base.pyi +6 -0
- amsdal/cloud/services/auth/credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/credentials.pyi +30 -0
- amsdal/cloud/services/auth/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/manager.pyi +26 -0
- amsdal/cloud/services/auth/signup_service.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/signup_service.pyi +32 -0
- amsdal/cloud/services/auth/token.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/token.pyi +27 -0
- amsdal/configs/__init__.py +0 -0
- amsdal/configs/__init__.pyi +0 -0
- amsdal/configs/constants.py +33 -0
- amsdal/configs/constants.pyi +22 -0
- amsdal/configs/main.py +258 -0
- amsdal/configs/main.pyi +173 -0
- amsdal/context/__init__.py +0 -0
- amsdal/context/__init__.pyi +0 -0
- amsdal/context/manager.py +69 -0
- amsdal/context/manager.pyi +50 -0
- amsdal/contrib/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/contrib/__init__.pyi +0 -0
- amsdal/contrib/app_config.py +7 -0
- amsdal/contrib/app_config.pyi +6 -0
- amsdal/contrib/auth/__init__.py +0 -0
- amsdal/contrib/auth/__init__.pyi +0 -0
- amsdal/contrib/auth/app.py +27 -0
- amsdal/contrib/auth/app.pyi +15 -0
- amsdal/contrib/auth/decorators/__init__.py +35 -0
- amsdal/contrib/auth/decorators/__init__.pyi +6 -0
- amsdal/contrib/auth/errors.py +7 -0
- amsdal/contrib/auth/errors.pyi +4 -0
- amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
- amsdal/contrib/auth/lifecycle/__init__.py +0 -0
- amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/auth/lifecycle/consumer.py +394 -0
- amsdal/contrib/auth/lifecycle/consumer.pyi +108 -0
- amsdal/contrib/auth/migrations/0000_initial.py +84 -0
- amsdal/contrib/auth/models/__init__.py +0 -0
- amsdal/contrib/auth/models/__init__.pyi +0 -0
- amsdal/contrib/auth/models/login_session.py +118 -0
- amsdal/contrib/auth/models/login_session.pyi +37 -0
- amsdal/contrib/auth/models/permission.py +23 -0
- amsdal/contrib/auth/models/permission.pyi +18 -0
- amsdal/contrib/auth/models/user.py +106 -0
- amsdal/contrib/auth/models/user.pyi +46 -0
- amsdal/contrib/auth/settings.py +36 -0
- amsdal/contrib/auth/settings.pyi +26 -0
- amsdal/contrib/frontend_configs/__init__.py +0 -0
- amsdal/contrib/frontend_configs/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/app.py +24 -0
- amsdal/contrib/frontend_configs/app.pyi +19 -0
- amsdal/contrib/frontend_configs/constants.py +1 -0
- amsdal/contrib/frontend_configs/constants.pyi +1 -0
- amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
- amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
- amsdal/contrib/frontend_configs/conversion/convert.py +286 -0
- amsdal/contrib/frontend_configs/conversion/convert.pyi +22 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.py +306 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +98 -0
- amsdal/contrib/frontend_configs/migrations/0000_initial.py +256 -0
- amsdal/contrib/frontend_configs/models/__init__.py +0 -0
- amsdal/contrib/frontend_configs/models/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.pyi +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.pyi +7 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +54 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.pyi +32 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.pyi +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.pyi +9 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.pyi +10 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.pyi +15 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.py +87 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.pyi +35 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.pyi +9 -0
- amsdal/contrib/frontend_configs/utils.py +29 -0
- amsdal/contrib/frontend_configs/utils.pyi +17 -0
- amsdal/errors.py +31 -0
- amsdal/errors.pyi +12 -0
- amsdal/fixtures/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/__init__.pyi +0 -0
- amsdal/fixtures/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/manager.pyi +170 -0
- amsdal/fixtures/utils.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/utils.pyi +9 -0
- amsdal/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/manager.pyi +265 -0
- amsdal/mixins/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/__init__.pyi +0 -0
- amsdal/mixins/class_versions_mixin.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/class_versions_mixin.pyi +12 -0
- amsdal/models/__init__.py +19 -0
- amsdal/models/__init__.pyi +9 -0
- amsdal/models/core/__init__.py +0 -0
- amsdal/models/core/__init__.pyi +0 -0
- amsdal/models/core/class_object.py +37 -0
- amsdal/models/core/class_object.pyi +24 -0
- amsdal/models/core/class_object_meta.py +26 -0
- amsdal/models/core/class_object_meta.pyi +15 -0
- amsdal/models/core/class_property.py +20 -0
- amsdal/models/core/class_property.pyi +11 -0
- amsdal/models/core/class_property_meta.py +15 -0
- amsdal/models/core/class_property_meta.pyi +10 -0
- amsdal/models/core/file.py +156 -0
- amsdal/models/core/file.pyi +104 -0
- amsdal/models/core/fixture.py +25 -0
- amsdal/models/core/fixture.pyi +14 -0
- amsdal/models/core/option.py +11 -0
- amsdal/models/core/option.pyi +8 -0
- amsdal/models/core/validator.py +12 -0
- amsdal/models/core/validator.pyi +8 -0
- amsdal/models/types/__init__.py +0 -0
- amsdal/models/types/__init__.pyi +0 -0
- amsdal/models/types/object.py +26 -0
- amsdal/models/types/object.pyi +16 -0
- amsdal/py.typed +0 -0
- amsdal/queryset/__init__.py +21 -0
- amsdal/queryset/__init__.pyi +6 -0
- amsdal/schemas/__init__.py +0 -0
- amsdal/schemas/__init__.pyi +0 -0
- amsdal/schemas/core/class_object/model.json +31 -0
- amsdal/schemas/core/class_object/properties/display_name.py +9 -0
- amsdal/schemas/core/class_object_meta/model.json +59 -0
- amsdal/schemas/core/class_property/model.json +22 -0
- amsdal/schemas/core/class_property_meta/model.json +23 -0
- amsdal/schemas/core/file/hooks/pre_create.py +24 -0
- amsdal/schemas/core/file/hooks/pre_update.py +24 -0
- amsdal/schemas/core/file/model.json +23 -0
- amsdal/schemas/core/file/properties/from_file.py +34 -0
- amsdal/schemas/core/file/properties/mimetype.py +13 -0
- amsdal/schemas/core/file/properties/str.py +6 -0
- amsdal/schemas/core/file/properties/to_file.py +24 -0
- amsdal/schemas/core/file/properties/validate_data.py +32 -0
- amsdal/schemas/core/fixture/model.json +35 -0
- amsdal/schemas/core/option/model.json +19 -0
- amsdal/schemas/core/validator/model.json +19 -0
- amsdal/schemas/interfaces.py +25 -0
- amsdal/schemas/interfaces.pyi +20 -0
- amsdal/schemas/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/schemas/manager.py +0 -0
- amsdal/schemas/manager.pyi +0 -0
- amsdal/schemas/mixins/__init__.py +0 -0
- amsdal/schemas/mixins/__init__.pyi +0 -0
- amsdal/schemas/mixins/check_dependencies_mixin.py +125 -0
- amsdal/schemas/mixins/check_dependencies_mixin.pyi +45 -0
- amsdal/schemas/mixins/verify_schemas_mixin.py +96 -0
- amsdal/schemas/mixins/verify_schemas_mixin.pyi +33 -0
- amsdal/schemas/repository.py +84 -0
- amsdal/schemas/repository.pyi +22 -0
- amsdal/schemas/types/anything/model.json +7 -0
- amsdal/schemas/types/array/model.json +7 -0
- amsdal/schemas/types/binary/model.json +7 -0
- amsdal/schemas/types/boolean/model.json +17 -0
- amsdal/schemas/types/date/model.json +7 -0
- amsdal/schemas/types/datetime/model.json +7 -0
- amsdal/schemas/types/dictionary/model.json +8 -0
- amsdal/schemas/types/number/model.json +8 -0
- amsdal/schemas/types/object/model.json +53 -0
- amsdal/schemas/types/string/model.json +8 -0
- amsdal/schemas/utils.py +16 -0
- amsdal/schemas/utils.pyi +10 -0
- amsdal/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/__init__.pyi +0 -0
- amsdal/services/transaction_execution.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/transaction_execution.pyi +93 -0
- amsdal/transactions/__init__.py +13 -0
- amsdal/transactions/__init__.pyi +4 -0
- amsdal/utils/__init__.py +0 -0
- amsdal/utils/__init__.pyi +0 -0
- amsdal/utils/contrib_paths.py +23 -0
- amsdal/utils/contrib_paths.pyi +14 -0
- amsdal/utils/rollback/__init__.py +440 -0
- amsdal/utils/rollback/__init__.pyi +38 -0
- amsdal/utils/tests/__init__.py +0 -0
- amsdal/utils/tests/enums.py +18 -0
- amsdal/utils/tests/factories.py +49 -0
- amsdal/utils/tests/helpers.py +499 -0
- amsdal-0.4.13.dist-info/METADATA +369 -0
- amsdal-0.4.13.dist-info/RECORD +269 -0
- amsdal-0.4.13.dist-info/WHEEL +6 -0
- amsdal-0.4.13.dist-info/licenses/LICENSE.txt +107 -0
- amsdal-0.4.13.dist-info/top_level.txt +1 -0
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,35 @@
|
|
1
|
+
import asyncio
|
2
|
+
from collections.abc import Callable
|
3
|
+
from functools import wraps
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from amsdal.context.manager import AmsdalContextManager
|
7
|
+
from amsdal.contrib.auth.errors import AuthenticationError
|
8
|
+
|
9
|
+
|
10
|
+
def require_auth(func: Callable[..., Any]) -> Callable[..., Any]:
|
11
|
+
if asyncio.iscoroutinefunction(func):
|
12
|
+
|
13
|
+
@wraps(func)
|
14
|
+
async def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]:
|
15
|
+
request = AmsdalContextManager().get_context().get('request', None)
|
16
|
+
|
17
|
+
if not request or not request.user:
|
18
|
+
msg = 'Authentication required'
|
19
|
+
raise AuthenticationError(msg)
|
20
|
+
|
21
|
+
return await func(*args, **kwargs)
|
22
|
+
|
23
|
+
else:
|
24
|
+
|
25
|
+
@wraps(func)
|
26
|
+
def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]:
|
27
|
+
request = AmsdalContextManager().get_context().get('request', None)
|
28
|
+
|
29
|
+
if not request or not request.user:
|
30
|
+
msg = 'Authentication required'
|
31
|
+
raise AuthenticationError(msg)
|
32
|
+
|
33
|
+
return func(*args, **kwargs)
|
34
|
+
|
35
|
+
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,64 @@
|
|
1
|
+
{
|
2
|
+
"Permission": [
|
3
|
+
{
|
4
|
+
"_external_id": "user_delete",
|
5
|
+
"model": "User",
|
6
|
+
"action": "delete"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"_external_id": "user_read",
|
10
|
+
"model": "User",
|
11
|
+
"action": "read"
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"_external_id": "user_update",
|
15
|
+
"model": "User",
|
16
|
+
"action": "update"
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"_external_id": "user_all_actions",
|
20
|
+
"model": "User",
|
21
|
+
"action": "*"
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"_external_id": "all_models_all_actions",
|
25
|
+
"model": "*",
|
26
|
+
"action": "*"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"_external_id": "login_session_delete",
|
30
|
+
"model": "LoginSession",
|
31
|
+
"action": "delete"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"_external_id": "login_session_read",
|
35
|
+
"model": "LoginSession",
|
36
|
+
"action": "read"
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"_external_id": "login_session_update",
|
40
|
+
"model": "LoginSession",
|
41
|
+
"action": "update"
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"_external_id": "permission_delete",
|
45
|
+
"model": "Permission",
|
46
|
+
"action": "delete"
|
47
|
+
},
|
48
|
+
{
|
49
|
+
"_external_id": "permission_read",
|
50
|
+
"model": "Permission",
|
51
|
+
"action": "read"
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"_external_id": "permission_update",
|
55
|
+
"model": "Permission",
|
56
|
+
"action": "update"
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"_external_id": "permission_create",
|
60
|
+
"model": "Permission",
|
61
|
+
"action": "create"
|
62
|
+
}
|
63
|
+
]
|
64
|
+
}
|
File without changes
|
File without changes
|
@@ -0,0 +1,394 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
import jwt
|
5
|
+
from amsdal_data.transactions.decorators import async_transaction
|
6
|
+
from amsdal_data.transactions.decorators import transaction
|
7
|
+
from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
|
8
|
+
from amsdal_models.classes.model import Model
|
9
|
+
from amsdal_utils.lifecycle.consumer import LifecycleConsumer
|
10
|
+
from amsdal_utils.models.data_models.reference import Reference
|
11
|
+
from amsdal_utils.models.enums import Versions
|
12
|
+
|
13
|
+
from amsdal.contrib.auth.errors import AuthenticationError
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
19
|
+
"""
|
20
|
+
Ensures the existence of a super user in the system.
|
21
|
+
|
22
|
+
This consumer checks if a super user exists based on the provided email and password
|
23
|
+
in the authentication settings. If the super user does not exist, it creates one.
|
24
|
+
"""
|
25
|
+
|
26
|
+
@transaction
|
27
|
+
def on_event(self) -> None:
|
28
|
+
"""
|
29
|
+
Checks for the existence of a super user and creates one if necessary.
|
30
|
+
|
31
|
+
This method ensures that a super user exists by checking the email and password
|
32
|
+
in the authentication settings. If the super user does not exist, it creates one
|
33
|
+
with the necessary permissions.
|
34
|
+
"""
|
35
|
+
from amsdal.contrib.auth.models.permission import Permission
|
36
|
+
from amsdal.contrib.auth.models.user import User
|
37
|
+
from amsdal.contrib.auth.settings import auth_settings
|
38
|
+
|
39
|
+
logger.info('Ensure super user exists')
|
40
|
+
|
41
|
+
if not (auth_settings.ADMIN_USER_EMAIL and auth_settings.ADMIN_USER_PASSWORD):
|
42
|
+
logger.info('Email / password missing for super user - skipping')
|
43
|
+
return
|
44
|
+
|
45
|
+
user = (
|
46
|
+
User.objects.filter(email=auth_settings.ADMIN_USER_EMAIL, _address__object_version=Versions.LATEST)
|
47
|
+
.get_or_none()
|
48
|
+
.execute()
|
49
|
+
)
|
50
|
+
if user is not None:
|
51
|
+
logger.info('Super user already exists - skipping')
|
52
|
+
return
|
53
|
+
|
54
|
+
logger.info("Super user doesn't exist - creating now")
|
55
|
+
|
56
|
+
access_all_permission = (
|
57
|
+
Permission.objects.filter(
|
58
|
+
model='*',
|
59
|
+
action='*',
|
60
|
+
_address__object_version=Versions.LATEST,
|
61
|
+
)
|
62
|
+
.get()
|
63
|
+
.execute()
|
64
|
+
)
|
65
|
+
|
66
|
+
instance = User(
|
67
|
+
email=auth_settings.ADMIN_USER_EMAIL,
|
68
|
+
password=auth_settings.ADMIN_USER_PASSWORD.encode(),
|
69
|
+
permissions=[access_all_permission],
|
70
|
+
)
|
71
|
+
instance.save(force_insert=True)
|
72
|
+
logger.info('Super user created successfully')
|
73
|
+
|
74
|
+
@async_transaction
|
75
|
+
async def on_event_async(self) -> None: # type: ignore[override]
|
76
|
+
"""
|
77
|
+
Checks for the existence of a super user and creates one if necessary.
|
78
|
+
|
79
|
+
This method ensures that a super user exists by checking the email and password
|
80
|
+
in the authentication settings. If the super user does not exist, it creates one
|
81
|
+
with the necessary permissions.
|
82
|
+
"""
|
83
|
+
from amsdal.contrib.auth.models.permission import Permission # type: ignore[import-not-found]
|
84
|
+
from amsdal.contrib.auth.models.user import User # type: ignore[import-not-found]
|
85
|
+
from amsdal.contrib.auth.settings import auth_settings
|
86
|
+
|
87
|
+
logger.info('Ensure super user exists')
|
88
|
+
|
89
|
+
if not (auth_settings.ADMIN_USER_EMAIL and auth_settings.ADMIN_USER_PASSWORD):
|
90
|
+
logger.info('Email / password missing for super user - skipping')
|
91
|
+
return
|
92
|
+
|
93
|
+
user = (
|
94
|
+
await User.objects.filter(email=auth_settings.ADMIN_USER_EMAIL, _address__object_version=Versions.LATEST)
|
95
|
+
.get_or_none()
|
96
|
+
.aexecute()
|
97
|
+
)
|
98
|
+
if user is not None:
|
99
|
+
logger.info('Super user already exists - skipping')
|
100
|
+
return
|
101
|
+
|
102
|
+
logger.info("Super user doesn't exist - creating now")
|
103
|
+
|
104
|
+
access_all_permission = (
|
105
|
+
await Permission.objects.filter(
|
106
|
+
model='*',
|
107
|
+
action='*',
|
108
|
+
_address__object_version=Versions.LATEST,
|
109
|
+
)
|
110
|
+
.get()
|
111
|
+
.aexecute()
|
112
|
+
)
|
113
|
+
|
114
|
+
instance = User(
|
115
|
+
email=auth_settings.ADMIN_USER_EMAIL,
|
116
|
+
password=auth_settings.ADMIN_USER_PASSWORD.encode(),
|
117
|
+
permissions=[access_all_permission],
|
118
|
+
)
|
119
|
+
await instance.asave(force_insert=True) # type: ignore[misc]
|
120
|
+
logger.info('Super user created successfully')
|
121
|
+
|
122
|
+
|
123
|
+
class AuthenticateUserConsumer(LifecycleConsumer):
|
124
|
+
"""
|
125
|
+
Authenticates a user based on a provided JWT token.
|
126
|
+
|
127
|
+
This consumer decodes the JWT token from the authorization header and retrieves
|
128
|
+
the corresponding user from the database. If the token is invalid or expired,
|
129
|
+
it raises an `AuthenticationError`.
|
130
|
+
"""
|
131
|
+
|
132
|
+
def on_event(self, auth_header: str, authentication_info: Any) -> None:
|
133
|
+
"""
|
134
|
+
Authenticates the user using the provided JWT token.
|
135
|
+
|
136
|
+
This method decodes the JWT token from the authorization header and retrieves
|
137
|
+
the corresponding user from the database. If the token is invalid or expired,
|
138
|
+
it raises an `AuthenticationError`.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
auth_header (str): The JWT token from the authorization header.
|
142
|
+
authentication_info (Any): The authentication information object to update with the user.
|
143
|
+
"""
|
144
|
+
from amsdal.contrib.auth.models.user import User # type: ignore[import-not-found]
|
145
|
+
from amsdal.contrib.auth.settings import auth_settings
|
146
|
+
|
147
|
+
authentication_info.user = None
|
148
|
+
email: str | None
|
149
|
+
|
150
|
+
try:
|
151
|
+
jwt_payload = jwt.decode(
|
152
|
+
auth_header,
|
153
|
+
auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
|
154
|
+
algorithms=['HS256'],
|
155
|
+
)
|
156
|
+
email = jwt_payload['email']
|
157
|
+
except jwt.ExpiredSignatureError as exc:
|
158
|
+
logger.error('Auth token expired. Defaulting to anonymous user.')
|
159
|
+
|
160
|
+
msg = 'Auth token has expired.'
|
161
|
+
raise AuthenticationError(msg) from exc
|
162
|
+
except Exception as exc:
|
163
|
+
logger.error('Auth token decode failure. Defaulting to anonymous user.')
|
164
|
+
|
165
|
+
msg = 'Failed to decode auth token.'
|
166
|
+
raise AuthenticationError(msg) from exc
|
167
|
+
|
168
|
+
user = User.objects.filter(email=email, _address__object_version=Versions.LATEST).get_or_none().execute()
|
169
|
+
|
170
|
+
authentication_info.user = user
|
171
|
+
|
172
|
+
async def on_event_async(self, auth_header: str, authentication_info: Any) -> None:
|
173
|
+
"""
|
174
|
+
Authenticates the user using the provided JWT token.
|
175
|
+
|
176
|
+
This method decodes the JWT token from the authorization header and retrieves
|
177
|
+
the corresponding user from the database. If the token is invalid or expired,
|
178
|
+
it raises an `AuthenticationError`.
|
179
|
+
|
180
|
+
Args:
|
181
|
+
auth_header (str): The JWT token from the authorization header.
|
182
|
+
authentication_info (Any): The authentication information object to update with the user.
|
183
|
+
"""
|
184
|
+
from amsdal.contrib.auth.models.user import User # type: ignore[import-not-found]
|
185
|
+
from amsdal.contrib.auth.settings import auth_settings
|
186
|
+
|
187
|
+
authentication_info.user = None
|
188
|
+
email: str | None
|
189
|
+
|
190
|
+
try:
|
191
|
+
jwt_payload = jwt.decode(
|
192
|
+
auth_header,
|
193
|
+
auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
|
194
|
+
algorithms=['HS256'],
|
195
|
+
)
|
196
|
+
email = jwt_payload['email']
|
197
|
+
except jwt.ExpiredSignatureError as exc:
|
198
|
+
logger.error('Auth token expired. Defaulting to anonymous user.')
|
199
|
+
|
200
|
+
msg = 'Auth token has expired.'
|
201
|
+
raise AuthenticationError(msg) from exc
|
202
|
+
except Exception as exc:
|
203
|
+
logger.error('Auth token decode failure. Defaulting to anonymous user.')
|
204
|
+
|
205
|
+
msg = 'Failed to decode auth token.'
|
206
|
+
raise AuthenticationError(msg) from exc
|
207
|
+
|
208
|
+
user = await User.objects.filter(email=email, _address__object_version=Versions.LATEST).get_or_none().aexecute()
|
209
|
+
|
210
|
+
authentication_info.user = user
|
211
|
+
|
212
|
+
|
213
|
+
class CheckPermissionConsumer(LifecycleConsumer):
|
214
|
+
"""
|
215
|
+
Checks and manages permissions for a given user and object.
|
216
|
+
|
217
|
+
This consumer prepopulates default permissions, checks class-level permissions,
|
218
|
+
and object-level permissions for a given user and object.
|
219
|
+
"""
|
220
|
+
|
221
|
+
def _prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None:
|
222
|
+
from amsdal.contrib.auth.models.permission import Permission
|
223
|
+
from amsdal.contrib.auth.settings import auth_settings
|
224
|
+
|
225
|
+
permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
226
|
+
permissions_info.has_create_permission = (
|
227
|
+
(not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION) if object_class.__name__ != 'LoginSession' else True
|
228
|
+
)
|
229
|
+
permissions_info.has_update_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
230
|
+
permissions_info.has_delete_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
231
|
+
|
232
|
+
required_permissions = Permission.objects.filter(
|
233
|
+
model=object_class.__name__,
|
234
|
+
_address__object_version=Versions.LATEST,
|
235
|
+
).execute()
|
236
|
+
|
237
|
+
for required_permission in required_permissions:
|
238
|
+
if required_permission.action == 'read':
|
239
|
+
permissions_info.has_read_permission = False
|
240
|
+
elif required_permission.action == 'create':
|
241
|
+
permissions_info.has_create_permission = False
|
242
|
+
elif required_permission.action == 'update':
|
243
|
+
permissions_info.has_update_permission = False
|
244
|
+
elif required_permission.action == 'delete':
|
245
|
+
permissions_info.has_delete_permission = False
|
246
|
+
|
247
|
+
async def _async_prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None:
|
248
|
+
from amsdal.contrib.auth.models.permission import Permission # type: ignore[import-not-found]
|
249
|
+
from amsdal.contrib.auth.settings import auth_settings
|
250
|
+
|
251
|
+
permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
252
|
+
permissions_info.has_create_permission = (
|
253
|
+
(not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION) if object_class.__name__ != 'LoginSession' else True
|
254
|
+
)
|
255
|
+
permissions_info.has_update_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
256
|
+
permissions_info.has_delete_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
257
|
+
|
258
|
+
required_permissions = await Permission.objects.filter(
|
259
|
+
model=object_class.__name__,
|
260
|
+
_address__object_version=Versions.LATEST,
|
261
|
+
).aexecute()
|
262
|
+
|
263
|
+
for required_permission in required_permissions:
|
264
|
+
if required_permission.action == 'read':
|
265
|
+
permissions_info.has_read_permission = False
|
266
|
+
elif required_permission.action == 'create':
|
267
|
+
permissions_info.has_create_permission = False
|
268
|
+
elif required_permission.action == 'update':
|
269
|
+
permissions_info.has_update_permission = False
|
270
|
+
elif required_permission.action == 'delete':
|
271
|
+
permissions_info.has_delete_permission = False
|
272
|
+
|
273
|
+
def _check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None:
|
274
|
+
if hasattr(object_class, 'has_permission'):
|
275
|
+
for action in ['read', 'create', 'update', 'delete']:
|
276
|
+
setattr(permissions_info, f'has_{action}_permission', object_class.has_permission(user, action))
|
277
|
+
|
278
|
+
if not user or not getattr(user, 'permissions', None):
|
279
|
+
return
|
280
|
+
|
281
|
+
user_permissions = [
|
282
|
+
ReferenceLoader(p).load_reference() if isinstance(p, Reference) else p for p in user.permissions
|
283
|
+
]
|
284
|
+
|
285
|
+
for user_permission in user_permissions:
|
286
|
+
if user_permission.model not in [object_class.__name__, '*']:
|
287
|
+
continue
|
288
|
+
|
289
|
+
if user_permission.action == 'read':
|
290
|
+
permissions_info.has_read_permission = True
|
291
|
+
elif user_permission.action == 'create':
|
292
|
+
permissions_info.has_create_permission = True
|
293
|
+
elif user_permission.action == 'update':
|
294
|
+
permissions_info.has_update_permission = True
|
295
|
+
elif user_permission.action == 'delete':
|
296
|
+
permissions_info.has_delete_permission = True
|
297
|
+
elif user_permission.action == '*':
|
298
|
+
permissions_info.has_read_permission = True
|
299
|
+
permissions_info.has_create_permission = True
|
300
|
+
permissions_info.has_update_permission = True
|
301
|
+
permissions_info.has_delete_permission = True
|
302
|
+
|
303
|
+
async def _async_check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None:
|
304
|
+
if hasattr(object_class, 'has_permission'):
|
305
|
+
for action in ['read', 'create', 'update', 'delete']:
|
306
|
+
setattr(permissions_info, f'has_{action}_permission', object_class.has_permission(user, action))
|
307
|
+
|
308
|
+
if not user or not getattr(user, 'permissions', None):
|
309
|
+
return
|
310
|
+
|
311
|
+
user_permissions = [
|
312
|
+
await ReferenceLoader(p).aload_reference() if isinstance(p, Reference) else p for p in user.permissions
|
313
|
+
]
|
314
|
+
|
315
|
+
for user_permission in user_permissions:
|
316
|
+
if user_permission.model not in [object_class.__name__, '*']:
|
317
|
+
continue
|
318
|
+
|
319
|
+
if user_permission.action == 'read':
|
320
|
+
permissions_info.has_read_permission = True
|
321
|
+
elif user_permission.action == 'create':
|
322
|
+
permissions_info.has_create_permission = True
|
323
|
+
elif user_permission.action == 'update':
|
324
|
+
permissions_info.has_update_permission = True
|
325
|
+
elif user_permission.action == 'delete':
|
326
|
+
permissions_info.has_delete_permission = True
|
327
|
+
elif user_permission.action == '*':
|
328
|
+
permissions_info.has_read_permission = True
|
329
|
+
permissions_info.has_create_permission = True
|
330
|
+
permissions_info.has_update_permission = True
|
331
|
+
permissions_info.has_delete_permission = True
|
332
|
+
|
333
|
+
def _check_object_permissions(self, obj: Model, user: Any, permissions_info: Any) -> None:
|
334
|
+
if hasattr(obj, 'has_object_permission'):
|
335
|
+
for action in ['read', 'update', 'delete']:
|
336
|
+
setattr(
|
337
|
+
permissions_info,
|
338
|
+
f'has_{action}_permission',
|
339
|
+
getattr(permissions_info, f'has_{action}_permission') and obj.has_object_permission(user, action),
|
340
|
+
)
|
341
|
+
|
342
|
+
def on_event(
|
343
|
+
self,
|
344
|
+
object_class: type[Model],
|
345
|
+
user: Any,
|
346
|
+
access_types: list[Any], # noqa: ARG002
|
347
|
+
permissions_info: Any,
|
348
|
+
obj: Model | None = None,
|
349
|
+
) -> None:
|
350
|
+
"""
|
351
|
+
Main method to check permissions for a given user and object.
|
352
|
+
|
353
|
+
This method prepopulates default permissions, checks class-level permissions,
|
354
|
+
and object-level permissions for the given user and object.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
object_class (type[Model]): The class of the object to check permissions for.
|
358
|
+
user (Any): The user to check permissions for.
|
359
|
+
access_types (list[Any]): The list of access types to check.
|
360
|
+
permissions_info (Any): The permissions information object to update.
|
361
|
+
obj (Model | None): The object to check permissions for, if any.
|
362
|
+
"""
|
363
|
+
self._prepopulate_default_permissions(object_class, permissions_info)
|
364
|
+
self._check_class_permissions(object_class, user, permissions_info)
|
365
|
+
|
366
|
+
if obj:
|
367
|
+
self._check_object_permissions(obj, user, permissions_info)
|
368
|
+
|
369
|
+
async def on_event_async(
|
370
|
+
self,
|
371
|
+
object_class: type[Model],
|
372
|
+
user: Any,
|
373
|
+
access_types: list[Any], # noqa: ARG002
|
374
|
+
permissions_info: Any,
|
375
|
+
obj: Model | None = None,
|
376
|
+
) -> None:
|
377
|
+
"""
|
378
|
+
Main method to check permissions for a given user and object.
|
379
|
+
|
380
|
+
This method prepopulates default permissions, checks class-level permissions,
|
381
|
+
and object-level permissions for the given user and object.
|
382
|
+
|
383
|
+
Args:
|
384
|
+
object_class (type[Model]): The class of the object to check permissions for.
|
385
|
+
user (Any): The user to check permissions for.
|
386
|
+
access_types (list[Any]): The list of access types to check.
|
387
|
+
permissions_info (Any): The permissions information object to update.
|
388
|
+
obj (Model | None): The object to check permissions for, if any.
|
389
|
+
"""
|
390
|
+
await self._async_prepopulate_default_permissions(object_class, permissions_info)
|
391
|
+
await self._async_check_class_permissions(object_class, user, permissions_info)
|
392
|
+
|
393
|
+
if obj:
|
394
|
+
self._check_object_permissions(obj, user, permissions_info)
|