django-nativemojo 0.1.10__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.
Files changed (194) hide show
  1. django_nativemojo-0.1.10.dist-info/LICENSE +19 -0
  2. django_nativemojo-0.1.10.dist-info/METADATA +96 -0
  3. django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
  4. django_nativemojo-0.1.10.dist-info/RECORD +194 -0
  5. django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
  6. mojo/__init__.py +3 -0
  7. mojo/apps/account/__init__.py +1 -0
  8. mojo/apps/account/admin.py +91 -0
  9. mojo/apps/account/apps.py +16 -0
  10. mojo/apps/account/migrations/0001_initial.py +77 -0
  11. mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
  12. mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
  13. mojo/apps/account/migrations/__init__.py +0 -0
  14. mojo/apps/account/models/__init__.py +3 -0
  15. mojo/apps/account/models/group.py +98 -0
  16. mojo/apps/account/models/member.py +95 -0
  17. mojo/apps/account/models/pkey.py +18 -0
  18. mojo/apps/account/models/user.py +211 -0
  19. mojo/apps/account/rest/__init__.py +3 -0
  20. mojo/apps/account/rest/group.py +25 -0
  21. mojo/apps/account/rest/user.py +47 -0
  22. mojo/apps/account/utils/__init__.py +0 -0
  23. mojo/apps/account/utils/jwtoken.py +72 -0
  24. mojo/apps/account/utils/passkeys.py +54 -0
  25. mojo/apps/fileman/README.md +549 -0
  26. mojo/apps/fileman/__init__.py +0 -0
  27. mojo/apps/fileman/apps.py +15 -0
  28. mojo/apps/fileman/backends/__init__.py +117 -0
  29. mojo/apps/fileman/backends/base.py +319 -0
  30. mojo/apps/fileman/backends/filesystem.py +397 -0
  31. mojo/apps/fileman/backends/s3.py +398 -0
  32. mojo/apps/fileman/examples/configurations.py +378 -0
  33. mojo/apps/fileman/examples/usage_example.py +665 -0
  34. mojo/apps/fileman/management/__init__.py +1 -0
  35. mojo/apps/fileman/management/commands/__init__.py +1 -0
  36. mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
  37. mojo/apps/fileman/models/__init__.py +7 -0
  38. mojo/apps/fileman/models/file.py +292 -0
  39. mojo/apps/fileman/models/manager.py +227 -0
  40. mojo/apps/fileman/models/render.py +0 -0
  41. mojo/apps/fileman/rest/__init__ +0 -0
  42. mojo/apps/fileman/rest/__init__.py +23 -0
  43. mojo/apps/fileman/rest/fileman.py +13 -0
  44. mojo/apps/fileman/rest/upload.py +92 -0
  45. mojo/apps/fileman/utils/__init__.py +19 -0
  46. mojo/apps/fileman/utils/upload.py +616 -0
  47. mojo/apps/incident/__init__.py +1 -0
  48. mojo/apps/incident/handlers/__init__.py +3 -0
  49. mojo/apps/incident/handlers/event_handlers.py +142 -0
  50. mojo/apps/incident/migrations/0001_initial.py +83 -0
  51. mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
  52. mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
  53. mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
  54. mojo/apps/incident/migrations/__init__.py +0 -0
  55. mojo/apps/incident/models/__init__.py +3 -0
  56. mojo/apps/incident/models/event.py +135 -0
  57. mojo/apps/incident/models/incident.py +33 -0
  58. mojo/apps/incident/models/rule.py +247 -0
  59. mojo/apps/incident/parsers/__init__.py +0 -0
  60. mojo/apps/incident/parsers/ossec/__init__.py +1 -0
  61. mojo/apps/incident/parsers/ossec/core.py +82 -0
  62. mojo/apps/incident/parsers/ossec/parsed.py +23 -0
  63. mojo/apps/incident/parsers/ossec/rules.py +124 -0
  64. mojo/apps/incident/parsers/ossec/utils.py +169 -0
  65. mojo/apps/incident/reporter.py +42 -0
  66. mojo/apps/incident/rest/__init__.py +2 -0
  67. mojo/apps/incident/rest/event.py +23 -0
  68. mojo/apps/incident/rest/ossec.py +22 -0
  69. mojo/apps/logit/__init__.py +0 -0
  70. mojo/apps/logit/admin.py +37 -0
  71. mojo/apps/logit/migrations/0001_initial.py +32 -0
  72. mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
  73. mojo/apps/logit/migrations/0003_log_level.py +18 -0
  74. mojo/apps/logit/migrations/__init__.py +0 -0
  75. mojo/apps/logit/models/__init__.py +1 -0
  76. mojo/apps/logit/models/log.py +57 -0
  77. mojo/apps/logit/rest.py +9 -0
  78. mojo/apps/metrics/README.md +79 -0
  79. mojo/apps/metrics/__init__.py +12 -0
  80. mojo/apps/metrics/redis_metrics.py +331 -0
  81. mojo/apps/metrics/rest/__init__.py +1 -0
  82. mojo/apps/metrics/rest/base.py +152 -0
  83. mojo/apps/metrics/rest/db.py +0 -0
  84. mojo/apps/metrics/utils.py +227 -0
  85. mojo/apps/notify/README.md +91 -0
  86. mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
  87. mojo/apps/notify/__init__.py +0 -0
  88. mojo/apps/notify/admin.py +52 -0
  89. mojo/apps/notify/handlers/__init__.py +0 -0
  90. mojo/apps/notify/handlers/example_handlers.py +516 -0
  91. mojo/apps/notify/handlers/ses/__init__.py +25 -0
  92. mojo/apps/notify/handlers/ses/bounce.py +0 -0
  93. mojo/apps/notify/handlers/ses/complaint.py +25 -0
  94. mojo/apps/notify/handlers/ses/message.py +86 -0
  95. mojo/apps/notify/management/__init__.py +0 -0
  96. mojo/apps/notify/management/commands/__init__.py +1 -0
  97. mojo/apps/notify/management/commands/process_notifications.py +370 -0
  98. mojo/apps/notify/mod +0 -0
  99. mojo/apps/notify/models/__init__.py +12 -0
  100. mojo/apps/notify/models/account.py +128 -0
  101. mojo/apps/notify/models/attachment.py +24 -0
  102. mojo/apps/notify/models/bounce.py +68 -0
  103. mojo/apps/notify/models/complaint.py +40 -0
  104. mojo/apps/notify/models/inbox.py +113 -0
  105. mojo/apps/notify/models/inbox_message.py +173 -0
  106. mojo/apps/notify/models/outbox.py +129 -0
  107. mojo/apps/notify/models/outbox_message.py +288 -0
  108. mojo/apps/notify/models/template.py +30 -0
  109. mojo/apps/notify/providers/__init__.py +0 -0
  110. mojo/apps/notify/providers/aws.py +73 -0
  111. mojo/apps/notify/rest/__init__.py +0 -0
  112. mojo/apps/notify/rest/ses.py +0 -0
  113. mojo/apps/notify/utils/__init__.py +2 -0
  114. mojo/apps/notify/utils/notifications.py +404 -0
  115. mojo/apps/notify/utils/parsing.py +202 -0
  116. mojo/apps/notify/utils/render.py +144 -0
  117. mojo/apps/tasks/README.md +118 -0
  118. mojo/apps/tasks/__init__.py +11 -0
  119. mojo/apps/tasks/manager.py +489 -0
  120. mojo/apps/tasks/rest/__init__.py +2 -0
  121. mojo/apps/tasks/rest/hooks.py +0 -0
  122. mojo/apps/tasks/rest/tasks.py +62 -0
  123. mojo/apps/tasks/runner.py +174 -0
  124. mojo/apps/tasks/tq_handlers.py +14 -0
  125. mojo/decorators/__init__.py +3 -0
  126. mojo/decorators/auth.py +25 -0
  127. mojo/decorators/cron.py +31 -0
  128. mojo/decorators/http.py +132 -0
  129. mojo/decorators/validate.py +14 -0
  130. mojo/errors.py +88 -0
  131. mojo/helpers/__init__.py +0 -0
  132. mojo/helpers/aws/__init__.py +0 -0
  133. mojo/helpers/aws/client.py +8 -0
  134. mojo/helpers/aws/s3.py +268 -0
  135. mojo/helpers/aws/setup_email.py +0 -0
  136. mojo/helpers/cron.py +79 -0
  137. mojo/helpers/crypto/__init__.py +4 -0
  138. mojo/helpers/crypto/aes.py +60 -0
  139. mojo/helpers/crypto/hash.py +59 -0
  140. mojo/helpers/crypto/privpub/__init__.py +1 -0
  141. mojo/helpers/crypto/privpub/hybrid.py +97 -0
  142. mojo/helpers/crypto/privpub/rsa.py +104 -0
  143. mojo/helpers/crypto/sign.py +36 -0
  144. mojo/helpers/crypto/too.l.py +25 -0
  145. mojo/helpers/crypto/utils.py +26 -0
  146. mojo/helpers/daemon.py +94 -0
  147. mojo/helpers/dates.py +69 -0
  148. mojo/helpers/dns/__init__.py +0 -0
  149. mojo/helpers/dns/godaddy.py +62 -0
  150. mojo/helpers/filetypes.py +128 -0
  151. mojo/helpers/logit.py +310 -0
  152. mojo/helpers/modules.py +95 -0
  153. mojo/helpers/paths.py +63 -0
  154. mojo/helpers/redis.py +10 -0
  155. mojo/helpers/request.py +89 -0
  156. mojo/helpers/request_parser.py +269 -0
  157. mojo/helpers/response.py +14 -0
  158. mojo/helpers/settings.py +146 -0
  159. mojo/helpers/sysinfo.py +140 -0
  160. mojo/helpers/ua.py +0 -0
  161. mojo/middleware/__init__.py +0 -0
  162. mojo/middleware/auth.py +26 -0
  163. mojo/middleware/logging.py +55 -0
  164. mojo/middleware/mojo.py +21 -0
  165. mojo/migrations/0001_initial.py +32 -0
  166. mojo/migrations/__init__.py +0 -0
  167. mojo/models/__init__.py +2 -0
  168. mojo/models/meta.py +262 -0
  169. mojo/models/rest.py +538 -0
  170. mojo/models/secrets.py +59 -0
  171. mojo/rest/__init__.py +1 -0
  172. mojo/rest/info.py +26 -0
  173. mojo/serializers/__init__.py +0 -0
  174. mojo/serializers/models.py +165 -0
  175. mojo/serializers/openapi.py +188 -0
  176. mojo/urls.py +38 -0
  177. mojo/ws4redis/README.md +174 -0
  178. mojo/ws4redis/__init__.py +2 -0
  179. mojo/ws4redis/client.py +283 -0
  180. mojo/ws4redis/connection.py +327 -0
  181. mojo/ws4redis/exceptions.py +32 -0
  182. mojo/ws4redis/redis.py +183 -0
  183. mojo/ws4redis/servers/__init__.py +0 -0
  184. mojo/ws4redis/servers/base.py +86 -0
  185. mojo/ws4redis/servers/django.py +171 -0
  186. mojo/ws4redis/servers/uwsgi.py +63 -0
  187. mojo/ws4redis/settings.py +45 -0
  188. mojo/ws4redis/utf8validator.py +128 -0
  189. mojo/ws4redis/websocket.py +403 -0
  190. testit/__init__.py +0 -0
  191. testit/client.py +147 -0
  192. testit/faker.py +20 -0
  193. testit/helpers.py +198 -0
  194. testit/runner.py +262 -0
@@ -0,0 +1,32 @@
1
+ # Generated by Django 4.2.21 on 2025-05-29 23:29
2
+
3
+ from django.db import migrations, models
4
+ import mojo.models.rest
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Log',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('created', models.DateTimeField(auto_now_add=True, db_index=True)),
20
+ ('kind', models.CharField(db_index=True, default=None, max_length=200, null=True)),
21
+ ('method', models.CharField(default=None, max_length=200, null=True)),
22
+ ('path', models.TextField(db_index=True, default=None, null=True)),
23
+ ('ip', models.CharField(db_index=True, default=None, max_length=32, null=True)),
24
+ ('uid', models.IntegerField(db_index=True, default=0)),
25
+ ('user_agent', models.TextField(default=None)),
26
+ ('log', models.TextField(default=None, null=True)),
27
+ ('model_name', models.TextField(db_index=True, default=None, null=True)),
28
+ ('model_id', models.IntegerField(db_index=True, default=0)),
29
+ ],
30
+ bases=(models.Model, mojo.models.rest.MojoModel),
31
+ ),
32
+ ]
File without changes
@@ -0,0 +1,2 @@
1
+ from .rest import MojoModel
2
+ from .secrets import MojoSecrets
mojo/models/meta.py ADDED
@@ -0,0 +1,262 @@
1
+ from objict import objict
2
+ from django.db import models as dm
3
+ import string
4
+ from rest.encryption import ENCRYPTER, DECRYPTER
5
+ from datetime import datetime, date
6
+
7
+ class MetaDataBase(dm.Model):
8
+ class Meta:
9
+ abstract = True
10
+
11
+ category = dm.CharField(db_index=True, max_length=32, default=None, null=True, blank=True)
12
+ key = dm.CharField(db_index=True, max_length=80)
13
+ value_format = dm.CharField(max_length=16)
14
+ value = dm.TextField()
15
+ int_value = dm.IntegerField(default=None, null=True, blank=True)
16
+ float_value = dm.FloatField(default=None, null=True, blank=True)
17
+
18
+ def set_value(self, value):
19
+ self.value = str(value)
20
+ value_type = type(value)
21
+
22
+ if value_type is int or self.value in ["0", "1"]:
23
+ if value_type is int and value > 2147483647:
24
+ self.value_format = "S"
25
+ return
26
+ self.value_format = "I"
27
+ self.int_value = value
28
+ elif value_type is float:
29
+ self.value_format = "F"
30
+ self.float_value = value
31
+ elif isinstance(value, list):
32
+ self.value_format = "L"
33
+ elif isinstance(value, dict):
34
+ self.value_format = "O"
35
+ elif isinstance(value, str) and len(value) < 9 and value.isdigit():
36
+ self.value_format = "I"
37
+ self.int_value = int(value)
38
+ elif value in ["True", "true", "False", "false"]:
39
+ self.value_format = "I"
40
+ self.int_value = 1 if value.lower() == "true" else 0
41
+ elif isinstance(value, bool):
42
+ self.value_format = "I"
43
+ self.int_value = 1 if value else 0
44
+ else:
45
+ self.value_format = "S"
46
+
47
+ def get_strict_type(self, field_type):
48
+ try:
49
+ return field_type(self.value)
50
+ except (ValueError, TypeError):
51
+ if field_type is bool:
52
+ return self.int_value != 0 if self.value_format == 'I' else self.value.lower() in ['true', '1', 'y', 'yes']
53
+ elif field_type in [date, datetime]:
54
+ return rh.parseDate(self.value)
55
+ return self.value
56
+
57
+ def get_value(self, field_type=None):
58
+ if field_type:
59
+ return self.get_strict_type(field_type)
60
+ if self.value_format == 'I':
61
+ return self.int_value
62
+ elif self.value_format == 'F':
63
+ return self.float_value
64
+ elif self.value_format in ["L", "O"] and self.value:
65
+ try:
66
+ return eval(self.value)
67
+ except Exception:
68
+ pass
69
+ return self.value
70
+
71
+ def __str__(self):
72
+ return f"{self.category}.{self.key}={self.value}" if self.category else f"{self.key}={self.value}"
73
+
74
+ class MetaDataModel:
75
+ def set_metadata(self, request, values=None):
76
+ if not self.id:
77
+ self.save()
78
+
79
+ values = values or request
80
+ if isinstance(values, list):
81
+ values = objict({k: v for item in values if isinstance(item, dict) for k, v in item.items()})
82
+
83
+ if not isinstance(values, dict):
84
+ raise ValueError(f"invalid metadata: {values}")
85
+
86
+ for key, value in values.items():
87
+ cat, key = key.split('.', 1) if '.' in key else (None, key)
88
+ self.set_property(key, value, cat, request=request)
89
+
90
+ def metadata(self):
91
+ return self.get_properties()
92
+
93
+ def remove_properties(self, category=None):
94
+ self.properties.filter(category=category).delete()
95
+
96
+ def get_properties(self, category=None):
97
+ result = {}
98
+ for prop in self.properties.all():
99
+ category_ = prop.category
100
+ key = prop.key
101
+
102
+ if not category_:
103
+ self._add_property_to_result(result, prop)
104
+ continue
105
+
106
+ props = self.get_field_props(category_)
107
+ if props.hidden:
108
+ continue
109
+
110
+ if category_ not in result:
111
+ result[category_] = {}
112
+
113
+ if category_ == "secrets":
114
+ masked_value = "*" * prop.int_value if prop.int_value else "******"
115
+ result[category_][key] = masked_value
116
+ else:
117
+ self._add_property_to_result(result[category_], prop)
118
+
119
+ return result.get(category, {}) if category else result
120
+
121
+ def _add_property_to_result(self, result_dict, prop):
122
+ props = self.get_field_props(prop.key)
123
+ if not props.hidden:
124
+ result_dict[prop.key] = prop.get_value()
125
+
126
+ def get_field_props(self, key):
127
+ self._init_field_props()
128
+ category, key = key.split('.', 1) if '.' in key else (None, key)
129
+ props = objict()
130
+
131
+ if self.__field_props:
132
+ cat_props = self.__field_props.get(category, {})
133
+ self._update_props_with_category(props, cat_props)
134
+
135
+ field_props = self.__field_props.get(key, {})
136
+ self._update_props_with_field(props, field_props)
137
+
138
+ return props
139
+
140
+ def _update_props_with_category(self, props, cat_props):
141
+ if cat_props:
142
+ props.notify = cat_props.get("notify")
143
+ props.requires = cat_props.get("requires")
144
+ props.hidden = cat_props.get("hidden", False)
145
+ on_change_name = cat_props.get("on_change")
146
+ if on_change_name:
147
+ props.on_change = getattr(self, on_change_name, None)
148
+
149
+ def _update_props_with_field(self, props, field_props):
150
+ props.notify = field_props.get("notify", props.notify)
151
+ props.requires = field_props.get("requires", props.requires)
152
+ props.hidden = field_props.get("hidden", props.hidden)
153
+ on_change_name = field_props.get("on_change")
154
+ if on_change_name:
155
+ props.on_change = getattr(self, on_change_name, None)
156
+
157
+ def check_field_perms(self, full_key, props, request=None):
158
+ if not props.requires:
159
+ return True
160
+ if not request or not request.member:
161
+ return False
162
+ if request.member.hasPermission(props.requires) or request.user.is_superuser:
163
+ return True
164
+
165
+ if props.notify and request.member:
166
+ subject = f"permission denied changing protected '{full_key}' field"
167
+ msg = f"permission denied changing protected field '{full_key}'\nby user: {request.user.username}\nfor: {self}"
168
+ request.member.notifyWithPermission(props.notify, subject, msg, email_only=True)
169
+ raise re.PermissionDeniedException(subject, 481)
170
+
171
+ def set_properties(self, data, category=None, request=None, using=None):
172
+ for k, v in data.items():
173
+ self.set_property(k, v, category, request=request, using=using)
174
+
175
+ def set_property(self, key, value, category=None, request=None, using=None, ascii_only=False, encrypted=False):
176
+ if ascii_only and isinstance(value, str):
177
+ value = ''.join(filter(lambda x: x in string.printable, value))
178
+
179
+ if using is None:
180
+ using = getattr(self.RestMeta, "DATABASE", None)
181
+
182
+ if request is None:
183
+ request = rh.getActiveRequest()
184
+
185
+ self._init_field_props()
186
+
187
+ if '.' in key:
188
+ category, key = key.split('.', 1)
189
+
190
+ full_key = f"{category}.{key}" if category else key
191
+ field_props = self.get_field_props(full_key)
192
+
193
+ if not self.check_field_perms(full_key, field_props, request):
194
+ return False
195
+
196
+ prop = self.properties.filter(category=category, key=key).last()
197
+ if not prop and (value is None or value == ""):
198
+ return False
199
+
200
+ has_changed, old_value = self._update_or_create_property(prop, category, key, value, encrypted, using)
201
+
202
+ if has_changed and field_props.on_change:
203
+ field_props.on_change(key, value, old_value, category)
204
+
205
+ self._notify_change_if_required(field_props, full_key, value, request)
206
+
207
+ if hasattr(self, "_recordRestChange"):
208
+ self._recordRestChange(f"metadata.{full_key}", old_value)
209
+
210
+ return has_changed
211
+
212
+ def _update_or_create_property(self, prop, category, key, value, encrypted, using):
213
+ has_changed = False
214
+ old_value = None
215
+
216
+ value_len = len(value) if encrypted else 0
217
+ if encrypted:
218
+ value = ENCRYPTER.encrypt(value)
219
+
220
+ if prop:
221
+ old_value = prop.get_value()
222
+ if value is None or value == "":
223
+ self.properties.filter(category=category, key=key).delete()
224
+ has_changed = True
225
+ else:
226
+ has_changed = str(value) != prop.value
227
+ if has_changed:
228
+ prop.set_value(value)
229
+ if encrypted:
230
+ prop.int_value = value_len
231
+ prop.save(using=using)
232
+ else:
233
+ has_changed = True
234
+ PropClass = self.get_fk_model("properties")
235
+ prop = PropClass(parent=self, key=key, category=category)
236
+ prop.set_value(value)
237
+ prop.save(using=using)
238
+
239
+ return has_changed, old_value
240
+
241
+ def _notify_change_if_required(self, field_props, full_key, value, request):
242
+ if field_props.notify and request and request.member:
243
+ username = request.member.username if request and request.member else "root"
244
+ truncated_value = "***" if value and len(str(value)) > 5 else value
245
+ msg = (f"protected field '{full_key}' changed to '{truncated_value}'\n"
246
+ f"by user: {username}\nfor: {self}")
247
+ request.member.notifyWithPermission(field_props.notify, f"protected '{full_key}' field changed", msg, email_only=True)
248
+
249
+ def get_property(self, key, default=None, category=None, field_type=None, decrypted=False):
250
+ category, key = key.split('.', 1) if '.' in key else (category, key)
251
+
252
+ try:
253
+ prop_value = self.properties.get(category=category, key=key).get_value(field_type)
254
+ return DECRYPTER.decrypt(prop_value) if decrypted and prop_value else prop_value
255
+ except Exception:
256
+ return default
257
+
258
+ def set_secret_property(self, key, value):
259
+ return self.set_property(key, value, category="secrets", encrypted=True)
260
+
261
+ def get_secret_property(self, key, default=None):
262
+ return self.get_property(key, default, "secrets", decrypted=True)