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.
- django_nativemojo-0.1.10.dist-info/LICENSE +19 -0
- django_nativemojo-0.1.10.dist-info/METADATA +96 -0
- django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
- django_nativemojo-0.1.10.dist-info/RECORD +194 -0
- django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
- mojo/__init__.py +3 -0
- mojo/apps/account/__init__.py +1 -0
- mojo/apps/account/admin.py +91 -0
- mojo/apps/account/apps.py +16 -0
- mojo/apps/account/migrations/0001_initial.py +77 -0
- mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
- mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
- mojo/apps/account/migrations/__init__.py +0 -0
- mojo/apps/account/models/__init__.py +3 -0
- mojo/apps/account/models/group.py +98 -0
- mojo/apps/account/models/member.py +95 -0
- mojo/apps/account/models/pkey.py +18 -0
- mojo/apps/account/models/user.py +211 -0
- mojo/apps/account/rest/__init__.py +3 -0
- mojo/apps/account/rest/group.py +25 -0
- mojo/apps/account/rest/user.py +47 -0
- mojo/apps/account/utils/__init__.py +0 -0
- mojo/apps/account/utils/jwtoken.py +72 -0
- mojo/apps/account/utils/passkeys.py +54 -0
- mojo/apps/fileman/README.md +549 -0
- mojo/apps/fileman/__init__.py +0 -0
- mojo/apps/fileman/apps.py +15 -0
- mojo/apps/fileman/backends/__init__.py +117 -0
- mojo/apps/fileman/backends/base.py +319 -0
- mojo/apps/fileman/backends/filesystem.py +397 -0
- mojo/apps/fileman/backends/s3.py +398 -0
- mojo/apps/fileman/examples/configurations.py +378 -0
- mojo/apps/fileman/examples/usage_example.py +665 -0
- mojo/apps/fileman/management/__init__.py +1 -0
- mojo/apps/fileman/management/commands/__init__.py +1 -0
- mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
- mojo/apps/fileman/models/__init__.py +7 -0
- mojo/apps/fileman/models/file.py +292 -0
- mojo/apps/fileman/models/manager.py +227 -0
- mojo/apps/fileman/models/render.py +0 -0
- mojo/apps/fileman/rest/__init__ +0 -0
- mojo/apps/fileman/rest/__init__.py +23 -0
- mojo/apps/fileman/rest/fileman.py +13 -0
- mojo/apps/fileman/rest/upload.py +92 -0
- mojo/apps/fileman/utils/__init__.py +19 -0
- mojo/apps/fileman/utils/upload.py +616 -0
- mojo/apps/incident/__init__.py +1 -0
- mojo/apps/incident/handlers/__init__.py +3 -0
- mojo/apps/incident/handlers/event_handlers.py +142 -0
- mojo/apps/incident/migrations/0001_initial.py +83 -0
- mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
- mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
- mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
- mojo/apps/incident/migrations/__init__.py +0 -0
- mojo/apps/incident/models/__init__.py +3 -0
- mojo/apps/incident/models/event.py +135 -0
- mojo/apps/incident/models/incident.py +33 -0
- mojo/apps/incident/models/rule.py +247 -0
- mojo/apps/incident/parsers/__init__.py +0 -0
- mojo/apps/incident/parsers/ossec/__init__.py +1 -0
- mojo/apps/incident/parsers/ossec/core.py +82 -0
- mojo/apps/incident/parsers/ossec/parsed.py +23 -0
- mojo/apps/incident/parsers/ossec/rules.py +124 -0
- mojo/apps/incident/parsers/ossec/utils.py +169 -0
- mojo/apps/incident/reporter.py +42 -0
- mojo/apps/incident/rest/__init__.py +2 -0
- mojo/apps/incident/rest/event.py +23 -0
- mojo/apps/incident/rest/ossec.py +22 -0
- mojo/apps/logit/__init__.py +0 -0
- mojo/apps/logit/admin.py +37 -0
- mojo/apps/logit/migrations/0001_initial.py +32 -0
- mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
- mojo/apps/logit/migrations/0003_log_level.py +18 -0
- mojo/apps/logit/migrations/__init__.py +0 -0
- mojo/apps/logit/models/__init__.py +1 -0
- mojo/apps/logit/models/log.py +57 -0
- mojo/apps/logit/rest.py +9 -0
- mojo/apps/metrics/README.md +79 -0
- mojo/apps/metrics/__init__.py +12 -0
- mojo/apps/metrics/redis_metrics.py +331 -0
- mojo/apps/metrics/rest/__init__.py +1 -0
- mojo/apps/metrics/rest/base.py +152 -0
- mojo/apps/metrics/rest/db.py +0 -0
- mojo/apps/metrics/utils.py +227 -0
- mojo/apps/notify/README.md +91 -0
- mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
- mojo/apps/notify/__init__.py +0 -0
- mojo/apps/notify/admin.py +52 -0
- mojo/apps/notify/handlers/__init__.py +0 -0
- mojo/apps/notify/handlers/example_handlers.py +516 -0
- mojo/apps/notify/handlers/ses/__init__.py +25 -0
- mojo/apps/notify/handlers/ses/bounce.py +0 -0
- mojo/apps/notify/handlers/ses/complaint.py +25 -0
- mojo/apps/notify/handlers/ses/message.py +86 -0
- mojo/apps/notify/management/__init__.py +0 -0
- mojo/apps/notify/management/commands/__init__.py +1 -0
- mojo/apps/notify/management/commands/process_notifications.py +370 -0
- mojo/apps/notify/mod +0 -0
- mojo/apps/notify/models/__init__.py +12 -0
- mojo/apps/notify/models/account.py +128 -0
- mojo/apps/notify/models/attachment.py +24 -0
- mojo/apps/notify/models/bounce.py +68 -0
- mojo/apps/notify/models/complaint.py +40 -0
- mojo/apps/notify/models/inbox.py +113 -0
- mojo/apps/notify/models/inbox_message.py +173 -0
- mojo/apps/notify/models/outbox.py +129 -0
- mojo/apps/notify/models/outbox_message.py +288 -0
- mojo/apps/notify/models/template.py +30 -0
- mojo/apps/notify/providers/__init__.py +0 -0
- mojo/apps/notify/providers/aws.py +73 -0
- mojo/apps/notify/rest/__init__.py +0 -0
- mojo/apps/notify/rest/ses.py +0 -0
- mojo/apps/notify/utils/__init__.py +2 -0
- mojo/apps/notify/utils/notifications.py +404 -0
- mojo/apps/notify/utils/parsing.py +202 -0
- mojo/apps/notify/utils/render.py +144 -0
- mojo/apps/tasks/README.md +118 -0
- mojo/apps/tasks/__init__.py +11 -0
- mojo/apps/tasks/manager.py +489 -0
- mojo/apps/tasks/rest/__init__.py +2 -0
- mojo/apps/tasks/rest/hooks.py +0 -0
- mojo/apps/tasks/rest/tasks.py +62 -0
- mojo/apps/tasks/runner.py +174 -0
- mojo/apps/tasks/tq_handlers.py +14 -0
- mojo/decorators/__init__.py +3 -0
- mojo/decorators/auth.py +25 -0
- mojo/decorators/cron.py +31 -0
- mojo/decorators/http.py +132 -0
- mojo/decorators/validate.py +14 -0
- mojo/errors.py +88 -0
- mojo/helpers/__init__.py +0 -0
- mojo/helpers/aws/__init__.py +0 -0
- mojo/helpers/aws/client.py +8 -0
- mojo/helpers/aws/s3.py +268 -0
- mojo/helpers/aws/setup_email.py +0 -0
- mojo/helpers/cron.py +79 -0
- mojo/helpers/crypto/__init__.py +4 -0
- mojo/helpers/crypto/aes.py +60 -0
- mojo/helpers/crypto/hash.py +59 -0
- mojo/helpers/crypto/privpub/__init__.py +1 -0
- mojo/helpers/crypto/privpub/hybrid.py +97 -0
- mojo/helpers/crypto/privpub/rsa.py +104 -0
- mojo/helpers/crypto/sign.py +36 -0
- mojo/helpers/crypto/too.l.py +25 -0
- mojo/helpers/crypto/utils.py +26 -0
- mojo/helpers/daemon.py +94 -0
- mojo/helpers/dates.py +69 -0
- mojo/helpers/dns/__init__.py +0 -0
- mojo/helpers/dns/godaddy.py +62 -0
- mojo/helpers/filetypes.py +128 -0
- mojo/helpers/logit.py +310 -0
- mojo/helpers/modules.py +95 -0
- mojo/helpers/paths.py +63 -0
- mojo/helpers/redis.py +10 -0
- mojo/helpers/request.py +89 -0
- mojo/helpers/request_parser.py +269 -0
- mojo/helpers/response.py +14 -0
- mojo/helpers/settings.py +146 -0
- mojo/helpers/sysinfo.py +140 -0
- mojo/helpers/ua.py +0 -0
- mojo/middleware/__init__.py +0 -0
- mojo/middleware/auth.py +26 -0
- mojo/middleware/logging.py +55 -0
- mojo/middleware/mojo.py +21 -0
- mojo/migrations/0001_initial.py +32 -0
- mojo/migrations/__init__.py +0 -0
- mojo/models/__init__.py +2 -0
- mojo/models/meta.py +262 -0
- mojo/models/rest.py +538 -0
- mojo/models/secrets.py +59 -0
- mojo/rest/__init__.py +1 -0
- mojo/rest/info.py +26 -0
- mojo/serializers/__init__.py +0 -0
- mojo/serializers/models.py +165 -0
- mojo/serializers/openapi.py +188 -0
- mojo/urls.py +38 -0
- mojo/ws4redis/README.md +174 -0
- mojo/ws4redis/__init__.py +2 -0
- mojo/ws4redis/client.py +283 -0
- mojo/ws4redis/connection.py +327 -0
- mojo/ws4redis/exceptions.py +32 -0
- mojo/ws4redis/redis.py +183 -0
- mojo/ws4redis/servers/__init__.py +0 -0
- mojo/ws4redis/servers/base.py +86 -0
- mojo/ws4redis/servers/django.py +171 -0
- mojo/ws4redis/servers/uwsgi.py +63 -0
- mojo/ws4redis/settings.py +45 -0
- mojo/ws4redis/utf8validator.py +128 -0
- mojo/ws4redis/websocket.py +403 -0
- testit/__init__.py +0 -0
- testit/client.py +147 -0
- testit/faker.py +20 -0
- testit/helpers.py +198 -0
- 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
|
mojo/models/__init__.py
ADDED
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)
|