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,142 @@
|
|
1
|
+
"""
|
2
|
+
Event handlers for incident processing.
|
3
|
+
|
4
|
+
This module contains handlers for processing incident events based on different
|
5
|
+
handler types (task, email, notify). These handlers are used by the RuleSet.run_handler
|
6
|
+
method to handle events that match rule criteria.
|
7
|
+
"""
|
8
|
+
|
9
|
+
class TaskHandler:
|
10
|
+
"""
|
11
|
+
Handler for executing tasks based on events.
|
12
|
+
|
13
|
+
This handler executes a named task with the given parameters
|
14
|
+
when an event matches rule criteria.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
handler_name (str): The name of the task to execute.
|
18
|
+
params (dict): Parameters to pass to the task.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, handler_name, **params):
|
22
|
+
"""
|
23
|
+
Initialize a TaskHandler with a task name and parameters.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
handler_name (str): The name of the task to execute.
|
27
|
+
**params: Parameters to pass to the task.
|
28
|
+
"""
|
29
|
+
self.handler_name = handler_name
|
30
|
+
self.params = params
|
31
|
+
|
32
|
+
def run(self, event):
|
33
|
+
"""
|
34
|
+
Execute the task for the given event.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
event (Event): The event that triggered this handler.
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
bool: True if the task was executed successfully, False otherwise.
|
41
|
+
"""
|
42
|
+
# TODO: Implement actual task execution logic
|
43
|
+
# For example, using Celery to execute tasks asynchronously
|
44
|
+
try:
|
45
|
+
# Example implementation:
|
46
|
+
# from mojo.tasks import execute_task
|
47
|
+
# result = execute_task.delay(self.handler_name, event=event, **self.params)
|
48
|
+
# return result.successful()
|
49
|
+
return True
|
50
|
+
except Exception as e:
|
51
|
+
# Log the error
|
52
|
+
# logger.error(f"Error executing task {self.handler_name}: {e}")
|
53
|
+
return False
|
54
|
+
|
55
|
+
|
56
|
+
class EmailHandler:
|
57
|
+
"""
|
58
|
+
Handler for sending email notifications based on events.
|
59
|
+
|
60
|
+
This handler sends an email to the specified recipient
|
61
|
+
when an event matches rule criteria.
|
62
|
+
|
63
|
+
Attributes:
|
64
|
+
recipient (str): The email address to send notifications to.
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self, recipient):
|
68
|
+
"""
|
69
|
+
Initialize an EmailHandler with a recipient.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
recipient (str): The email address to send notifications to.
|
73
|
+
"""
|
74
|
+
self.recipient = recipient
|
75
|
+
|
76
|
+
def run(self, event):
|
77
|
+
"""
|
78
|
+
Send an email notification for the given event.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
event (Event): The event that triggered this handler.
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
bool: True if the email was sent successfully, False otherwise.
|
85
|
+
"""
|
86
|
+
# TODO: Implement actual email sending logic
|
87
|
+
try:
|
88
|
+
# Example implementation:
|
89
|
+
# from mojo.helpers.mail import send_mail
|
90
|
+
# subject = f"Incident Alert: {event.name}"
|
91
|
+
# body = f"An incident has been detected:\n\n{event.details}\n\nMetadata: {event.metadata}"
|
92
|
+
# result = send_mail(subject, body, [self.recipient])
|
93
|
+
# return result
|
94
|
+
return True
|
95
|
+
except Exception as e:
|
96
|
+
# Log the error
|
97
|
+
# logger.error(f"Error sending email to {self.recipient}: {e}")
|
98
|
+
return False
|
99
|
+
|
100
|
+
|
101
|
+
class NotifyHandler:
|
102
|
+
"""
|
103
|
+
Handler for sending notifications through various channels based on events.
|
104
|
+
|
105
|
+
This handler can send notifications through multiple channels (SMS, push, etc.)
|
106
|
+
when an event matches rule criteria.
|
107
|
+
|
108
|
+
Attributes:
|
109
|
+
recipient (str): The recipient identifier (can be a username, user ID, etc.).
|
110
|
+
"""
|
111
|
+
|
112
|
+
def __init__(self, recipient):
|
113
|
+
"""
|
114
|
+
Initialize a NotifyHandler with a recipient.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
recipient (str): The recipient identifier.
|
118
|
+
"""
|
119
|
+
self.recipient = recipient
|
120
|
+
|
121
|
+
def run(self, event):
|
122
|
+
"""
|
123
|
+
Send a notification for the given event.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
event (Event): The event that triggered this handler.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
bool: True if the notification was sent successfully, False otherwise.
|
130
|
+
"""
|
131
|
+
# TODO: Implement actual notification logic
|
132
|
+
try:
|
133
|
+
# Example implementation:
|
134
|
+
# from mojo.helpers.notifications import send_notification
|
135
|
+
# message = f"Incident Alert: {event.name}\n{event.details}"
|
136
|
+
# result = send_notification(self.recipient, message, metadata=event.metadata)
|
137
|
+
# return result
|
138
|
+
return True
|
139
|
+
except Exception as e:
|
140
|
+
# Log the error
|
141
|
+
# logger.error(f"Error sending notification to {self.recipient}: {e}")
|
142
|
+
return False
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-06-04 02:22
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
import mojo.models.rest
|
6
|
+
|
7
|
+
|
8
|
+
class Migration(migrations.Migration):
|
9
|
+
|
10
|
+
initial = True
|
11
|
+
|
12
|
+
dependencies = [
|
13
|
+
]
|
14
|
+
|
15
|
+
operations = [
|
16
|
+
migrations.CreateModel(
|
17
|
+
name='Incident',
|
18
|
+
fields=[
|
19
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
20
|
+
('created', models.DateTimeField(auto_now_add=True, db_index=True)),
|
21
|
+
('priority', models.IntegerField(db_index=True, default=0)),
|
22
|
+
('state', models.IntegerField(db_index=True, default=0)),
|
23
|
+
('category', models.CharField(db_index=True, max_length=124)),
|
24
|
+
('title', models.TextField(default=None, null=True)),
|
25
|
+
('details', models.TextField(default=None, null=True)),
|
26
|
+
('model_name', models.TextField(db_index=True, default=None, null=True)),
|
27
|
+
('model_id', models.IntegerField(db_index=True, default=0)),
|
28
|
+
('source_ip', models.CharField(blank=True, db_index=True, default=None, max_length=16, null=True)),
|
29
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
30
|
+
],
|
31
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
32
|
+
),
|
33
|
+
migrations.CreateModel(
|
34
|
+
name='RuleSet',
|
35
|
+
fields=[
|
36
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
37
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
38
|
+
('modified', models.DateTimeField(auto_now=True)),
|
39
|
+
('priority', models.IntegerField(db_index=True, default=0)),
|
40
|
+
('category', models.CharField(db_index=True, max_length=124)),
|
41
|
+
('name', models.TextField(default=None, null=True)),
|
42
|
+
('bundle', models.IntegerField(default=0)),
|
43
|
+
('bundle_by', models.IntegerField(default=3)),
|
44
|
+
('match_by', models.IntegerField(default=0)),
|
45
|
+
('handler', models.TextField(default=None, null=True)),
|
46
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
47
|
+
],
|
48
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
49
|
+
),
|
50
|
+
migrations.CreateModel(
|
51
|
+
name='Rule',
|
52
|
+
fields=[
|
53
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
54
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
55
|
+
('modified', models.DateTimeField(auto_now=True)),
|
56
|
+
('name', models.TextField(default=None, null=True)),
|
57
|
+
('index', models.IntegerField(db_index=True, default=0)),
|
58
|
+
('comparator', models.CharField(default='==', max_length=32)),
|
59
|
+
('field_name', models.CharField(default=None, max_length=124, null=True)),
|
60
|
+
('value', models.CharField(default='', max_length=124)),
|
61
|
+
('value_type', models.CharField(default='int', max_length=10)),
|
62
|
+
('is_required', models.IntegerField(default=0)),
|
63
|
+
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='incident.ruleset')),
|
64
|
+
],
|
65
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
66
|
+
),
|
67
|
+
migrations.CreateModel(
|
68
|
+
name='Event',
|
69
|
+
fields=[
|
70
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
71
|
+
('created', models.DateTimeField(auto_now_add=True, db_index=True)),
|
72
|
+
('level', models.IntegerField(db_index=True, default=0)),
|
73
|
+
('category', models.CharField(db_index=True, max_length=124)),
|
74
|
+
('title', models.TextField(default=None, null=True)),
|
75
|
+
('details', models.TextField(default=None, null=True)),
|
76
|
+
('model_name', models.TextField(db_index=True, default=None, null=True)),
|
77
|
+
('model_id', models.IntegerField(db_index=True, default=0)),
|
78
|
+
('metadata', models.JSONField(blank=True, default=dict)),
|
79
|
+
('incident', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='events', to='incident.incident')),
|
80
|
+
],
|
81
|
+
bases=(models.Model, mojo.models.rest.MojoModel),
|
82
|
+
),
|
83
|
+
]
|
mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-06-04 13:42
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
import django.db.models.deletion
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('incident', '0001_initial'),
|
11
|
+
]
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.RenameField(
|
15
|
+
model_name='ruleset',
|
16
|
+
old_name='bundle',
|
17
|
+
new_name='bundle_minutes',
|
18
|
+
),
|
19
|
+
migrations.AddField(
|
20
|
+
model_name='event',
|
21
|
+
name='hostname',
|
22
|
+
field=models.CharField(db_index=True, default=None, max_length=16, null=True),
|
23
|
+
),
|
24
|
+
migrations.AddField(
|
25
|
+
model_name='event',
|
26
|
+
name='source_ip',
|
27
|
+
field=models.CharField(db_index=True, default=None, max_length=16, null=True),
|
28
|
+
),
|
29
|
+
migrations.AddField(
|
30
|
+
model_name='incident',
|
31
|
+
name='hostname',
|
32
|
+
field=models.CharField(db_index=True, default=None, max_length=16, null=True),
|
33
|
+
),
|
34
|
+
migrations.AddField(
|
35
|
+
model_name='incident',
|
36
|
+
name='rule_set',
|
37
|
+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incidents', to='incident.ruleset'),
|
38
|
+
),
|
39
|
+
migrations.AlterField(
|
40
|
+
model_name='incident',
|
41
|
+
name='source_ip',
|
42
|
+
field=models.CharField(db_index=True, default=None, max_length=16, null=True),
|
43
|
+
),
|
44
|
+
]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-06-04 14:30
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('incident', '0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='event',
|
15
|
+
name='model_id',
|
16
|
+
field=models.IntegerField(db_index=True, default=None, null=True),
|
17
|
+
),
|
18
|
+
]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-06-04 17:22
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('incident', '0003_alter_event_model_id'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='incident',
|
15
|
+
name='model_id',
|
16
|
+
field=models.IntegerField(db_index=True, default=None, null=True),
|
17
|
+
),
|
18
|
+
]
|
File without changes
|
@@ -0,0 +1,135 @@
|
|
1
|
+
from mojo.apps.metrics import record
|
2
|
+
from django.db import models
|
3
|
+
from mojo.models import MojoModel
|
4
|
+
from mojo.helpers import dates
|
5
|
+
from mojo.helpers.settings import settings
|
6
|
+
from mojo.apps import metrics
|
7
|
+
|
8
|
+
|
9
|
+
INCIDENT_LEVEL_THRESHOLD = settings.get('INCIDENT_LEVEL_THRESHOLD', 7)
|
10
|
+
|
11
|
+
class Event(models.Model, MojoModel):
|
12
|
+
"""
|
13
|
+
Event model.
|
14
|
+
|
15
|
+
Level 0–3: Informational or low importance
|
16
|
+
Level 4–7: Warning or potential issue
|
17
|
+
Level 8–15: Increasing severity, with Level 15 being critical
|
18
|
+
"""
|
19
|
+
created = models.DateTimeField(auto_now_add=True, editable=False, db_index=True)
|
20
|
+
|
21
|
+
level = models.IntegerField(default=0, db_index=True)
|
22
|
+
category = models.CharField(max_length=124, db_index=True)
|
23
|
+
source_ip = models.CharField(max_length=16, null=True, default=None, db_index=True)
|
24
|
+
hostname = models.CharField(max_length=16, null=True, default=None, db_index=True)
|
25
|
+
|
26
|
+
title = models.TextField(default=None, null=True)
|
27
|
+
details = models.TextField(default=None, null=True)
|
28
|
+
|
29
|
+
model_name = models.TextField(default=None, null=True, db_index=True)
|
30
|
+
model_id = models.IntegerField(default=None, null=True, db_index=True)
|
31
|
+
|
32
|
+
incident = models.ForeignKey("incident.Incident", null=True, related_name="events",
|
33
|
+
default=None, on_delete=models.CASCADE)
|
34
|
+
|
35
|
+
# JSON-based metadata field
|
36
|
+
metadata = models.JSONField(default=dict, blank=True)
|
37
|
+
|
38
|
+
class RestMeta:
|
39
|
+
SEARCH_FIELDS = ["details"]
|
40
|
+
VIEW_PERMS = ["view_incidents"]
|
41
|
+
CREATE_PERMS = None
|
42
|
+
|
43
|
+
def sync_metadata(self):
|
44
|
+
# Gather all field values into the metadata
|
45
|
+
field_values = {
|
46
|
+
'level': self.level,
|
47
|
+
'category': self.category,
|
48
|
+
'source_ip': self.source_ip,
|
49
|
+
'title': self.title,
|
50
|
+
'details': self.details,
|
51
|
+
'model_name': self.model_name,
|
52
|
+
'model_id': self.model_id }
|
53
|
+
# Update the metadata with these values
|
54
|
+
self.metadata.update(field_values)
|
55
|
+
|
56
|
+
def publish(self):
|
57
|
+
from mojo.apps.incident.models import RuleSet
|
58
|
+
# Find the RuleSet by category
|
59
|
+
self.record_event_metrics()
|
60
|
+
rule_set = RuleSet.check_by_category(self.category, self)
|
61
|
+
|
62
|
+
if rule_set or self.level >= INCIDENT_LEVEL_THRESHOLD:
|
63
|
+
incident, created = self.get_or_create_incident(rule_set)
|
64
|
+
self.link_to_incident(incident)
|
65
|
+
if rule_set and created:
|
66
|
+
rule_set.run_handler(self, incident)
|
67
|
+
|
68
|
+
def record_event_metrics(self):
|
69
|
+
if settings.INCIDENT_EVENT_METRICS:
|
70
|
+
metrics.record('incident_events', account="incident",
|
71
|
+
min_granularity=settings.get("INCIDENT_METRICS_MIN_GRANULARITY", "hours"))
|
72
|
+
|
73
|
+
def record_incident_metrics(self):
|
74
|
+
if settings.INCIDENT_EVENT_METRICS:
|
75
|
+
metrics.record('incidents', account="incident",
|
76
|
+
min_granularity=settings.get("INCIDENT_METRICS_MIN_GRANULARITY", "hours"))
|
77
|
+
|
78
|
+
def get_or_create_incident(self, rule_set=None):
|
79
|
+
"""
|
80
|
+
Gets or creates an incident based on the event's level and rule set bundle criteria.
|
81
|
+
"""
|
82
|
+
from mojo.apps.incident.models import Incident
|
83
|
+
|
84
|
+
incident = None
|
85
|
+
created = False
|
86
|
+
if rule_set is not None and rule_set.bundle_by > 0:
|
87
|
+
bundle_criteria = self.determine_bundle_criteria(rule_set)
|
88
|
+
incident = Incident.objects.filter(**bundle_criteria).first()
|
89
|
+
|
90
|
+
if not incident:
|
91
|
+
# Create a new incident if none found
|
92
|
+
created = True
|
93
|
+
incident = Incident(
|
94
|
+
priority=self.level,
|
95
|
+
state=0,
|
96
|
+
category=self.category,
|
97
|
+
title=self.title,
|
98
|
+
details=self.details,
|
99
|
+
hostname=self.hostname,
|
100
|
+
model_name=self.model_name,
|
101
|
+
model_id=self.model_id,
|
102
|
+
source_ip=self.source_ip
|
103
|
+
)
|
104
|
+
incident.metadata.update(self.metadata)
|
105
|
+
incident.save()
|
106
|
+
self.record_incident_metrics()
|
107
|
+
|
108
|
+
return incident, created
|
109
|
+
|
110
|
+
def determine_bundle_criteria(self, rule_set):
|
111
|
+
"""
|
112
|
+
Determines the bundle criteria based on the rule set configuration.
|
113
|
+
"""
|
114
|
+
bundle_criteria = {
|
115
|
+
"category": self.category
|
116
|
+
}
|
117
|
+
if rule_set.bundle_minutes:
|
118
|
+
bundle_criteria['created__gte'] = dates.subtract(minutes=rule_set.bundle_minutes)
|
119
|
+
if rule_set.bundle_by in [1, 5, 6, 9]: # hostname or hostname and others
|
120
|
+
bundle_criteria['hostname'] = self.hostname
|
121
|
+
if rule_set.bundle_by in [2, 3, 5, 6, 7, 8]: # model and combinations
|
122
|
+
bundle_criteria['model_name'] = self.model_name
|
123
|
+
if rule_set.bundle_by in [3, 6, 8]: # model_id where applicable
|
124
|
+
bundle_criteria['model_id'] = self.model_id
|
125
|
+
if rule_set.bundle_by in [4, 7, 8, 9]: # source_ip and combinations
|
126
|
+
bundle_criteria['source_ip'] = self.source_ip
|
127
|
+
|
128
|
+
return bundle_criteria
|
129
|
+
|
130
|
+
def link_to_incident(self, incident):
|
131
|
+
"""
|
132
|
+
Links the event to an incident and saves the event.
|
133
|
+
"""
|
134
|
+
self.incident = incident
|
135
|
+
self.save()
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from mojo.models import MojoModel
|
3
|
+
|
4
|
+
|
5
|
+
class Incident(models.Model, MojoModel):
|
6
|
+
"""
|
7
|
+
Incident model.
|
8
|
+
"""
|
9
|
+
created = models.DateTimeField(auto_now_add=True, editable=False, db_index=True)
|
10
|
+
|
11
|
+
priority = models.IntegerField(default=0, db_index=True)
|
12
|
+
state = models.IntegerField(default=0, db_index=True)
|
13
|
+
category = models.CharField(max_length=124, db_index=True)
|
14
|
+
title = models.TextField(default=None, null=True)
|
15
|
+
details = models.TextField(default=None, null=True)
|
16
|
+
|
17
|
+
model_name = models.TextField(default=None, null=True, db_index=True)
|
18
|
+
model_id = models.IntegerField(default=None, null=True, db_index=True)
|
19
|
+
|
20
|
+
# the
|
21
|
+
source_ip = models.CharField(max_length=16, null=True, default=None, db_index=True)
|
22
|
+
hostname = models.CharField(max_length=16, null=True, default=None, db_index=True)
|
23
|
+
|
24
|
+
# JSON-based metadata field
|
25
|
+
metadata = models.JSONField(default=dict, blank=True)
|
26
|
+
|
27
|
+
rule_set = models.ForeignKey("incident.Ruleset", on_delete=models.SET_NULL,
|
28
|
+
null=True, blank=True, related_name="incidents")
|
29
|
+
|
30
|
+
class RestMeta:
|
31
|
+
SEARCH_FIELDS = ["details"]
|
32
|
+
VIEW_PERMS = ["view_incidents"]
|
33
|
+
CREATE_PERMS = None
|