dmart 0.1.6__py3-none-any.whl → 0.1.8__py3-none-any.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.
- alembic/scripts/__init__.py +0 -0
- alembic/scripts/calculate_checksums.py +77 -0
- alembic/scripts/migration_f7a4949eed19.py +28 -0
- alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
- alembic/versions/10d2041b94d4_last_checksum_history.py +62 -0
- alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
- alembic/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
- alembic/versions/3c8bca2219cc_add_otp_table.py +38 -0
- alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
- alembic/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
- alembic/versions/74288ccbd3b5_initial.py +264 -0
- alembic/versions/7520a89a8467_rm_activesession_table.py +39 -0
- alembic/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
- alembic/versions/8640dcbebf85_add_notes_to_users.py +32 -0
- alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
- alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
- alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
- alembic/versions/__init__.py +0 -0
- alembic/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
- alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
- alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
- api/user/model/__init__.py +0 -0
- api/user/model/errors.py +14 -0
- api/user/model/requests.py +165 -0
- api/user/model/responses.py +11 -0
- {dmart-0.1.6.dist-info → dmart-0.1.8.dist-info}/METADATA +1 -1
- {dmart-0.1.6.dist-info → dmart-0.1.8.dist-info}/RECORD +48 -5
- plugins/action_log/__init__.py +0 -0
- plugins/action_log/plugin.py +121 -0
- plugins/admin_notification_sender/__init__.py +0 -0
- plugins/admin_notification_sender/plugin.py +124 -0
- plugins/ldap_manager/__init__.py +0 -0
- plugins/ldap_manager/plugin.py +100 -0
- plugins/local_notification/__init__.py +0 -0
- plugins/local_notification/plugin.py +123 -0
- plugins/realtime_updates_notifier/__init__.py +0 -0
- plugins/realtime_updates_notifier/plugin.py +58 -0
- plugins/redis_db_update/__init__.py +0 -0
- plugins/redis_db_update/plugin.py +188 -0
- plugins/resource_folders_creation/__init__.py +0 -0
- plugins/resource_folders_creation/plugin.py +81 -0
- plugins/system_notification_sender/__init__.py +0 -0
- plugins/system_notification_sender/plugin.py +188 -0
- plugins/update_access_controls/__init__.py +0 -0
- plugins/update_access_controls/plugin.py +9 -0
- {dmart-0.1.6.dist-info → dmart-0.1.8.dist-info}/WHEEL +0 -0
- {dmart-0.1.6.dist-info → dmart-0.1.8.dist-info}/entry_points.txt +0 -0
- {dmart-0.1.6.dist-info → dmart-0.1.8.dist-info}/top_level.txt +0 -0
api/user/model/errors.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from models.api import Error
|
|
2
|
+
from utils.internal_error_code import InternalErrorCode
|
|
3
|
+
|
|
4
|
+
INVALID_OTP = Error(
|
|
5
|
+
type="OTP",
|
|
6
|
+
code=InternalErrorCode.OTP_INVALID,
|
|
7
|
+
message="Invalid OTP",
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
EXPIRED_OTP = Error(
|
|
11
|
+
type="OTP",
|
|
12
|
+
code=InternalErrorCode.OTP_EXPIRED,
|
|
13
|
+
message="Expired OTP",
|
|
14
|
+
)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from utils.internal_error_code import InternalErrorCode
|
|
4
|
+
import utils.regex as rgx
|
|
5
|
+
from models.api import Exception, Error
|
|
6
|
+
from models.enums import StrEnum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OTPType(StrEnum):
|
|
10
|
+
SMS = "SMS"
|
|
11
|
+
EMAIL = "EMAIL"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SendOTPRequest(BaseModel):
|
|
15
|
+
shortname: str | None = Field(None, pattern=rgx.SHORTNAME)
|
|
16
|
+
msisdn: str | None = Field(None, pattern=rgx.MSISDN)
|
|
17
|
+
email: str | None = Field(None, pattern=rgx.EMAIL)
|
|
18
|
+
|
|
19
|
+
def check_fields(self) -> Dict[str, str]:
|
|
20
|
+
if self.email is None and self.msisdn is None and self.shortname is None:
|
|
21
|
+
raise Exception(
|
|
22
|
+
422,
|
|
23
|
+
Error(
|
|
24
|
+
type="OTP",
|
|
25
|
+
code=InternalErrorCode.EMAIL_OR_MSISDN_REQUIRED,
|
|
26
|
+
message="One of these [email, msisdn, shortname] should be set!",
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if [self.email, self.msisdn, self.shortname].count(None) != 2:
|
|
31
|
+
raise Exception(
|
|
32
|
+
422,
|
|
33
|
+
Error(
|
|
34
|
+
type="OTP",
|
|
35
|
+
code=InternalErrorCode.INVALID_STANDALONE_DATA,
|
|
36
|
+
message="Too many input has been passed",
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
elif self.msisdn:
|
|
41
|
+
return {"msisdn": self.msisdn}
|
|
42
|
+
elif self.email:
|
|
43
|
+
return {"email": self.email}
|
|
44
|
+
elif self.shortname:
|
|
45
|
+
return {"shortname": self.shortname}
|
|
46
|
+
|
|
47
|
+
raise Exception(
|
|
48
|
+
500,
|
|
49
|
+
Error(
|
|
50
|
+
type="OTP",
|
|
51
|
+
code=InternalErrorCode.OTP_ISSUE,
|
|
52
|
+
message="Something went wrong",
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
model_config = {
|
|
57
|
+
"json_schema_extra": {
|
|
58
|
+
"examples": [
|
|
59
|
+
{
|
|
60
|
+
"msisdn": "7777778110"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class PasswordResetRequest(BaseModel):
|
|
67
|
+
msisdn: str | None = Field(None, pattern=rgx.MSISDN)
|
|
68
|
+
shortname: str | None = Field(None, pattern=rgx.SHORTNAME)
|
|
69
|
+
email: str | None = Field(None, pattern=rgx.EMAIL)
|
|
70
|
+
|
|
71
|
+
def check_fields(self) -> Dict[str, str]:
|
|
72
|
+
if self.email is None and self.msisdn is None and self.shortname is None:
|
|
73
|
+
raise Exception(
|
|
74
|
+
422,
|
|
75
|
+
Error(
|
|
76
|
+
type="OTP",
|
|
77
|
+
code=InternalErrorCode.EMAIL_OR_MSISDN_REQUIRED,
|
|
78
|
+
message="One of these [shortname, email, msisdn] should be set!",
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if [self.email, self.msisdn, self.shortname].count(None) != 2:
|
|
83
|
+
raise Exception(
|
|
84
|
+
422,
|
|
85
|
+
Error(
|
|
86
|
+
type="OTP",
|
|
87
|
+
code=InternalErrorCode.INVALID_STANDALONE_DATA,
|
|
88
|
+
message="Too many input has been passed",
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
elif self.msisdn:
|
|
93
|
+
return {"msisdn": self.msisdn}
|
|
94
|
+
elif self.email:
|
|
95
|
+
return {"email": self.email}
|
|
96
|
+
elif self.shortname:
|
|
97
|
+
return {"shortname": self.shortname}
|
|
98
|
+
|
|
99
|
+
raise Exception(
|
|
100
|
+
500,
|
|
101
|
+
Error(
|
|
102
|
+
type="password_reset",
|
|
103
|
+
code=InternalErrorCode.PASSWORD_RESET_ERROR,
|
|
104
|
+
message="Something went wrong",
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
model_config = {
|
|
109
|
+
"json_schema_extra": {
|
|
110
|
+
"examples": [
|
|
111
|
+
{
|
|
112
|
+
"msisdn": "7777778110"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
class ConfirmOTPRequest(SendOTPRequest, BaseModel):
|
|
118
|
+
code: str = Field(..., pattern=rgx.OTP_CODE)
|
|
119
|
+
|
|
120
|
+
model_config = {
|
|
121
|
+
"json_schema_extra": {
|
|
122
|
+
"examples": [
|
|
123
|
+
{
|
|
124
|
+
"code": "84293201"
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
class UserLoginRequest(BaseModel):
|
|
131
|
+
shortname: str | None = Field(None, pattern=rgx.SHORTNAME)
|
|
132
|
+
email: str | None = Field(None, pattern=rgx.EMAIL)
|
|
133
|
+
msisdn: str | None = Field(None, pattern=rgx.MSISDN)
|
|
134
|
+
password: str | None = Field(None)
|
|
135
|
+
invitation: str | None = Field(None, pattern=rgx.INVITATION)
|
|
136
|
+
firebase_token: str | None = Field(None)
|
|
137
|
+
otp: str | None = Field(None)
|
|
138
|
+
|
|
139
|
+
def check_fields(self) -> Dict[str, str] | None:
|
|
140
|
+
if self.shortname is None and self.email is None and self.msisdn is None:
|
|
141
|
+
return {}
|
|
142
|
+
# raise ValueError("One of these [shortname, email, msisdn] should be set!")
|
|
143
|
+
|
|
144
|
+
if [self.shortname, self.email, self.msisdn].count(None) != 2:
|
|
145
|
+
raise ValueError("Too many input has been passed")
|
|
146
|
+
|
|
147
|
+
if self.shortname:
|
|
148
|
+
return {"shortname": self.shortname}
|
|
149
|
+
elif self.msisdn:
|
|
150
|
+
return {"msisdn": self.msisdn}
|
|
151
|
+
elif self.email:
|
|
152
|
+
return {"email": self.email}
|
|
153
|
+
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
model_config = {
|
|
157
|
+
"json_schema_extra": {
|
|
158
|
+
"examples": [
|
|
159
|
+
{
|
|
160
|
+
"shortname": "john_doo",
|
|
161
|
+
"password": "my_secure_password_@_93301"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -14,6 +14,27 @@ sync.py,sha256=FlmubtlnFaxtZkbRV1-eyS_Sx5KBRvWyIZjvd0Tiar4,7339
|
|
|
14
14
|
websocket.py,sha256=Q8WUTvOTBHKP5xy5wim8yn0t-BfjrPwx7J_6vbzAm1A,7576
|
|
15
15
|
alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
alembic/env.py,sha256=z12UKhorKSOKEovOCQOwRjfR_tup4VeRlhcB1UPk3Xw,2700
|
|
17
|
+
alembic/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
alembic/scripts/calculate_checksums.py,sha256=v2NLEvReA9V3noJE-BWANgKDdhc8Mqg1ZmJJ8nc8sGI,3443
|
|
19
|
+
alembic/scripts/migration_f7a4949eed19.py,sha256=oUXuxjU4MbVafm4S-xu5J_4apHqW6hQZ8ftpJtCtM28,1462
|
|
20
|
+
alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py,sha256=70vrPfhMHVHhw1l7KNQpvCUU76XZfvGcPlrXOeU4FHU,2599
|
|
21
|
+
alembic/versions/10d2041b94d4_last_checksum_history.py,sha256=FYjyzE5Xi6bn9LUtxLKFnQOKhg4GZOrTkpqtmlCnY6Y,2860
|
|
22
|
+
alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py,sha256=taaFTkTaFfLfj7QUUfIdcojvbOdjut_dWtrIvQUN7-4,921
|
|
23
|
+
alembic/versions/26bfe19b49d4_rm_failedloginattempts.py,sha256=-Roftn8OSUz7kfR3yMI02rulyvnH46W0WpJfDQ5xAdk,1471
|
|
24
|
+
alembic/versions/3c8bca2219cc_add_otp_table.py,sha256=f-YSxx1iLA0iHWukq1VnngFmiiYQyEzowicox0-wtbY,1125
|
|
25
|
+
alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py,sha256=yP40IHwtzFAAUid_VeeCkt_9F2RszMYBjNKx2mDTKNw,1037
|
|
26
|
+
alembic/versions/71bc1df82e6a_adding_user_last_login_at.py,sha256=LINO2hWUEFmsTspupd8AxgUS6tNJFNgGC5do1QPMQ1E,1486
|
|
27
|
+
alembic/versions/74288ccbd3b5_initial.py,sha256=hZ1w5mqVKSO13J1O4zcTccDJBk2c-cmIB0AUx1KLlLI,13988
|
|
28
|
+
alembic/versions/7520a89a8467_rm_activesession_table.py,sha256=4VYv9tCXkTQNaXPVvjdsKTiw093KkBBUHNSiWdZtqtA,1305
|
|
29
|
+
alembic/versions/848b623755a4_make_created_nd_updated_at_required.py,sha256=o106xd-apT8ZAXdVCjX76o7VuqRQNkIl0V4f41m5j2E,5375
|
|
30
|
+
alembic/versions/8640dcbebf85_add_notes_to_users.py,sha256=XjfW2Lc0-xoRGB2qfdzDstw7Caaev6JhqRNYV_sIqgc,813
|
|
31
|
+
alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py,sha256=F9FDF2qyeqvOx1gkYq5HQMkVCH1QrXciYSHVxijsSSU,4024
|
|
32
|
+
alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py,sha256=S466VE3jsxXDEpTKyhIN06uutM0jI7zYHTWIYvU_pJw,2579
|
|
33
|
+
alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py,sha256=xrqfJB5cER0PYi1torwkgsbBBjDeeM6dyj4pu-quphk,3662
|
|
34
|
+
alembic/versions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
alembic/versions/b53f916b3f6d_json_to_jsonb.py,sha256=LPnevQjbjs0KrwqxYHtGUC6mSBBsUpiCBnwD-FN2x50,24840
|
|
36
|
+
alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py,sha256=4U45sfMGIBMcRkwPuFPRc_M9pL2lmOn9kguWjakPAbU,1007
|
|
37
|
+
alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py,sha256=LA4rx3u0Ei5m4OcSsVYHBsGMeKOJdx8G88yK1kBLFys,2307
|
|
17
38
|
api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
39
|
api/info/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
40
|
api/info/router.py,sha256=sQZZor7A-uDzsJX39aqEA7bMZOJ-WTitYeFvVNWfaHw,3938
|
|
@@ -27,6 +48,10 @@ api/qr/router.py,sha256=Ru7UT_iQS6mFwE1bCPPrusSQfFgoV_u6pjZJ0gArE7g,3870
|
|
|
27
48
|
api/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
49
|
api/user/router.py,sha256=FwuxM04niGlls-V1MPzHH5A4jHJ0g5NNIFZBE6bJ9Fc,51302
|
|
29
50
|
api/user/service.py,sha256=-iQpcBVPTDiLE_xOf87Ni0oSQDtmALAXEwU4IgSvnJk,8463
|
|
51
|
+
api/user/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
api/user/model/errors.py,sha256=rhcIcbAIk_7rs5lxdgcsf7SWFGmC9QLsgc67x7S6CKA,299
|
|
53
|
+
api/user/model/requests.py,sha256=MazMirg7wQoUm4qvnm_EAB_gJIy3YxvYYqNyU3fZJc0,5025
|
|
54
|
+
api/user/model/responses.py,sha256=0vbigspq_aBd1JS6hEm13BnG7Hm7EIiWBZ6xz3d5hmE,202
|
|
30
55
|
config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
56
|
config/channels.json,sha256=GepystGi0h_1fuakC_gdIc-YYxyy-a4TI619ygIpyyM,156
|
|
32
57
|
config/notification.json,sha256=esrOaMUIqfcCHB0Tawp3t4cu7DQAA15X12OS-Gyenb0,361
|
|
@@ -63,6 +88,24 @@ models/api.py,sha256=f5X56dudyEysPmDuI5grM2RRCXuIQoehaAB6wMAGG28,6473
|
|
|
63
88
|
models/core.py,sha256=tEb7cbnC71yE9SDluynj7dE3U8Ed-EbF3uRJizy-uuU,16880
|
|
64
89
|
models/enums.py,sha256=y2G5EKIc8FusVW4JvEozGFKL2GxjtuOK7k3zSguP4dc,5395
|
|
65
90
|
plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
|
+
plugins/action_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
92
|
+
plugins/action_log/plugin.py,sha256=-JY_iIIJJjFFofvpMoCxNJMXNr_KC6kA8TiwvvoxaWI,4434
|
|
93
|
+
plugins/admin_notification_sender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
|
+
plugins/admin_notification_sender/plugin.py,sha256=sxVGW8qtRmDEQeS6QD3F3IqzZOoh_9H_y2TsRCDkXaw,4771
|
|
95
|
+
plugins/ldap_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
|
+
plugins/ldap_manager/plugin.py,sha256=c05pKGsyLETMrheCqIw3gZOSLdTWvEDq_WCl0Q_0yXQ,3030
|
|
97
|
+
plugins/local_notification/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
+
plugins/local_notification/plugin.py,sha256=FObVxID5Bg0G_xStpJYZkg706wu_CpUqk09DmzHAyPQ,4074
|
|
99
|
+
plugins/realtime_updates_notifier/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
|
+
plugins/realtime_updates_notifier/plugin.py,sha256=Gcvob4ShSs2Ht1hLD2vtwhR_PSFYMv3_l_VPM2opTYs,2374
|
|
101
|
+
plugins/redis_db_update/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
|
+
plugins/redis_db_update/plugin.py,sha256=z05k1zNJgBnKPj-jrtMUeI9br75ZPlifbzL0HxpRnXg,7128
|
|
103
|
+
plugins/resource_folders_creation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
|
+
plugins/resource_folders_creation/plugin.py,sha256=OwYPtRjMt2esAAEdv1FjdZgjEz01yt2xOZQi3nB0kEQ,3327
|
|
105
|
+
plugins/system_notification_sender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
106
|
+
plugins/system_notification_sender/plugin.py,sha256=96xzmuUGvO2WOJ5y90akDoRB7Bqy-VpOo6ht8-rLLtQ,8253
|
|
107
|
+
plugins/update_access_controls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
|
+
plugins/update_access_controls/plugin.py,sha256=43UV4vg-zxBF_7Bv0AZH6gU0Bgy2ybapNK21wJTF05k,301
|
|
66
109
|
pytests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
110
|
pytests/api_user_models_erros_test.py,sha256=6VWLIhazYjz7avXIMIDpT6doiBO5FVzGsGJ3Cv8cXyg,583
|
|
68
111
|
pytests/api_user_models_requests_test.py,sha256=1AYZcMwa-AVeGrhTgwIkwqw3w7_CDPkbaJ0YDxLLKdY,3859
|
|
@@ -99,8 +142,8 @@ utils/ticket_sys_utils.py,sha256=9QAlW2iiy8KyxQRBDj_WmzS5kKb0aYJmGwd4qzmGVqo,700
|
|
|
99
142
|
utils/web_notifier.py,sha256=QM87VVid2grC5lK3NdS1yzz0z1wXljr4GChJOeK86W4,843
|
|
100
143
|
utils/templates/activation.html.j2,sha256=XAMKCdoqONoc4ZQucD0yV-Pg5DlHHASZrTVItNS-iBE,640
|
|
101
144
|
utils/templates/reminder.html.j2,sha256=aoS8bTs56q4hjAZKsb0jV9c-PIURBELuBOpT_qPZNVU,639
|
|
102
|
-
dmart-0.1.
|
|
103
|
-
dmart-0.1.
|
|
104
|
-
dmart-0.1.
|
|
105
|
-
dmart-0.1.
|
|
106
|
-
dmart-0.1.
|
|
145
|
+
dmart-0.1.8.dist-info/METADATA,sha256=rm9HvMHuQkwfhoqO9mQfhMjHGA8_7DU__kyukUzXXwM,2091
|
|
146
|
+
dmart-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
147
|
+
dmart-0.1.8.dist-info/entry_points.txt,sha256=GjfoGh1bpxuU9HHGJzbtCFPNptHv9TryxHMN3uBSKpg,37
|
|
148
|
+
dmart-0.1.8.dist-info/top_level.txt,sha256=JTypu1r5v9v7ru-60JSSbnSMEESHFRMacpcz-rPJx_U,262
|
|
149
|
+
dmart-0.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Any
|
|
3
|
+
import aiofiles
|
|
4
|
+
from utils.middleware import get_request_data
|
|
5
|
+
from models.core import ActionType, PluginBase, Event, Payload
|
|
6
|
+
from models.enums import ContentType, ResourceType
|
|
7
|
+
from models.core import Action, Locator, Meta
|
|
8
|
+
from utils.helpers import camel_case
|
|
9
|
+
from utils.settings import settings
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from fastapi.logger import logger
|
|
12
|
+
from data_adapters.adapter import data_adapter as db
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Plugin(PluginBase):
|
|
16
|
+
async def hook(self, data: Event):
|
|
17
|
+
if settings.active_data_db == "sql":
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
not isinstance(data.shortname, str)
|
|
22
|
+
or not isinstance(data.action_type, ActionType)
|
|
23
|
+
or not isinstance(data.resource_type, ResourceType)
|
|
24
|
+
or not isinstance(data.attributes, dict)
|
|
25
|
+
):
|
|
26
|
+
logger.warning("invalid data at action_log")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
class_type = getattr(
|
|
30
|
+
sys.modules["models.core"], camel_case(ResourceType(data.resource_type))
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if data.action_type == ActionType.delete:
|
|
34
|
+
entry = data.attributes["entry"]
|
|
35
|
+
else:
|
|
36
|
+
entry = await db.load_or_none(
|
|
37
|
+
space_name=data.space_name,
|
|
38
|
+
subpath=data.subpath,
|
|
39
|
+
shortname=data.shortname,
|
|
40
|
+
class_type=class_type,
|
|
41
|
+
user_shortname=data.user_shortname,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if entry is None:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
action_attributes = {}
|
|
48
|
+
if data.action_type == ActionType.create:
|
|
49
|
+
payload: dict[str,Any] = {}
|
|
50
|
+
if(
|
|
51
|
+
entry.payload and
|
|
52
|
+
isinstance(entry.payload, Payload) and
|
|
53
|
+
entry.payload.content_type and
|
|
54
|
+
entry.payload.content_type == ContentType.json
|
|
55
|
+
and entry.payload.body
|
|
56
|
+
):
|
|
57
|
+
mypayload = await db.load_resource_payload(
|
|
58
|
+
space_name=data.space_name,
|
|
59
|
+
subpath=data.subpath,
|
|
60
|
+
filename=entry.payload.body if isinstance(entry.payload.body, str) else data.shortname,
|
|
61
|
+
class_type=class_type,
|
|
62
|
+
)
|
|
63
|
+
payload = mypayload if mypayload else {}
|
|
64
|
+
action_attributes = self.generate_create_event_attributes(entry, payload)
|
|
65
|
+
|
|
66
|
+
elif data.action_type == ActionType.update:
|
|
67
|
+
action_attributes = data.attributes.get("history_diff", {})
|
|
68
|
+
|
|
69
|
+
action_attributes = {**action_attributes, **get_request_data()}
|
|
70
|
+
action_attributes.pop("_sa_instance_state", None)
|
|
71
|
+
event_obj = Action(
|
|
72
|
+
resource=Locator(
|
|
73
|
+
uuid=entry.uuid,
|
|
74
|
+
type=data.resource_type,
|
|
75
|
+
space_name=data.space_name,
|
|
76
|
+
subpath=data.subpath,
|
|
77
|
+
shortname=data.shortname,
|
|
78
|
+
displayname=entry.displayname,
|
|
79
|
+
description=entry.description,
|
|
80
|
+
tags=entry.tags,
|
|
81
|
+
),
|
|
82
|
+
user_shortname=data.user_shortname,
|
|
83
|
+
request=data.action_type,
|
|
84
|
+
timestamp=datetime.now(),
|
|
85
|
+
attributes=action_attributes,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
events_file_path = (
|
|
89
|
+
settings.spaces_folder
|
|
90
|
+
/ data.space_name
|
|
91
|
+
/ ".dm"
|
|
92
|
+
)
|
|
93
|
+
events_file_path.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
events_file_path = events_file_path / "events.jsonl"
|
|
95
|
+
|
|
96
|
+
# Remove binary content in the event object before serializing to json
|
|
97
|
+
if isinstance(event_obj.attributes, dict) and "media" in event_obj.attributes:
|
|
98
|
+
del event_obj.attributes["media"]
|
|
99
|
+
|
|
100
|
+
file_content = (
|
|
101
|
+
f"{event_obj.model_dump_json()}\n"
|
|
102
|
+
)
|
|
103
|
+
async with aiofiles.open(events_file_path, "a") as events_file:
|
|
104
|
+
await events_file.write(file_content)
|
|
105
|
+
|
|
106
|
+
def generate_create_event_attributes(self, entry: Meta, attributes: dict):
|
|
107
|
+
generated_attributes = {}
|
|
108
|
+
for key, value in entry.__dict__.items():
|
|
109
|
+
if key not in Meta.model_fields:
|
|
110
|
+
generated_attributes[key] = value
|
|
111
|
+
|
|
112
|
+
if entry.payload:
|
|
113
|
+
if isinstance(entry.payload, Payload):
|
|
114
|
+
generated_attributes["payload"] = entry.payload.model_dump()
|
|
115
|
+
else:
|
|
116
|
+
generated_attributes["payload"] = entry.payload
|
|
117
|
+
|
|
118
|
+
if attributes:
|
|
119
|
+
generated_attributes["payload"]["body"] = attributes
|
|
120
|
+
|
|
121
|
+
return generated_attributes
|
|
File without changes
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from sys import modules as sys_modules
|
|
3
|
+
|
|
4
|
+
from models import api
|
|
5
|
+
from models.core import Notification, NotificationData, PluginBase, Event, Translation
|
|
6
|
+
from utils.helpers import camel_case
|
|
7
|
+
from utils.notification import NotificationManager
|
|
8
|
+
from utils.settings import settings
|
|
9
|
+
from fastapi.logger import logger
|
|
10
|
+
from data_adapters.adapter import data_adapter as db
|
|
11
|
+
|
|
12
|
+
class Plugin(PluginBase):
|
|
13
|
+
async def hook(self, data: Event):
|
|
14
|
+
"""
|
|
15
|
+
after creating a new admin notification request
|
|
16
|
+
1- get the notification request
|
|
17
|
+
2- if it's scheduled for later, ignore it and let the cron job handle it
|
|
18
|
+
3- else send it to all its msisdns
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Type narrowing for PyRight
|
|
22
|
+
if not isinstance(data.shortname, str):
|
|
23
|
+
logger.warning(
|
|
24
|
+
"data.shortname is None and str is required at system_notification_sender"
|
|
25
|
+
)
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
notification_request_meta = await db.load(
|
|
29
|
+
data.space_name,
|
|
30
|
+
data.subpath,
|
|
31
|
+
data.shortname,
|
|
32
|
+
getattr(sys_modules["models.core"], camel_case(data.resource_type)),
|
|
33
|
+
data.user_shortname,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
notification_dict = notification_request_meta.dict()
|
|
37
|
+
notification_dict["subpath"] = data.subpath
|
|
38
|
+
notification_request_payload = await db.get_payload_from_event(data)
|
|
39
|
+
|
|
40
|
+
notification_dict.update(notification_request_payload)
|
|
41
|
+
|
|
42
|
+
if not notification_dict or notification_dict.get("scheduled_at", False):
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
# Get msisdns users
|
|
46
|
+
search_criteria = notification_dict.get('search_string', '')
|
|
47
|
+
if not search_criteria:
|
|
48
|
+
search_criteria = '@msisdn:' + '|'.join(notification_dict.get('msisdns', ''))
|
|
49
|
+
|
|
50
|
+
total, receivers = await db.query(api.Query(
|
|
51
|
+
space_name=data.space_name,
|
|
52
|
+
subpath=notification_dict['subpath'],
|
|
53
|
+
filters={},
|
|
54
|
+
search=search_criteria,
|
|
55
|
+
limit=10000,
|
|
56
|
+
offset=0
|
|
57
|
+
))
|
|
58
|
+
if total == 0:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
sub_receivers: dict = receivers[0].model_dump()
|
|
62
|
+
|
|
63
|
+
receivers_shortnames = set()
|
|
64
|
+
for receiver in sub_receivers["data"]:
|
|
65
|
+
receivers_shortnames.add(json.loads(receiver)["shortname"])
|
|
66
|
+
|
|
67
|
+
# await send_notification(
|
|
68
|
+
# notification_dict=notification_dict,
|
|
69
|
+
# receivers=receivers_shortnames
|
|
70
|
+
# )
|
|
71
|
+
notification_manager = NotificationManager()
|
|
72
|
+
formatted_req = await self.prepare_request(notification_dict)
|
|
73
|
+
for receiver in set(receivers_shortnames):
|
|
74
|
+
if not formatted_req["push_only"]:
|
|
75
|
+
notification_obj = await Notification.from_request(notification_dict)
|
|
76
|
+
await db.internal_save_model(
|
|
77
|
+
"personal",
|
|
78
|
+
f"people/{receiver}/notifications",
|
|
79
|
+
notification_obj,
|
|
80
|
+
)
|
|
81
|
+
for platform in formatted_req["platforms"]:
|
|
82
|
+
await notification_manager.send(
|
|
83
|
+
platform=platform,
|
|
84
|
+
data=NotificationData(
|
|
85
|
+
receiver=receiver,
|
|
86
|
+
title=formatted_req["title"],
|
|
87
|
+
body=formatted_req["body"],
|
|
88
|
+
image_urls=formatted_req["images_urls"],
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
notification_request_payload["status"] = "finished"
|
|
94
|
+
await db.save_payload_from_json(
|
|
95
|
+
space_name=data.space_name,
|
|
96
|
+
subpath=data.subpath,
|
|
97
|
+
meta=notification_request_meta,
|
|
98
|
+
payload_data=notification_request_payload,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
async def prepare_request(self, notification_dict) -> dict:
|
|
102
|
+
# Get Notification Request Images
|
|
103
|
+
attachments_path = (
|
|
104
|
+
settings.spaces_folder
|
|
105
|
+
/ f"{settings.management_space}/"
|
|
106
|
+
f"{notification_dict['subpath']}/.dm/{notification_dict['shortname']}"
|
|
107
|
+
)
|
|
108
|
+
notification_attachments = await db.get_entry_attachments(
|
|
109
|
+
subpath=f"{notification_dict['subpath']}/{notification_dict['shortname']}",
|
|
110
|
+
attachments_path=attachments_path,
|
|
111
|
+
)
|
|
112
|
+
notification_images = {
|
|
113
|
+
"en": notification_attachments.get("media", {}).get("en"),
|
|
114
|
+
"ar": notification_attachments.get("media", {}).get("ar"),
|
|
115
|
+
"ku": notification_attachments.get("media", {}).get("ku"),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"platforms": notification_dict["types"],
|
|
120
|
+
"title": Translation(**notification_dict["displayname"]),
|
|
121
|
+
"body": Translation(**notification_dict["description"]),
|
|
122
|
+
"images_urls": Translation(**notification_images),
|
|
123
|
+
"push_only": notification_dict.get("push_only", False),
|
|
124
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from fastapi.logger import logger
|
|
2
|
+
from models.core import Event, PluginBase, User
|
|
3
|
+
from models.enums import ActionType
|
|
4
|
+
from utils.settings import settings
|
|
5
|
+
from data_adapters.adapter import data_adapter as db
|
|
6
|
+
|
|
7
|
+
from ldap3 import AUTO_BIND_NO_TLS, MODIFY_REPLACE, Server, Connection, ALL
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Plugin(PluginBase):
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
super().__init__()
|
|
14
|
+
try:
|
|
15
|
+
server = Server(settings.ldap_url, get_info=ALL)
|
|
16
|
+
self.conn = Connection(
|
|
17
|
+
server,
|
|
18
|
+
user=settings.ldap_admin_dn,
|
|
19
|
+
password=settings.ldap_pass,
|
|
20
|
+
auto_bind=AUTO_BIND_NO_TLS
|
|
21
|
+
)
|
|
22
|
+
except Exception:
|
|
23
|
+
logger.error(
|
|
24
|
+
"Failed to connect to LDAP"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def hook(self, data: Event):
|
|
30
|
+
if not hasattr(self, "conn"):
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Type narrowing for PyRight
|
|
34
|
+
if not isinstance(data.shortname, str):
|
|
35
|
+
logger.warning(
|
|
36
|
+
"data.shortname is None and str is required at ldap_manager"
|
|
37
|
+
)
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
if data.action_type == ActionType.delete:
|
|
41
|
+
self.delete(data.shortname)
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
user_model: User = await db.load(
|
|
45
|
+
space_name=settings.management_space,
|
|
46
|
+
subpath=data.subpath,
|
|
47
|
+
shortname=data.shortname,
|
|
48
|
+
class_type=User
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if data.action_type == ActionType.create:
|
|
52
|
+
self.add(data.shortname, user_model)
|
|
53
|
+
|
|
54
|
+
elif data.action_type == ActionType.update:
|
|
55
|
+
self.modify(data.shortname, user_model)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
elif data.action_type == ActionType.move and "src_shortname" in data.attributes:
|
|
59
|
+
self.delete(data.attributes['src_shortname'])
|
|
60
|
+
self.add(data.shortname, user_model)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def delete(self, shortname: str):
|
|
65
|
+
self.conn.delete(f"cn={shortname},{settings.ldap_root_dn}")
|
|
66
|
+
|
|
67
|
+
def add(
|
|
68
|
+
self,
|
|
69
|
+
shortname: str,
|
|
70
|
+
user_model: User
|
|
71
|
+
):
|
|
72
|
+
self.conn.add(
|
|
73
|
+
f"cn={shortname},{settings.ldap_root_dn}",
|
|
74
|
+
'dmartUser',
|
|
75
|
+
{
|
|
76
|
+
"cn": shortname.encode(),
|
|
77
|
+
"sn": shortname.encode(),
|
|
78
|
+
"gn": str(getattr(user_model, "displayname", "")).encode(),
|
|
79
|
+
"userPassword": getattr(user_model, "password", "").encode()
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def modify(
|
|
84
|
+
self,
|
|
85
|
+
shortname: str,
|
|
86
|
+
user_model: User
|
|
87
|
+
):
|
|
88
|
+
self.conn.modify(
|
|
89
|
+
f"cn={shortname},{settings.ldap_root_dn}",
|
|
90
|
+
{
|
|
91
|
+
"gn": [(
|
|
92
|
+
MODIFY_REPLACE,
|
|
93
|
+
[str(getattr(user_model, "displayname", "")).encode()]
|
|
94
|
+
)],
|
|
95
|
+
"userPassword": [(
|
|
96
|
+
MODIFY_REPLACE,
|
|
97
|
+
[getattr(user_model, "password", "").encode()]
|
|
98
|
+
)],
|
|
99
|
+
}
|
|
100
|
+
)
|
|
File without changes
|