amsdal 0.3.3__cp311-cp311-macosx_10_9_universal2.whl → 0.5.29__cp311-cp311-macosx_10_9_universal2.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 +56 -2
- amsdal/__about__.py +1 -1
- amsdal/__init__.py +20 -0
- amsdal/__init__.pyi +9 -0
- amsdal/__migrations__/0000_initial.py +23 -190
- amsdal/__migrations__/0001_create_class_file.py +61 -0
- amsdal/__migrations__/0002_create_class_file.py +109 -0
- amsdal/__migrations__/0003_update_class_file.py +91 -0
- amsdal/__migrations__/0004_update_class_file.py +45 -0
- amsdal/cloud/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/client.cpython-311-darwin.so +0 -0
- amsdal/cloud/constants.cpython-311-darwin.so +0 -0
- amsdal/cloud/enums.cpython-311-darwin.so +0 -0
- amsdal/cloud/models/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/models/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_dependency.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_secret.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_env.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_session.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_env.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_secret.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/expose_db.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_deploys.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_envs.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_secrets.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/manager.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/signup_action.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/update_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/credentials.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/credentials.pyi +0 -1
- amsdal/cloud/services/auth/manager.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/signup_service.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/token.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/token.pyi +0 -1
- amsdal/configs/main.py +40 -20
- amsdal/configs/main.pyi +19 -18
- amsdal/contrib/__init__.cpython-311-darwin.so +0 -0
- amsdal/contrib/auth/errors.py +36 -0
- amsdal/contrib/auth/errors.pyi +12 -0
- amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
- amsdal/contrib/auth/lifecycle/consumer.py +13 -13
- amsdal/contrib/auth/lifecycle/consumer.pyi +3 -0
- amsdal/contrib/auth/migrations/0000_initial.py +69 -31
- amsdal/contrib/auth/migrations/0001_add_mfa_support.py +188 -0
- amsdal/contrib/auth/models/__init__.py +1 -0
- amsdal/contrib/auth/models/backup_code.py +85 -0
- amsdal/contrib/auth/models/email_mfa_device.py +108 -0
- amsdal/contrib/auth/models/login_session.py +235 -0
- amsdal/contrib/auth/models/mfa_device.py +86 -0
- amsdal/contrib/auth/models/permission.py +23 -0
- amsdal/contrib/auth/models/sms_device.py +113 -0
- amsdal/contrib/auth/models/totp_device.py +58 -0
- amsdal/contrib/auth/models/user.py +156 -0
- amsdal/contrib/auth/services/__init__.py +1 -0
- amsdal/contrib/auth/services/mfa_device_service.py +544 -0
- amsdal/contrib/auth/services/mfa_device_service.pyi +216 -0
- amsdal/contrib/auth/services/totp_service.py +358 -0
- amsdal/contrib/auth/services/totp_service.pyi +158 -0
- amsdal/contrib/auth/settings.py +8 -0
- amsdal/contrib/auth/settings.pyi +8 -0
- amsdal/contrib/auth/transactions/__init__.py +1 -0
- amsdal/contrib/auth/transactions/mfa_device_transactions.py +463 -0
- amsdal/contrib/auth/transactions/mfa_device_transactions.pyi +226 -0
- amsdal/contrib/auth/transactions/totp_transactions.py +206 -0
- amsdal/contrib/auth/transactions/totp_transactions.pyi +113 -0
- amsdal/contrib/auth/utils/__init__.py +0 -0
- amsdal/contrib/auth/utils/__init__.pyi +0 -0
- amsdal/contrib/auth/utils/mfa.py +257 -0
- amsdal/contrib/auth/utils/mfa.pyi +119 -0
- amsdal/contrib/frontend_configs/conversion/convert.py +85 -25
- amsdal/contrib/frontend_configs/conversion/convert.pyi +0 -1
- amsdal/contrib/frontend_configs/lifecycle/consumer.py +32 -13
- amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +1 -1
- amsdal/contrib/frontend_configs/migrations/0000_initial.py +167 -195
- amsdal/contrib/frontend_configs/migrations/0001_update_frontend_control_config.py +245 -0
- amsdal/contrib/frontend_configs/migrations/0002_add_button_and_invoke_actions.py +352 -0
- amsdal/contrib/frontend_configs/migrations/0003_create_class_frontendconfigdashboardelement.py +145 -0
- amsdal/contrib/frontend_configs/models/__init__.py +0 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +110 -0
- amsdal/contrib/frontend_configs/models/frontend_config_dashboard.py +51 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.py +110 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
- amsdal/errors.py +0 -3
- amsdal/errors.pyi +0 -1
- amsdal/fixtures/__init__.cpython-311-darwin.so +0 -0
- amsdal/fixtures/manager.cpython-311-darwin.so +0 -0
- amsdal/fixtures/manager.pyi +73 -123
- amsdal/fixtures/utils.cpython-311-darwin.so +0 -0
- amsdal/fixtures/utils.pyi +9 -0
- amsdal/manager.cpython-311-darwin.so +0 -0
- amsdal/manager.pyi +9 -96
- amsdal/mixins/__init__.cpython-311-darwin.so +0 -0
- amsdal/mixins/class_versions_mixin.cpython-311-darwin.so +0 -0
- amsdal/models/__init__.py +19 -0
- amsdal/models/core/__init__.py +0 -0
- amsdal/models/core/class_object.py +38 -0
- amsdal/models/core/class_property.py +26 -0
- amsdal/models/core/file.py +243 -0
- amsdal/models/core/fixture.py +25 -0
- amsdal/models/core/option.py +11 -0
- amsdal/models/core/storage_metadata.py +15 -0
- amsdal/models/core/validator.py +12 -0
- amsdal/models/mixins.py +31 -0
- amsdal/models/types/__init__.py +0 -0
- amsdal/models/types/object.py +26 -0
- amsdal/queryset/__init__.py +21 -0
- amsdal/queryset/__init__.pyi +6 -0
- amsdal/schemas/core/class_object/model.json +20 -0
- amsdal/schemas/core/class_property/model.json +19 -0
- amsdal/schemas/core/file/properties/from_file.py +1 -1
- amsdal/schemas/core/file/properties/validate_data.py +3 -4
- amsdal/schemas/core/storage_metadata/model.json +52 -0
- amsdal/schemas/interfaces.py +25 -0
- amsdal/schemas/interfaces.pyi +20 -0
- amsdal/schemas/manager.cpython-311-darwin.so +0 -0
- amsdal/schemas/manager.py +0 -116
- amsdal/schemas/manager.pyi +0 -65
- amsdal/schemas/mixins/__init__.py +0 -0
- amsdal/schemas/mixins/__init__.pyi +0 -0
- amsdal/schemas/mixins/check_dependencies_mixin.py +130 -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/utils.py +16 -0
- amsdal/schemas/utils.pyi +10 -0
- amsdal/services/__init__.py +11 -0
- amsdal/services/__init__.pyi +4 -0
- amsdal/services/external_connections.py +262 -0
- amsdal/services/external_connections.pyi +190 -0
- amsdal/services/external_model_generator.py +350 -0
- amsdal/services/external_model_generator.pyi +134 -0
- amsdal/services/transaction_execution.cpython-311-darwin.so +0 -0
- amsdal/services/transaction_execution.pyi +2 -1
- amsdal/storages/__init__.py +20 -0
- amsdal/storages/__init__.pyi +8 -0
- amsdal/storages/file_system.py +214 -0
- amsdal/storages/file_system.pyi +36 -0
- amsdal/transactions/__init__.py +13 -0
- amsdal/transactions/__init__.pyi +4 -0
- amsdal/utils/rollback/__init__.py +99 -54
- amsdal/utils/rollback/__init__.pyi +6 -0
- amsdal/utils/tests/enums.py +0 -2
- amsdal/utils/tests/helpers.py +253 -231
- amsdal/utils/tests/migrations.py +157 -0
- {amsdal-0.3.3.dist-info → amsdal-0.5.29.dist-info}/METADATA +17 -10
- amsdal-0.5.29.dist-info/RECORD +276 -0
- {amsdal-0.3.3.dist-info → amsdal-0.5.29.dist-info}/WHEEL +1 -1
- amsdal/__migrations__/0001_datetime_type.py +0 -18
- amsdal/__migrations__/0002_fixture_order.py +0 -40
- amsdal/__migrations__/0003_schema_type_in_class_meta.py +0 -56
- amsdal/contrib/auth/models/login_session/hooks/pre_init.py +0 -68
- amsdal/contrib/auth/models/login_session/model.json +0 -23
- amsdal/contrib/auth/models/login_session/modifiers/display_name.py +0 -11
- amsdal/contrib/auth/models/permission/fixtures/basic_permissions.json +0 -62
- amsdal/contrib/auth/models/permission/model.json +0 -18
- amsdal/contrib/auth/models/permission/modifiers/display_name.py +0 -11
- amsdal/contrib/auth/models/user/hooks/post_init.py +0 -76
- amsdal/contrib/auth/models/user/hooks/pre_create.py +0 -8
- amsdal/contrib/auth/models/user/model.json +0 -25
- amsdal/contrib/auth/models/user/modifiers/display_name.py +0 -19
- amsdal/contrib/frontend_configs/models/frontend_activator_config/model.json +0 -11
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator/model.json +0 -11
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator/model.json +0 -52
- amsdal/contrib/frontend_configs/models/frontend_config_option/model.json +0 -15
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/model.json +0 -6
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/properties/model_dump.py +0 -13
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option/model.json +0 -19
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask/model.json +0 -26
- amsdal/contrib/frontend_configs/models/frontend_config_validator/model.json +0 -41
- amsdal/contrib/frontend_configs/models/frontend_control_config/model.json +0 -250
- amsdal/contrib/frontend_configs/models/frontend_model_config/fixtures/permissions.json +0 -24
- amsdal/contrib/frontend_configs/models/frontend_model_config/model.json +0 -17
- amsdal/contrib/frontend_configs/models/frontent_config_control_action/model.json +0 -54
- amsdal/contrib/frontend_configs/models/frontent_config_control_action/properties/action_validate.py +0 -33
- amsdal/migration/__init__.cpython-311-darwin.so +0 -0
- amsdal/migration/base_migration_schemas.cpython-311-darwin.so +0 -0
- amsdal/migration/base_migration_schemas.pyi +0 -120
- amsdal/migration/data_classes.cpython-311-darwin.so +0 -0
- amsdal/migration/data_classes.pyi +0 -172
- amsdal/migration/executors/__init__.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/base.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/base.pyi +0 -118
- amsdal/migration/executors/default_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/default_executor.pyi +0 -184
- amsdal/migration/executors/state_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/state_executor.pyi +0 -78
- amsdal/migration/file_migration_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_executor.pyi +0 -68
- amsdal/migration/file_migration_generator.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_generator.pyi +0 -139
- amsdal/migration/file_migration_store.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_store.pyi +0 -61
- amsdal/migration/file_migration_writer.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_writer.pyi +0 -73
- amsdal/migration/migrations.cpython-311-darwin.so +0 -0
- amsdal/migration/migrations.pyi +0 -166
- amsdal/migration/migrations_loader.cpython-311-darwin.so +0 -0
- amsdal/migration/migrations_loader.pyi +0 -32
- amsdal/migration/schemas_loaders.cpython-311-darwin.so +0 -0
- amsdal/migration/schemas_loaders.pyi +0 -37
- amsdal/migration/templates/data_migration.tmpl +0 -18
- amsdal/migration/templates/dict_validator.tmpl +0 -4
- amsdal/migration/templates/migration.tmpl +0 -6
- amsdal/migration/templates/model_class.tmpl +0 -8
- amsdal/migration/templates/model_class_layout.tmpl +0 -24
- amsdal/migration/templates/options_validator.tmpl +0 -4
- amsdal/migration/utils.cpython-311-darwin.so +0 -0
- amsdal/migration/utils.pyi +0 -58
- amsdal/mixins/build_mixin.cpython-311-darwin.so +0 -0
- amsdal/mixins/build_mixin.pyi +0 -78
- amsdal/schemas/core/class_object_meta/model.json +0 -59
- amsdal/schemas/core/class_property_meta/model.json +0 -23
- amsdal/services/__init__.cpython-311-darwin.so +0 -0
- amsdal-0.3.3.dist-info/RECORD +0 -257
- amsdal-0.3.3.dist-info/licenses/LICENSE.txt +0 -107
- /amsdal/{migration → contrib/auth/services}/__init__.pyi +0 -0
- /amsdal/{migration/executors → contrib/auth/transactions}/__init__.pyi +0 -0
- {amsdal-0.3.3.dist-info → amsdal-0.5.29.dist-info/licenses}/LICENSE.txt +0 -0
- {amsdal-0.3.3.dist-info → amsdal-0.5.29.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from amsdal_models.migration import migrations
|
|
2
|
+
from amsdal_utils.models.enums import ModuleType
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Migration(migrations.Migration):
|
|
6
|
+
operations: list[migrations.Operation] = [
|
|
7
|
+
migrations.UpdateClass(
|
|
8
|
+
module_type=ModuleType.CORE,
|
|
9
|
+
class_name="File",
|
|
10
|
+
old_schema={
|
|
11
|
+
"title": "File",
|
|
12
|
+
"required": ["filename"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"filename": {"type": "string", "title": "Filename"},
|
|
15
|
+
"data": {"type": "binary", "title": "Data"},
|
|
16
|
+
"size": {"type": "number", "title": "Size"},
|
|
17
|
+
"storage_address": {"type": "anything", "title": "Storage Reference"},
|
|
18
|
+
},
|
|
19
|
+
"custom_code": 'import base64\nimport io\nfrom contextlib import suppress\nfrom pathlib import Path\nfrom typing import IO\nfrom typing import Any\nfrom typing import BinaryIO\n\nfrom amsdal_models.storage.backends.db import DBStorage\nfrom amsdal_models.storage.base import Storage\nfrom pydantic import model_validator\n\n\n@classmethod\ndef data_base64_decode(cls, data: Any) -> bytes:\n if isinstance(data, str):\n data = data.encode(\'utf-8\')\n is_base64: bool = False\n with suppress(Exception):\n is_base64 = base64.b64encode(base64.b64decode(data)) == data\n if is_base64:\n return base64.b64decode(data)\n return data\n\n@classmethod\ndef from_bytes(cls, filename: str, data: bytes) -> \'File\':\n """\n Creates a `File` object from a byte string.\n\n Args:\n filename (str): The filename of the file.\n data (bytes): The byte string containing the file data.:\n\n Returns:\n File: The created `File` object.\n """\n obj = cls(filename=filename, data=data, size=len(data))\n obj._needs_persist = True\n return obj\n\n@classmethod\ndef from_file(cls, file_or_path: Path | BinaryIO) -> \'File\':\n """\n Creates a `File` object from a file path or a binary file object.\n\n Args:\n file_or_path (Path | BinaryIO): The file path or binary file object.\n\n Returns:\n File: The created `File` object.\n\n Raises:\n ValueError: If the provided path is a directory.\n """\n f: BinaryIO | io.BufferedReader\n if isinstance(file_or_path, Path):\n if file_or_path.is_dir():\n msg = f\'{file_or_path} is a directory\'\n raise ValueError(msg)\n f = file_or_path.open(\'rb\')\n filename = file_or_path.name\n size = file_or_path.stat().st_size\n else:\n f = file_or_path\n filename = Path(getattr(f, \'name\', \'unnamed\')).name\n try:\n if f.seekable():\n f.seek(0, io.SEEK_END)\n size = f.tell()\n f.seek(0)\n else:\n size = None\n except (OSError, AttributeError):\n size = None\n obj = cls(filename=filename, size=size)\n obj._source = f\n obj._needs_persist = True\n return obj\n\n@model_validator(mode=\'before\')\n@classmethod\ndef validate_model_data(cls, data: Any) -> Any:\n if isinstance(data, dict):\n if \'data\' in data:\n if data[\'data\']:\n data[\'data\'] = cls.data_base64_decode(data[\'data\'])\n data[\'size\'] = len(data[\'data\'])\n else:\n data[\'size\'] = 0\n return data\n\n@property\ndef mimetype(self) -> str | None:\n """\n Returns the MIME type of the file based on its filename.\n\n This method uses the `mimetypes` module to guess the MIME type of the file.\n\n Returns:\n str | None: The guessed MIME type of the file, or None if it cannot be determined.\n """\n import mimetypes\n return mimetypes.guess_type(self.filename)[0]\n\n@property\ndef storage(self) -> Storage:\n from amsdal.storages import default_storage\n if self._storage:\n return self._storage\n if self.storage_address:\n return Storage.from_storage_spec({\'storage_class\': self.storage_address.ref.resource})\n return default_storage()\n\nasync def aopen(self, mode: str=\'rb\') -> Any:\n """\n Async variant of open().\n\n Uses the resolved storage to call aopen(); if the backend does not implement\n async, falls back to the sync open().\n """\n try:\n return await self.storage.aopen(self, mode)\n except NotImplementedError:\n return self.storage.open(self, mode)\n\nasync def apre_create(self) -> None:\n if self._needs_persist:\n from amsdal_models.storage.persistence import apersist_file\n await apersist_file(self, storage=self.storage)\n\nasync def apre_update(self) -> None:\n if self._needs_persist:\n from amsdal_models.storage.persistence import apersist_file\n await apersist_file(self, storage=self.storage)\n\nasync def aread_bytes(self) -> bytes:\n async with await self.aopen() as f:\n return await f.read()\n\nasync def aurl(self) -> str:\n """\n Async variant of url().\n\n Uses the resolved storage to call aurl(); if the backend does not implement\n async, falls back to the sync url().\n """\n try:\n return await self.storage.aurl(self)\n except NotImplementedError:\n return self.storage.url(self)\n\ndef __repr__(self) -> str:\n return f"File<{self.filename}>({self.size or len(self.data or \'\') or 0} bytes)"\n\ndef __str__(self) -> str:\n return repr(self)\n\ndef open(self, mode: str=\'rb\') -> IO[Any]:\n """\n Open a binary stream for reading (or other modes if supported) using storage_address.\n\n Raises StateError if storage_address is missing.\n """\n return self.storage.open(self, mode)\n\ndef pre_create(self) -> None:\n if self._needs_persist:\n from amsdal_models.storage.persistence import persist_file\n persist_file(self, storage=self.storage)\n\ndef pre_update(self) -> None:\n if self._needs_persist:\n from amsdal_models.storage.persistence import persist_file\n persist_file(self, storage=self.storage)\n\ndef read_bytes(self) -> bytes:\n with self.open() as f:\n return f.read()\n\ndef set_data(self, data: bytes | str) -> None:\n if not isinstance(self.storage, DBStorage):\n msg = \'Cannot set data on a file that is not stored in a database. Use `File.from_bytes` instead.\'\n raise ValueError(msg)\n self.data = self.data_base64_decode(data)\n self.size = len(self.data)\n self._needs_persist = True\n\ndef to_file(self, file_or_path: Path | BinaryIO) -> None:\n """\n Writes the object\'s data to a file path or a binary file object.\n\n Args:\n file_or_path (Path | BinaryIO): The file path or binary file object where the data will be written.\n\n Returns:\n None\n\n Raises:\n ValueError: If the provided path is a directory.\n """\n with self.open() as f:\n if isinstance(file_or_path, Path):\n if file_or_path.is_dir():\n file_or_path = file_or_path / self.name\n file_or_path.write_bytes(f.read())\n else:\n file_or_path.write(f.read())\n file_or_path.seek(0)\n\ndef url(self) -> str:\n """\n Return a URL for this file using its storage_address.\n\n Raises StateError if storage_address is missing.\n """\n return self.storage.url(self)',
|
|
20
|
+
"storage_metadata": {
|
|
21
|
+
"table_name": "File",
|
|
22
|
+
"db_fields": {},
|
|
23
|
+
"primary_key": ["partition_key"],
|
|
24
|
+
"foreign_keys": {},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
new_schema={
|
|
28
|
+
"title": "File",
|
|
29
|
+
"required": ["filename"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"filename": {"type": "string", "title": "Filename"},
|
|
32
|
+
"data": {"type": "binary", "title": "Data"},
|
|
33
|
+
"size": {"type": "number", "title": "Size"},
|
|
34
|
+
"storage_address": {"type": "anything", "title": "Storage Reference"},
|
|
35
|
+
},
|
|
36
|
+
"custom_code": 'import base64\nimport io\nfrom contextlib import suppress\nfrom pathlib import Path\nfrom typing import IO\nfrom typing import Any\nfrom typing import BinaryIO\n\nfrom amsdal_models.storage.backends.db import AsyncFileWrapper\nfrom amsdal_models.storage.backends.db import DBStorage\nfrom amsdal_models.storage.base import Storage\nfrom pydantic import model_validator\n\n\n@classmethod\ndef data_base64_decode(cls, data: Any) -> bytes:\n if isinstance(data, str):\n data = data.encode(\'utf-8\')\n is_base64: bool = False\n with suppress(Exception):\n is_base64 = base64.b64encode(base64.b64decode(data)) == data\n if is_base64:\n return base64.b64decode(data)\n return data\n\n@classmethod\ndef from_bytes(cls, filename: str, data: bytes) -> \'File\':\n """\n Creates a `File` object from a byte string.\n\n Args:\n filename (str): The filename of the file.\n data (bytes): The byte string containing the file data.:\n\n Returns:\n File: The created `File` object.\n """\n obj = cls(filename=filename, data=data, size=len(data))\n return obj\n\n@classmethod\ndef from_file(cls, file_or_path: Path | BinaryIO) -> \'File\':\n """\n Creates a `File` object from a file path or a binary file object.\n\n Args:\n file_or_path (Path | BinaryIO): The file path or binary file object.\n\n Returns:\n File: The created `File` object.\n\n Raises:\n ValueError: If the provided path is a directory.\n """\n f: BinaryIO | io.BufferedReader\n if isinstance(file_or_path, Path):\n if file_or_path.is_dir():\n msg = f\'{file_or_path} is a directory\'\n raise ValueError(msg)\n f = file_or_path.open(\'rb\')\n filename = file_or_path.name\n size = file_or_path.stat().st_size\n else:\n f = file_or_path\n filename = Path(getattr(f, \'name\', \'unnamed\')).name\n try:\n if f.seekable():\n f.seek(0, io.SEEK_END)\n size = f.tell()\n f.seek(0)\n else:\n size = None\n except (OSError, AttributeError):\n size = None\n obj = cls(filename=filename, size=size)\n obj._source = f\n return obj\n\n@model_validator(mode=\'before\')\n@classmethod\ndef validate_model_data(cls, data: Any) -> Any:\n if isinstance(data, dict):\n if \'data\' in data:\n if data[\'data\']:\n data[\'data\'] = cls.data_base64_decode(data[\'data\'])\n data[\'size\'] = len(data[\'data\'])\n else:\n data[\'size\'] = 0\n return data\n\n@property\ndef mimetype(self) -> str | None:\n """\n Returns the MIME type of the file based on its filename.\n\n This method uses the `mimetypes` module to guess the MIME type of the file.\n\n Returns:\n str | None: The guessed MIME type of the file, or None if it cannot be determined.\n """\n import mimetypes\n return mimetypes.guess_type(self.filename)[0]\n\n@property\ndef storage(self) -> Storage:\n from amsdal.storages import default_storage\n if self._storage:\n return self._storage\n if self.storage_address:\n return Storage.from_storage_spec({\'storage_class\': self.storage_address.ref.resource})\n return default_storage()\n\nasync def aopen(self, mode: str=\'rb\') -> Any:\n """\n Async variant of open().\n\n Uses the resolved storage to call aopen(); if the backend does not implement\n async, falls back to the sync open().\n """\n try:\n return await self.storage.aopen(self, mode)\n except NotImplementedError:\n return AsyncFileWrapper(self.storage.open(self, mode))\n\nasync def apre_create(self) -> None:\n from amsdal_models.storage.persistence import apersist_file\n await apersist_file(self, storage=self.storage)\n\nasync def apre_update(self) -> None:\n from amsdal_models.storage.persistence import apersist_file\n await apersist_file(self, storage=self.storage)\n\nasync def aread_bytes(self) -> bytes:\n async with await self.aopen() as f:\n return await f.read()\n\nasync def aurl(self) -> str:\n """\n Async variant of url().\n\n Uses the resolved storage to call aurl(); if the backend does not implement\n async, falls back to the sync url().\n """\n try:\n return await self.storage.aurl(self)\n except NotImplementedError:\n return self.storage.url(self)\n\ndef __repr__(self) -> str:\n return f"File<{self.filename}>({self.size or len(self.data or \'\') or 0} bytes)"\n\ndef __str__(self) -> str:\n return repr(self)\n\ndef open(self, mode: str=\'rb\') -> IO[Any]:\n """\n Open a binary stream for reading (or other modes if supported) using storage_address.\n\n Raises StateError if storage_address is missing.\n """\n return self.storage.open(self, mode)\n\ndef pre_create(self) -> None:\n from amsdal_models.storage.persistence import persist_file\n persist_file(self, storage=self.storage)\n\ndef pre_update(self) -> None:\n from amsdal_models.storage.persistence import persist_file\n persist_file(self, storage=self.storage)\n\ndef read_bytes(self) -> bytes:\n with self.open() as f:\n return f.read()\n\ndef set_data(self, data: bytes | str) -> None:\n if not isinstance(self.storage, DBStorage):\n msg = \'Cannot set data on a file that is not stored in a database. Use `File.from_bytes` instead.\'\n raise ValueError(msg)\n self.data = self.data_base64_decode(data)\n self.size = len(self.data)\n\ndef to_file(self, file_or_path: Path | BinaryIO) -> None:\n """\n Writes the object\'s data to a file path or a binary file object.\n\n Args:\n file_or_path (Path | BinaryIO): The file path or binary file object where the data will be written.\n\n Returns:\n None\n\n Raises:\n ValueError: If the provided path is a directory.\n """\n with self.open() as f:\n if isinstance(file_or_path, Path):\n if file_or_path.is_dir():\n file_or_path = file_or_path / self.name\n file_or_path.write_bytes(f.read())\n else:\n file_or_path.write(f.read())\n file_or_path.seek(0)\n\ndef url(self) -> str:\n """\n Return a URL for this file using its storage_address.\n\n Raises StateError if storage_address is missing.\n """\n return self.storage.url(self)',
|
|
37
|
+
"storage_metadata": {
|
|
38
|
+
"table_name": "File",
|
|
39
|
+
"db_fields": {},
|
|
40
|
+
"primary_key": ["partition_key"],
|
|
41
|
+
"foreign_keys": {},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
),
|
|
45
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,7 +6,6 @@ from amsdal.cloud.services.actions.create_session import CreateSessionAction as
|
|
|
6
6
|
from amsdal.cloud.services.auth.base import AuthHandlerBase as AuthHandlerBase
|
|
7
7
|
from amsdal.cloud.services.auth.token import TokenAuthHandler as TokenAuthHandler
|
|
8
8
|
from amsdal.errors import AmsdalAuthenticationError as AmsdalAuthenticationError
|
|
9
|
-
from amsdal.schemas.manager import SchemaManager as SchemaManager
|
|
10
9
|
|
|
11
10
|
class CredentialsAuthHandler(AuthHandlerBase):
|
|
12
11
|
"""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,7 +2,6 @@ from _typeshed import Incomplete
|
|
|
2
2
|
from amsdal.cloud.constants import JWT_PUBLIC_KEY as JWT_PUBLIC_KEY
|
|
3
3
|
from amsdal.cloud.services.auth.base import AuthHandlerBase as AuthHandlerBase
|
|
4
4
|
from amsdal.errors import AmsdalAuthenticationError as AmsdalAuthenticationError
|
|
5
|
-
from amsdal.schemas.manager import SchemaManager as SchemaManager
|
|
6
5
|
|
|
7
6
|
HMAC_KEY: bytes
|
|
8
7
|
|
amsdal/configs/main.py
CHANGED
|
@@ -48,20 +48,33 @@ class Settings(BaseSettings):
|
|
|
48
48
|
|
|
49
49
|
APP_PATH: Path = Path('.')
|
|
50
50
|
CONFIG_PATH: Path | None = None
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
TYPE_MODELS_MODULE: str = 'amsdal.models.types'
|
|
52
|
+
CORE_MODELS_MODULE: str = 'amsdal.models.core'
|
|
53
|
+
USER_MODELS_MODULE_PATH: Path | None = None
|
|
54
|
+
USER_MODELS_MODULE: str = 'models'
|
|
55
|
+
FIXTURES_MODULE_PATH: Path | None = None
|
|
53
56
|
FIXTURES_MODULE_NAME: str = 'fixtures'
|
|
57
|
+
STATIC_MODULE_PATH: Path | None = None
|
|
54
58
|
STATIC_MODULE_NAME: str = 'static'
|
|
59
|
+
TRANSACTIONS_MODULE_PATH: Path | None = None
|
|
55
60
|
TRANSACTIONS_MODULE_NAME: str = 'transactions'
|
|
61
|
+
MIGRATIONS_MODULE_PATH: Path | None = None
|
|
56
62
|
MIGRATIONS_DIRECTORY_NAME: str = 'migrations'
|
|
57
63
|
ACCESS_KEY_ID: str | None = None
|
|
58
64
|
SECRET_ACCESS_KEY: str | None = None
|
|
59
65
|
ACCESS_TOKEN: str | None = None
|
|
60
66
|
SANDBOX_ENVIRONMENT: bool | None = None
|
|
61
|
-
CONTRIBS: list[str] = [
|
|
67
|
+
CONTRIBS: list[str] | str = [
|
|
62
68
|
'amsdal.contrib.auth.app.AuthAppConfig',
|
|
63
69
|
'amsdal.contrib.frontend_configs.app.FrontendConfigAppConfig',
|
|
64
70
|
]
|
|
71
|
+
CONTRIB_MODELS_PACKAGE_NAME: str = 'models'
|
|
72
|
+
CONTRIB_MIGRATIONS_DIRECTORY_NAME: str = 'migrations'
|
|
73
|
+
|
|
74
|
+
# File Storage
|
|
75
|
+
MEDIA_ROOT: Path = Path('./media')
|
|
76
|
+
MEDIA_URL: str = '/media/'
|
|
77
|
+
DEFAULT_FILE_STORAGE: str = 'amsdal_models.storage.backends.db.DBStorage'
|
|
65
78
|
|
|
66
79
|
@field_validator('CONTRIBS', mode='after')
|
|
67
80
|
def load_contrib_modules(cls, value: list[str]) -> list[str]: # noqa: N805
|
|
@@ -88,7 +101,7 @@ class Settings(BaseSettings):
|
|
|
88
101
|
return value
|
|
89
102
|
|
|
90
103
|
@property
|
|
91
|
-
def
|
|
104
|
+
def user_models_path(self) -> Path:
|
|
92
105
|
"""
|
|
93
106
|
Returns the root path for models.
|
|
94
107
|
|
|
@@ -98,20 +111,16 @@ class Settings(BaseSettings):
|
|
|
98
111
|
Returns:
|
|
99
112
|
Path: The root path for the models directory.
|
|
100
113
|
"""
|
|
101
|
-
|
|
114
|
+
if self.USER_MODELS_MODULE_PATH:
|
|
115
|
+
return self.USER_MODELS_MODULE_PATH
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Returns the root path for schemas.
|
|
117
|
+
if '.' in self.USER_MODELS_MODULE:
|
|
118
|
+
_package, _ = self.USER_MODELS_MODULE.split('.', 1)
|
|
119
|
+
_module = importlib.import_module(_package)
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
based on the `APP_PATH` and `SCHEMAS_MODULE_NAME` attributes.
|
|
121
|
+
return Path(_module.__path__[0])
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
Path: The root path for the schemas directory.
|
|
113
|
-
"""
|
|
114
|
-
return self.APP_PATH / self.SCHEMAS_MODULE_NAME
|
|
123
|
+
return self.APP_PATH / self.USER_MODELS_MODULE
|
|
115
124
|
|
|
116
125
|
@property
|
|
117
126
|
def fixtures_root_path(self) -> Path:
|
|
@@ -124,7 +133,7 @@ class Settings(BaseSettings):
|
|
|
124
133
|
Returns:
|
|
125
134
|
Path: The root path for the fixtures directory.
|
|
126
135
|
"""
|
|
127
|
-
return self.APP_PATH / self.FIXTURES_MODULE_NAME
|
|
136
|
+
return self.FIXTURES_MODULE_PATH or self.APP_PATH / self.FIXTURES_MODULE_NAME
|
|
128
137
|
|
|
129
138
|
@property
|
|
130
139
|
def static_root_path(self) -> Path:
|
|
@@ -137,7 +146,7 @@ class Settings(BaseSettings):
|
|
|
137
146
|
Returns:
|
|
138
147
|
Path: The root path for the static files directory.
|
|
139
148
|
"""
|
|
140
|
-
return self.APP_PATH / self.STATIC_MODULE_NAME
|
|
149
|
+
return self.STATIC_MODULE_PATH or self.APP_PATH / self.STATIC_MODULE_NAME
|
|
141
150
|
|
|
142
151
|
@property
|
|
143
152
|
def transactions_root_path(self) -> Path:
|
|
@@ -150,7 +159,7 @@ class Settings(BaseSettings):
|
|
|
150
159
|
Returns:
|
|
151
160
|
Path: The root path for the transactions directory.
|
|
152
161
|
"""
|
|
153
|
-
return self.
|
|
162
|
+
return self.TRANSACTIONS_MODULE_PATH or self.APP_PATH / self.TRANSACTIONS_MODULE_NAME
|
|
154
163
|
|
|
155
164
|
@property
|
|
156
165
|
def migrations_root_path(self) -> Path:
|
|
@@ -163,10 +172,10 @@ class Settings(BaseSettings):
|
|
|
163
172
|
Returns:
|
|
164
173
|
Path: The root path for the migrations directory.
|
|
165
174
|
"""
|
|
166
|
-
return self.
|
|
175
|
+
return self.MIGRATIONS_MODULE_PATH or self.APP_PATH / self.MIGRATIONS_DIRECTORY_NAME
|
|
167
176
|
|
|
168
177
|
@model_validator(mode='after')
|
|
169
|
-
def
|
|
178
|
+
def check_config_path_set(self) -> 'Settings':
|
|
170
179
|
"""
|
|
171
180
|
Ensures the configuration path is set.
|
|
172
181
|
|
|
@@ -183,6 +192,17 @@ class Settings(BaseSettings):
|
|
|
183
192
|
|
|
184
193
|
return self
|
|
185
194
|
|
|
195
|
+
@model_validator(mode='before')
|
|
196
|
+
@classmethod
|
|
197
|
+
def normalize_complex_values(cls, data: Any) -> Any:
|
|
198
|
+
if isinstance(data, dict):
|
|
199
|
+
if 'CONTRIBS' in data:
|
|
200
|
+
contribs = data['CONTRIBS']
|
|
201
|
+
|
|
202
|
+
if isinstance(contribs, str) and '[' not in contribs:
|
|
203
|
+
data['CONTRIBS'] = [item.strip() for item in contribs.split(',')]
|
|
204
|
+
return data
|
|
205
|
+
|
|
186
206
|
|
|
187
207
|
if TYPE_CHECKING:
|
|
188
208
|
base: TypeAlias = Settings
|
amsdal/configs/main.pyi
CHANGED
|
@@ -33,17 +33,28 @@ class Settings(BaseSettings):
|
|
|
33
33
|
model_config: Incomplete
|
|
34
34
|
APP_PATH: Path
|
|
35
35
|
CONFIG_PATH: Path | None
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
TYPE_MODELS_MODULE: str
|
|
37
|
+
CORE_MODELS_MODULE: str
|
|
38
|
+
USER_MODELS_MODULE_PATH: Path | None
|
|
39
|
+
USER_MODELS_MODULE: str
|
|
40
|
+
FIXTURES_MODULE_PATH: Path | None
|
|
38
41
|
FIXTURES_MODULE_NAME: str
|
|
42
|
+
STATIC_MODULE_PATH: Path | None
|
|
39
43
|
STATIC_MODULE_NAME: str
|
|
44
|
+
TRANSACTIONS_MODULE_PATH: Path | None
|
|
40
45
|
TRANSACTIONS_MODULE_NAME: str
|
|
46
|
+
MIGRATIONS_MODULE_PATH: Path | None
|
|
41
47
|
MIGRATIONS_DIRECTORY_NAME: str
|
|
42
48
|
ACCESS_KEY_ID: str | None
|
|
43
49
|
SECRET_ACCESS_KEY: str | None
|
|
44
50
|
ACCESS_TOKEN: str | None
|
|
45
51
|
SANDBOX_ENVIRONMENT: bool | None
|
|
46
|
-
CONTRIBS: list[str]
|
|
52
|
+
CONTRIBS: list[str] | str
|
|
53
|
+
CONTRIB_MODELS_PACKAGE_NAME: str
|
|
54
|
+
CONTRIB_MIGRATIONS_DIRECTORY_NAME: str
|
|
55
|
+
MEDIA_ROOT: Path
|
|
56
|
+
MEDIA_URL: str
|
|
57
|
+
DEFAULT_FILE_STORAGE: str
|
|
47
58
|
def load_contrib_modules(cls, value: list[str]) -> list[str]:
|
|
48
59
|
"""
|
|
49
60
|
Loads and initializes contrib modules.
|
|
@@ -58,7 +69,7 @@ class Settings(BaseSettings):
|
|
|
58
69
|
list[str]: The same list of contrib module paths after loading and initializing the modules.
|
|
59
70
|
"""
|
|
60
71
|
@property
|
|
61
|
-
def
|
|
72
|
+
def user_models_path(self) -> Path:
|
|
62
73
|
"""
|
|
63
74
|
Returns the root path for models.
|
|
64
75
|
|
|
@@ -69,17 +80,6 @@ class Settings(BaseSettings):
|
|
|
69
80
|
Path: The root path for the models directory.
|
|
70
81
|
"""
|
|
71
82
|
@property
|
|
72
|
-
def schemas_root_path(self) -> Path:
|
|
73
|
-
"""
|
|
74
|
-
Returns the root path for schemas.
|
|
75
|
-
|
|
76
|
-
This property constructs and returns the path to the schemas directory
|
|
77
|
-
based on the `APP_PATH` and `SCHEMAS_MODULE_NAME` attributes.
|
|
78
|
-
|
|
79
|
-
Returns:
|
|
80
|
-
Path: The root path for the schemas directory.
|
|
81
|
-
"""
|
|
82
|
-
@property
|
|
83
83
|
def fixtures_root_path(self) -> Path:
|
|
84
84
|
"""
|
|
85
85
|
Returns the root path for fixtures.
|
|
@@ -123,7 +123,7 @@ class Settings(BaseSettings):
|
|
|
123
123
|
Returns:
|
|
124
124
|
Path: The root path for the migrations directory.
|
|
125
125
|
"""
|
|
126
|
-
def
|
|
126
|
+
def check_config_path_set(self) -> Settings:
|
|
127
127
|
"""
|
|
128
128
|
Ensures the configuration path is set.
|
|
129
129
|
|
|
@@ -133,8 +133,9 @@ class Settings(BaseSettings):
|
|
|
133
133
|
Returns:
|
|
134
134
|
Settings: The updated settings instance with the `CONFIG_PATH` attribute set.
|
|
135
135
|
"""
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
@classmethod
|
|
137
|
+
def normalize_complex_values(cls, data: Any) -> Any: ...
|
|
138
|
+
base: TypeAlias = Settings
|
|
138
139
|
|
|
139
140
|
class SettingsProxy(base):
|
|
140
141
|
"""
|
|
Binary file
|
amsdal/contrib/auth/errors.py
CHANGED
|
@@ -5,3 +5,39 @@ class UserCreationError(AmsdalError): ...
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class AuthenticationError(AmsdalError): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MFARequiredError(AuthenticationError):
|
|
11
|
+
"""Raised when MFA verification is required but not provided."""
|
|
12
|
+
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InvalidMFACodeError(AuthenticationError):
|
|
17
|
+
"""Raised when the provided MFA code is invalid."""
|
|
18
|
+
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MFADeviceNotFoundError(AmsdalError):
|
|
23
|
+
"""Raised when no valid MFA device is found for the user."""
|
|
24
|
+
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MFASetupError(AmsdalError):
|
|
29
|
+
"""Raised when there's an error during MFA device setup."""
|
|
30
|
+
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PermissionDeniedError(AmsdalError):
|
|
35
|
+
"""Raised when a user lacks permission for an operation."""
|
|
36
|
+
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class UserNotFoundError(AmsdalError):
|
|
41
|
+
"""Raised when a target user cannot be found."""
|
|
42
|
+
|
|
43
|
+
...
|
amsdal/contrib/auth/errors.pyi
CHANGED
|
@@ -2,3 +2,15 @@ from amsdal_utils.errors import AmsdalError
|
|
|
2
2
|
|
|
3
3
|
class UserCreationError(AmsdalError): ...
|
|
4
4
|
class AuthenticationError(AmsdalError): ...
|
|
5
|
+
class MFARequiredError(AuthenticationError):
|
|
6
|
+
"""Raised when MFA verification is required but not provided."""
|
|
7
|
+
class InvalidMFACodeError(AuthenticationError):
|
|
8
|
+
"""Raised when the provided MFA code is invalid."""
|
|
9
|
+
class MFADeviceNotFoundError(AmsdalError):
|
|
10
|
+
"""Raised when no valid MFA device is found for the user."""
|
|
11
|
+
class MFASetupError(AmsdalError):
|
|
12
|
+
"""Raised when there's an error during MFA device setup."""
|
|
13
|
+
class PermissionDeniedError(AmsdalError):
|
|
14
|
+
"""Raised when a user lacks permission for an operation."""
|
|
15
|
+
class UserNotFoundError(AmsdalError):
|
|
16
|
+
"""Raised when a target user cannot be found."""
|
|
@@ -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
|
+
}
|
|
@@ -32,9 +32,9 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
32
32
|
in the authentication settings. If the super user does not exist, it creates one
|
|
33
33
|
with the necessary permissions.
|
|
34
34
|
"""
|
|
35
|
+
from amsdal.contrib.auth.models.permission import Permission
|
|
36
|
+
from amsdal.contrib.auth.models.user import User
|
|
35
37
|
from amsdal.contrib.auth.settings import auth_settings
|
|
36
|
-
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
37
|
-
from models.contrib.user import User # type: ignore[import-not-found]
|
|
38
38
|
|
|
39
39
|
logger.info('Ensure super user exists')
|
|
40
40
|
|
|
@@ -63,9 +63,9 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
63
63
|
.execute()
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
instance = User(
|
|
66
|
+
instance = User( # type: ignore[call-arg]
|
|
67
67
|
email=auth_settings.ADMIN_USER_EMAIL,
|
|
68
|
-
password=auth_settings.ADMIN_USER_PASSWORD,
|
|
68
|
+
password=auth_settings.ADMIN_USER_PASSWORD.encode(),
|
|
69
69
|
permissions=[access_all_permission],
|
|
70
70
|
)
|
|
71
71
|
instance.save(force_insert=True)
|
|
@@ -80,9 +80,9 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
80
80
|
in the authentication settings. If the super user does not exist, it creates one
|
|
81
81
|
with the necessary permissions.
|
|
82
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]
|
|
83
85
|
from amsdal.contrib.auth.settings import auth_settings
|
|
84
|
-
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
85
|
-
from models.contrib.user import User # type: ignore[import-not-found]
|
|
86
86
|
|
|
87
87
|
logger.info('Ensure super user exists')
|
|
88
88
|
|
|
@@ -111,12 +111,12 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
111
111
|
.aexecute()
|
|
112
112
|
)
|
|
113
113
|
|
|
114
|
-
instance = User(
|
|
114
|
+
instance = User( # type: ignore[call-arg]
|
|
115
115
|
email=auth_settings.ADMIN_USER_EMAIL,
|
|
116
|
-
password=auth_settings.ADMIN_USER_PASSWORD,
|
|
116
|
+
password=auth_settings.ADMIN_USER_PASSWORD.encode(),
|
|
117
117
|
permissions=[access_all_permission],
|
|
118
118
|
)
|
|
119
|
-
await instance.asave(force_insert=True)
|
|
119
|
+
await instance.asave(force_insert=True) # type: ignore[misc]
|
|
120
120
|
logger.info('Super user created successfully')
|
|
121
121
|
|
|
122
122
|
|
|
@@ -141,8 +141,8 @@ class AuthenticateUserConsumer(LifecycleConsumer):
|
|
|
141
141
|
auth_header (str): The JWT token from the authorization header.
|
|
142
142
|
authentication_info (Any): The authentication information object to update with the user.
|
|
143
143
|
"""
|
|
144
|
+
from amsdal.contrib.auth.models.user import User # type: ignore[import-not-found]
|
|
144
145
|
from amsdal.contrib.auth.settings import auth_settings
|
|
145
|
-
from models.contrib.user import User # type: ignore[import-not-found]
|
|
146
146
|
|
|
147
147
|
authentication_info.user = None
|
|
148
148
|
email: str | None
|
|
@@ -181,8 +181,8 @@ class AuthenticateUserConsumer(LifecycleConsumer):
|
|
|
181
181
|
auth_header (str): The JWT token from the authorization header.
|
|
182
182
|
authentication_info (Any): The authentication information object to update with the user.
|
|
183
183
|
"""
|
|
184
|
+
from amsdal.contrib.auth.models.user import User # type: ignore[import-not-found]
|
|
184
185
|
from amsdal.contrib.auth.settings import auth_settings
|
|
185
|
-
from models.contrib.user import User # type: ignore[import-not-found]
|
|
186
186
|
|
|
187
187
|
authentication_info.user = None
|
|
188
188
|
email: str | None
|
|
@@ -219,8 +219,8 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
219
219
|
"""
|
|
220
220
|
|
|
221
221
|
def _prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None:
|
|
222
|
+
from amsdal.contrib.auth.models.permission import Permission
|
|
222
223
|
from amsdal.contrib.auth.settings import auth_settings
|
|
223
|
-
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
224
224
|
|
|
225
225
|
permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
|
226
226
|
permissions_info.has_create_permission = (
|
|
@@ -245,8 +245,8 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
245
245
|
permissions_info.has_delete_permission = False
|
|
246
246
|
|
|
247
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]
|
|
248
249
|
from amsdal.contrib.auth.settings import auth_settings
|
|
249
|
-
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
250
250
|
|
|
251
251
|
permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
|
252
252
|
permissions_info.has_create_permission = (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from _typeshed import Incomplete
|
|
2
2
|
from amsdal.contrib.auth.errors import AuthenticationError as AuthenticationError
|
|
3
|
+
from amsdal_data.transactions.decorators import async_transaction, transaction
|
|
3
4
|
from amsdal_models.classes.model import Model
|
|
4
5
|
from amsdal_utils.lifecycle.consumer import LifecycleConsumer
|
|
5
6
|
from typing import Any
|
|
@@ -13,6 +14,7 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
13
14
|
This consumer checks if a super user exists based on the provided email and password
|
|
14
15
|
in the authentication settings. If the super user does not exist, it creates one.
|
|
15
16
|
"""
|
|
17
|
+
@transaction
|
|
16
18
|
def on_event(self) -> None:
|
|
17
19
|
"""
|
|
18
20
|
Checks for the existence of a super user and creates one if necessary.
|
|
@@ -21,6 +23,7 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
21
23
|
in the authentication settings. If the super user does not exist, it creates one
|
|
22
24
|
with the necessary permissions.
|
|
23
25
|
"""
|
|
26
|
+
@async_transaction
|
|
24
27
|
async def on_event_async(self) -> None:
|
|
25
28
|
"""
|
|
26
29
|
Checks for the existence of a super user and creates one if necessary.
|