amsdal 0.1.0__cp311-cp311-macosx_10_9_universal2.whl → 0.1.2__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.

Potentially problematic release.


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

Files changed (95) hide show
  1. amsdal/__about__.py +1 -1
  2. amsdal/__migrations__/0000_initial.py +203 -0
  3. amsdal/cloud/__init__.cpython-311-darwin.so +0 -0
  4. amsdal/cloud/client.cpython-311-darwin.so +0 -0
  5. amsdal/cloud/constants.cpython-311-darwin.so +0 -0
  6. amsdal/cloud/enums.cpython-311-darwin.so +0 -0
  7. amsdal/cloud/models/__init__.cpython-311-darwin.so +0 -0
  8. amsdal/cloud/models/base.cpython-311-darwin.so +0 -0
  9. amsdal/cloud/services/__init__.cpython-311-darwin.so +0 -0
  10. amsdal/cloud/services/actions/__init__.cpython-311-darwin.so +0 -0
  11. amsdal/cloud/services/actions/add_dependency.cpython-311-darwin.so +0 -0
  12. amsdal/cloud/services/actions/add_secret.cpython-311-darwin.so +0 -0
  13. amsdal/cloud/services/actions/base.cpython-311-darwin.so +0 -0
  14. amsdal/cloud/services/actions/create_deploy.cpython-311-darwin.so +0 -0
  15. amsdal/cloud/services/actions/create_session.cpython-311-darwin.so +0 -0
  16. amsdal/cloud/services/actions/delete_dependency.cpython-311-darwin.so +0 -0
  17. amsdal/cloud/services/actions/delete_secret.cpython-311-darwin.so +0 -0
  18. amsdal/cloud/services/actions/destroy_deploy.cpython-311-darwin.so +0 -0
  19. amsdal/cloud/services/actions/expose_db.cpython-311-darwin.so +0 -0
  20. amsdal/cloud/services/actions/list_dependencies.cpython-311-darwin.so +0 -0
  21. amsdal/cloud/services/actions/list_deploys.cpython-311-darwin.so +0 -0
  22. amsdal/cloud/services/actions/list_secrets.cpython-311-darwin.so +0 -0
  23. amsdal/cloud/services/actions/manager.cpython-311-darwin.so +0 -0
  24. amsdal/cloud/services/actions/signup_action.cpython-311-darwin.so +0 -0
  25. amsdal/cloud/services/actions/update_deploy.cpython-311-darwin.so +0 -0
  26. amsdal/cloud/services/auth/__init__.cpython-311-darwin.so +0 -0
  27. amsdal/cloud/services/auth/base.cpython-311-darwin.so +0 -0
  28. amsdal/cloud/services/auth/credentials.cpython-311-darwin.so +0 -0
  29. amsdal/cloud/services/auth/manager.cpython-311-darwin.so +0 -0
  30. amsdal/cloud/services/auth/signup_service.cpython-311-darwin.so +0 -0
  31. amsdal/cloud/services/auth/token.cpython-311-darwin.so +0 -0
  32. amsdal/contrib/__init__.cpython-311-darwin.so +0 -0
  33. amsdal/contrib/auth/migrations/0000_initial.py +49 -0
  34. amsdal/contrib/auth/models/login_session/hooks/pre_init.py +51 -0
  35. amsdal/contrib/auth/models/login_session/model.json +23 -0
  36. amsdal/contrib/auth/models/login_session/modifiers/display_name.py +3 -0
  37. amsdal/contrib/auth/models/permission/fixtures/basic_permissions.json +62 -0
  38. amsdal/contrib/auth/models/permission/model.json +18 -0
  39. amsdal/contrib/auth/models/permission/modifiers/display_name.py +3 -0
  40. amsdal/contrib/auth/models/user/hooks/pre_create.py +2 -0
  41. amsdal/contrib/auth/models/user/hooks/pre_init.py +25 -0
  42. amsdal/contrib/auth/models/user/model.json +25 -0
  43. amsdal/contrib/auth/models/user/modifiers/display_name.py +11 -0
  44. amsdal/contrib/frontend_configs/migrations/0000_initial.py +255 -0
  45. amsdal/contrib/frontend_configs/models/frontend_activator_config/model.json +11 -0
  46. amsdal/contrib/frontend_configs/models/frontend_config_async_validator/model.json +11 -0
  47. amsdal/contrib/frontend_configs/models/frontend_config_group_validator/model.json +52 -0
  48. amsdal/contrib/frontend_configs/models/frontend_config_option/model.json +15 -0
  49. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/model.json +6 -0
  50. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base/properties/model_dump.py +13 -0
  51. amsdal/contrib/frontend_configs/models/frontend_config_slider_option/model.json +19 -0
  52. amsdal/contrib/frontend_configs/models/frontend_config_text_mask/model.json +26 -0
  53. amsdal/contrib/frontend_configs/models/frontend_config_validator/model.json +41 -0
  54. amsdal/contrib/frontend_configs/models/frontend_control_config/model.json +250 -0
  55. amsdal/contrib/frontend_configs/models/frontend_model_config/model.json +17 -0
  56. amsdal/contrib/frontend_configs/models/frontent_config_control_action/model.json +54 -0
  57. amsdal/contrib/frontend_configs/models/frontent_config_control_action/properties/action_validate.py +17 -0
  58. amsdal/fixtures/__init__.cpython-311-darwin.so +0 -0
  59. amsdal/fixtures/manager.cpython-311-darwin.so +0 -0
  60. amsdal/manager.cpython-311-darwin.so +0 -0
  61. amsdal/migration/__init__.cpython-311-darwin.so +0 -0
  62. amsdal/migration/base_migration_schemas.cpython-311-darwin.so +0 -0
  63. amsdal/migration/base_migration_schemas.pyi +2 -2
  64. amsdal/migration/data_classes.cpython-311-darwin.so +0 -0
  65. amsdal/migration/executors/__init__.cpython-311-darwin.so +0 -0
  66. amsdal/migration/executors/base.cpython-311-darwin.so +0 -0
  67. amsdal/migration/executors/default_executor.cpython-311-darwin.so +0 -0
  68. amsdal/migration/executors/state_executor.cpython-311-darwin.so +0 -0
  69. amsdal/migration/file_migration_executor.cpython-311-darwin.so +0 -0
  70. amsdal/migration/file_migration_generator.cpython-311-darwin.so +0 -0
  71. amsdal/migration/file_migration_store.cpython-311-darwin.so +0 -0
  72. amsdal/migration/file_migration_writer.cpython-311-darwin.so +0 -0
  73. amsdal/migration/migrations.cpython-311-darwin.so +0 -0
  74. amsdal/migration/migrations_loader.cpython-311-darwin.so +0 -0
  75. amsdal/migration/schemas_loaders.cpython-311-darwin.so +0 -0
  76. amsdal/migration/templates/data_migration.tmpl +18 -0
  77. amsdal/migration/templates/dict_validator.tmpl +4 -0
  78. amsdal/migration/templates/migration.tmpl +6 -0
  79. amsdal/migration/templates/model_class.tmpl +8 -0
  80. amsdal/migration/templates/model_class_layout.tmpl +21 -0
  81. amsdal/migration/templates/options_validator.tmpl +4 -0
  82. amsdal/migration/utils.cpython-311-darwin.so +0 -0
  83. amsdal/mixins/__init__.cpython-311-darwin.so +0 -0
  84. amsdal/mixins/build_mixin.cpython-311-darwin.so +0 -0
  85. amsdal/mixins/class_versions_mixin.cpython-311-darwin.so +0 -0
  86. amsdal/operations/__init__.cpython-311-darwin.so +0 -0
  87. amsdal/operations/manager.cpython-311-darwin.so +0 -0
  88. amsdal/schemas/manager.cpython-311-darwin.so +0 -0
  89. amsdal/services/__init__.cpython-311-darwin.so +0 -0
  90. amsdal/services/transaction_execution.cpython-311-darwin.so +0 -0
  91. {amsdal-0.1.0.dist-info → amsdal-0.1.2.dist-info}/METADATA +1 -1
  92. {amsdal-0.1.0.dist-info → amsdal-0.1.2.dist-info}/RECORD +95 -63
  93. {amsdal-0.1.0.dist-info → amsdal-0.1.2.dist-info}/LICENSE.txt +0 -0
  94. {amsdal-0.1.0.dist-info → amsdal-0.1.2.dist-info}/WHEEL +0 -0
  95. {amsdal-0.1.0.dist-info → amsdal-0.1.2.dist-info}/top_level.txt +0 -0
amsdal/__about__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2023-present
2
2
  #
3
3
  # SPDX-License-Identifier: AMSDAL End User License Agreement
4
- __version__ = '0.1.0'
4
+ __version__ = '0.1.2'
@@ -0,0 +1,203 @@
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.TYPE,
10
+ class_name='Object',
11
+ new_schema={
12
+ 'title': 'Object',
13
+ 'properties': {
14
+ 'title': {'type': 'string', 'title': 'Title'},
15
+ 'type': {'type': 'string', 'title': 'Type'},
16
+ 'default': {'type': 'anything', 'title': 'Default'},
17
+ 'properties': {
18
+ 'type': 'dictionary',
19
+ 'items': {'key': {'type': 'string'}, 'value': {'type': 'anything'}},
20
+ 'title': 'Properties',
21
+ },
22
+ 'required': {'type': 'array', 'items': {'type': 'string'}, 'title': 'Required'},
23
+ 'unique': {
24
+ 'type': 'array',
25
+ 'items': {'type': 'array', 'items': {'type': 'string'}},
26
+ 'title': 'Unique Fields',
27
+ },
28
+ 'custom_code': {'type': 'string', 'title': 'Custom Code'},
29
+ 'meta_class': {'type': 'string', 'title': 'Meta Class'},
30
+ },
31
+ },
32
+ ),
33
+ migrations.CreateClass(
34
+ schema_type=SchemaTypes.TYPE,
35
+ class_name='Binary',
36
+ new_schema={'title': 'Binary', 'properties': {}, 'meta_class': 'TypeMeta'},
37
+ ),
38
+ migrations.CreateClass(
39
+ schema_type=SchemaTypes.TYPE,
40
+ class_name='Dictionary',
41
+ new_schema={'title': 'Dictionary', 'properties': {}, 'meta_class': 'TypeMeta'},
42
+ ),
43
+ migrations.CreateClass(
44
+ schema_type=SchemaTypes.TYPE,
45
+ class_name='Anything',
46
+ new_schema={'title': 'Anything', 'properties': {}, 'meta_class': 'TypeMeta'},
47
+ ),
48
+ migrations.CreateClass(
49
+ schema_type=SchemaTypes.TYPE,
50
+ class_name='String',
51
+ new_schema={'title': 'String', 'default': '', 'properties': {}, 'meta_class': 'TypeMeta'},
52
+ ),
53
+ migrations.CreateClass(
54
+ schema_type=SchemaTypes.TYPE,
55
+ class_name='Array',
56
+ new_schema={'title': 'Array', 'properties': {}, 'meta_class': 'TypeMeta'},
57
+ ),
58
+ migrations.CreateClass(
59
+ schema_type=SchemaTypes.TYPE,
60
+ class_name='Number',
61
+ new_schema={'title': 'Number', 'properties': {}, 'meta_class': 'TypeMeta'},
62
+ ),
63
+ migrations.CreateClass(
64
+ schema_type=SchemaTypes.TYPE,
65
+ class_name='Boolean',
66
+ new_schema={
67
+ 'title': 'Boolean',
68
+ 'properties': {},
69
+ 'options': [{'key': 'true', 'value': True}, {'key': 'false', 'value': False}],
70
+ 'meta_class': 'TypeMeta',
71
+ },
72
+ ),
73
+ migrations.CreateClass(
74
+ schema_type=SchemaTypes.CORE,
75
+ class_name='Option',
76
+ new_schema={
77
+ 'title': 'Option',
78
+ 'required': ['key', 'value'],
79
+ 'properties': {
80
+ 'key': {'type': 'string', 'title': 'Key'},
81
+ 'value': {'type': 'string', 'title': 'Value Type'},
82
+ },
83
+ 'meta_class': 'TypeMeta',
84
+ },
85
+ ),
86
+ migrations.CreateClass(
87
+ schema_type=SchemaTypes.CORE,
88
+ class_name='Validator',
89
+ new_schema={
90
+ 'title': 'Validator',
91
+ 'required': ['name', 'data'],
92
+ 'properties': {
93
+ 'name': {'type': 'string', 'title': 'Validator Name'},
94
+ 'data': {'type': 'anything', 'title': 'Validator Data'},
95
+ },
96
+ 'meta_class': 'TypeMeta',
97
+ },
98
+ ),
99
+ migrations.CreateClass(
100
+ schema_type=SchemaTypes.CORE,
101
+ class_name='ClassPropertyMeta',
102
+ new_schema={
103
+ 'title': 'ClassPropertyMeta',
104
+ 'properties': {
105
+ 'title': {'type': 'string', 'title': 'Title'},
106
+ 'default': {'type': 'anything', 'title': 'Default'},
107
+ 'options': {'type': 'array', 'items': {'type': 'Option'}, 'title': 'Options'},
108
+ },
109
+ 'meta_class': 'TypeMeta',
110
+ },
111
+ ),
112
+ migrations.CreateClass(
113
+ schema_type=SchemaTypes.CORE,
114
+ class_name='ClassProperty',
115
+ new_schema={
116
+ 'title': 'ClassProperty',
117
+ 'required': ['type'],
118
+ 'properties': {
119
+ 'type': {'type': 'string', 'title': 'Type'},
120
+ 'items': {
121
+ 'type': 'dictionary',
122
+ 'items': {'key': {'type': 'string'}, 'value': {'type': 'anything'}},
123
+ 'title': 'Items',
124
+ },
125
+ },
126
+ 'meta_class': 'TypeMeta',
127
+ },
128
+ ),
129
+ migrations.CreateClass(
130
+ schema_type=SchemaTypes.CORE,
131
+ class_name='ClassObject',
132
+ new_schema={
133
+ 'title': 'ClassObject',
134
+ 'properties': {
135
+ 'properties': {
136
+ 'type': 'dictionary',
137
+ 'items': {'key': {'type': 'string'}, 'value': {'type': 'ClassProperty'}},
138
+ 'title': 'Properties',
139
+ },
140
+ 'required': {'type': 'array', 'items': {'type': 'string'}, 'title': 'Required'},
141
+ 'meta_class': {'type': 'string', 'default': 'ClassObject', 'title': 'Meta Class'},
142
+ },
143
+ 'custom_code': '@property # type: ignore[misc]\ndef display_name(self) -> str: # type: ignore[no-untyped-def]\n return self.title',
144
+ },
145
+ ),
146
+ migrations.CreateClass(
147
+ schema_type=SchemaTypes.CORE,
148
+ class_name='ClassObjectMeta',
149
+ new_schema={
150
+ 'title': 'ClassObjectMeta',
151
+ 'required': ['title', 'type'],
152
+ 'properties': {
153
+ 'title': {'type': 'string', 'title': 'Title'},
154
+ 'type': {'type': 'string', 'title': 'Type'},
155
+ 'default': {'type': 'anything', 'title': 'Default'},
156
+ 'properties': {
157
+ 'type': 'dictionary',
158
+ 'items': {'key': {'type': 'string'}, 'value': {'type': 'ClassPropertyMeta'}},
159
+ 'title': 'Properties',
160
+ },
161
+ 'indexed': {'type': 'array', 'items': {'type': 'string'}, 'title': 'Indexed'},
162
+ 'unique': {
163
+ 'type': 'array',
164
+ 'items': {'type': 'array', 'items': {'type': 'string'}},
165
+ 'title': 'Unique Fields',
166
+ },
167
+ 'custom_code': {'type': 'string', 'title': 'Custom Code'},
168
+ },
169
+ },
170
+ ),
171
+ migrations.CreateClass(
172
+ schema_type=SchemaTypes.CORE,
173
+ class_name='Fixture',
174
+ new_schema={
175
+ 'title': 'Fixture',
176
+ 'required': ['data', 'external_id'],
177
+ 'properties': {
178
+ 'class_name': {'type': 'string', 'title': 'Class Name'},
179
+ 'external_id': {'type': 'string', 'title': 'External ID'},
180
+ 'data': {
181
+ 'type': 'dictionary',
182
+ 'items': {'key': {'type': 'string'}, 'value': {'type': 'anything'}},
183
+ 'title': 'Data',
184
+ },
185
+ },
186
+ 'unique_properties': [['external_id']],
187
+ },
188
+ ),
189
+ migrations.CreateClass(
190
+ schema_type=SchemaTypes.CORE,
191
+ class_name='File',
192
+ new_schema={
193
+ 'title': 'File',
194
+ 'required': ['filename', 'data'],
195
+ 'properties': {
196
+ 'filename': {'type': 'string', 'title': 'Filename'},
197
+ 'data': {'type': 'binary', 'title': 'Data'},
198
+ 'size': {'type': 'number', 'title': 'Size'},
199
+ },
200
+ 'custom_code': "def pre_update(self): # type: ignore[no-untyped-def]\n self.size = len(self.data or b'')\n\ndef pre_create(self) -> None: # type: ignore[no-untyped-def]\n self.size = len(self.data or b'')\n\nfrom pathlib import Path\nfrom typing import BinaryIO\n\n\ndef to_file(self, file_or_path: Path | BinaryIO) -> None: # type: ignore[no-untyped-def]\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(self.data) # type: ignore[union-attr]\n else:\n file_or_path.write(self.data)\n file_or_path.seek(0)\n\nfrom pathlib import Path\nfrom typing import BinaryIO\n\n\n@classmethod # type: ignore[misc, no-untyped-def]\ndef from_file(\n cls,\n file_or_path: Path | BinaryIO,\n) -> 'File': # type: ignore[name-defined] # noqa: F821\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\n data = file_or_path.read_bytes()\n filename = file_or_path.name\n else:\n file_or_path.seek(0)\n data = file_or_path.read()\n filename = Path(file_or_path.name).name\n\n return cls(data=data, filename=filename)\n\nimport base64\n\nfrom pydantic import field_validator\n\n\n@field_validator('data') # type: ignore[misc]\n@classmethod\ndef data_base64_decode(cls, v: bytes) -> bytes: # type: ignore[no-untyped-def] # noqa: ARG001\n is_base64: bool = False\n\n try:\n is_base64 = base64.b64encode(base64.b64decode(v)) == v\n except Exception:\n ...\n\n if is_base64:\n return base64.b64decode(v)\n\n return v\n\n@property # type: ignore[misc]\ndef mimetype(self) -> str | None: # type: ignore[no-untyped-def]\n import mimetypes\n\n return mimetypes.guess_type(self.filename)[0]",
201
+ },
202
+ ),
203
+ ]
Binary file
Binary file
Binary file
@@ -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,51 @@
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
+ if not is_new_object or '_metadata' in kwargs:
13
+ return
14
+
15
+ from amsdal.contrib.auth.errors import AuthenticationError
16
+ from amsdal.contrib.auth.settings import auth_settings
17
+
18
+ email = kwargs.get('email', None)
19
+ password = kwargs.get('password', None)
20
+
21
+ if not email:
22
+ msg = "Email can't be empty"
23
+ raise AuthenticationError(msg)
24
+
25
+ if not password:
26
+ msg = "Password can't be empty"
27
+ raise AuthenticationError(msg)
28
+
29
+ lowercased_email = email.lower()
30
+
31
+ from models.contrib.user import User # type: ignore[import-not-found]
32
+
33
+ user = User.objects.filter(email=lowercased_email, _address__object_version=Versions.LATEST).get_or_none().execute()
34
+
35
+ if not user:
36
+ msg = 'Invalid email / password'
37
+ raise AuthenticationError(msg)
38
+
39
+ if not bcrypt.checkpw(password.encode('utf-8') if isinstance(password, str) else password, user.password):
40
+ msg = 'Invalid email / password'
41
+ raise AuthenticationError(msg)
42
+
43
+ kwargs['password'] = 'validated'
44
+ expiration_time = datetime.now(tz=timezone.utc) + timedelta(seconds=1200)
45
+ token = jwt.encode(
46
+ {'email': lowercased_email, 'exp': expiration_time},
47
+ key=auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
48
+ algorithm='HS256',
49
+ )
50
+
51
+ 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,3 @@
1
+ @property # type: ignore[misc]
2
+ def display_name(self) -> str: # type: ignore[no-untyped-def]
3
+ 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,3 @@
1
+ @property # type: ignore[misc]
2
+ def display_name(self) -> str: # type: ignore[no-untyped-def]
3
+ return f'{self.model}:{self.action}'
@@ -0,0 +1,2 @@
1
+ def pre_create(self) -> None: # type: ignore[no-untyped-def] # noqa: ARG001
2
+ pass
@@ -0,0 +1,25 @@
1
+ from typing import Any
2
+
3
+
4
+ def pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # type: ignore[no-untyped-def] # noqa: ARG001
5
+ import bcrypt
6
+
7
+ from amsdal.contrib.auth.errors import UserCreationError
8
+
9
+ email = kwargs.get('email', None)
10
+ password = kwargs.get('password', None)
11
+
12
+ if email is None or email == '':
13
+ msg = "Email can't be empty"
14
+ raise UserCreationError(msg)
15
+
16
+ if password is None or password == '':
17
+ msg = "Password can't be empty"
18
+ raise UserCreationError(msg)
19
+
20
+ kwargs['email'] = email.lower()
21
+
22
+ if is_new_object and '_metadata' not in kwargs:
23
+ hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
24
+ kwargs['password'] = hashed_password
25
+ kwargs['_object_id'] = email.lower()
@@ -0,0 +1,25 @@
1
+ {
2
+ "title": "User",
3
+ "type": "object",
4
+ "properties": {
5
+ "email": {
6
+ "title": "Email",
7
+ "type": "string"
8
+ },
9
+ "password": {
10
+ "title": "Password (hash)",
11
+ "type": "binary"
12
+ },
13
+ "permissions": {
14
+ "title": "Permissions",
15
+ "type": "array",
16
+ "items": {
17
+ "type": "Permission"
18
+ }
19
+ }
20
+ },
21
+ "required": [
22
+ "email",
23
+ "password"
24
+ ]
25
+ }
@@ -0,0 +1,11 @@
1
+ @property # type: ignore[misc]
2
+ def display_name(self) -> str: # type: ignore[no-untyped-def]
3
+ return self.email
4
+
5
+
6
+ def __str__(self) -> str: # type: ignore[no-untyped-def] # noqa: N807
7
+ return f'User(email={self.email})'
8
+
9
+
10
+ def __repr__(self) -> str: # type: ignore[no-untyped-def] # noqa: N807
11
+ return str(self)