django-log-formatter-asim 1.1.0a4__py3-none-any.whl → 1.2.0__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_log_formatter_asim/events/__init__.py +7 -2
- django_log_formatter_asim/events/account_management.py +133 -0
- django_log_formatter_asim/events/authentication.py +133 -141
- django_log_formatter_asim/events/common.py +80 -12
- django_log_formatter_asim/events/file_activity.py +159 -160
- django_log_formatter_asim-1.2.0.dist-info/METADATA +399 -0
- django_log_formatter_asim-1.2.0.dist-info/RECORD +11 -0
- {django_log_formatter_asim-1.1.0a4.dist-info → django_log_formatter_asim-1.2.0.dist-info}/WHEEL +1 -1
- django_log_formatter_asim-1.1.0a4.dist-info/METADATA +0 -176
- django_log_formatter_asim-1.1.0a4.dist-info/RECORD +0 -10
- {django_log_formatter_asim-1.1.0a4.dist-info → django_log_formatter_asim-1.2.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
1
|
+
from .account_management import LogAccountManagement
|
|
2
|
+
from .authentication import LogAuthentication
|
|
3
|
+
from .file_activity import LogFileActivity
|
|
4
|
+
|
|
5
|
+
log_account_management = LogAccountManagement()
|
|
6
|
+
log_authentication = LogAuthentication()
|
|
7
|
+
log_file_activity = LogFileActivity()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing import TypedDict
|
|
6
|
+
|
|
7
|
+
from django.http import HttpRequest
|
|
8
|
+
|
|
9
|
+
from .common import Activity
|
|
10
|
+
from .common import Client
|
|
11
|
+
from .common import LoggedInUser
|
|
12
|
+
from .common import Result
|
|
13
|
+
from .common import Server
|
|
14
|
+
from .common import Severity
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FileActivityEvent(str, Enum):
|
|
18
|
+
UserCreated = "UserCreated"
|
|
19
|
+
UserDeleted = "UserDeleted"
|
|
20
|
+
UserModified = "UserModified"
|
|
21
|
+
UserLocked = "UserLocked"
|
|
22
|
+
UserUnlocked = "UserUnlocked"
|
|
23
|
+
UserDisabled = "UserDisabled"
|
|
24
|
+
UserEnabled = "UserEnabled"
|
|
25
|
+
PasswordChanged = "PasswordChanged"
|
|
26
|
+
PasswordReset = "PasswordReset"
|
|
27
|
+
GroupCreated = "GroupCreated"
|
|
28
|
+
GroupDeleted = "GroupDeleted"
|
|
29
|
+
GroupModified = "GroupModified"
|
|
30
|
+
UserAddedToGroup = "UserAddedToGroup"
|
|
31
|
+
UserRemovedFromGroup = "UserRemovedFromGroup"
|
|
32
|
+
GroupEnumerated = "GroupEnumerated"
|
|
33
|
+
UserRead = "UserRead"
|
|
34
|
+
GroupRead = "GroupRead"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Account(TypedDict, total=False):
|
|
38
|
+
"""Dictionary to represent details of the account management event."""
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
If a user was managed, the username of that user
|
|
42
|
+
"""
|
|
43
|
+
username: Optional[str]
|
|
44
|
+
"""If a group was managed, the name of the group."""
|
|
45
|
+
group: Optional[str]
|
|
46
|
+
"""
|
|
47
|
+
If the Account Management event is one of the following.
|
|
48
|
+
|
|
49
|
+
- UserModified
|
|
50
|
+
- GroupModified
|
|
51
|
+
|
|
52
|
+
Details of the property which was changed, in the form:
|
|
53
|
+
("propertyName", "oldValue", "newValue")
|
|
54
|
+
"""
|
|
55
|
+
changed: tuple[str, str, str]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LogAccountManagement(Activity):
|
|
59
|
+
Event = FileActivityEvent
|
|
60
|
+
Result = Result
|
|
61
|
+
Severity = Severity
|
|
62
|
+
|
|
63
|
+
def __call__(
|
|
64
|
+
self,
|
|
65
|
+
request: HttpRequest,
|
|
66
|
+
event: Event,
|
|
67
|
+
account: Account,
|
|
68
|
+
result: Result,
|
|
69
|
+
user: Optional[LoggedInUser] = None,
|
|
70
|
+
server: Optional[Server] = None,
|
|
71
|
+
client: Optional[Client] = None,
|
|
72
|
+
severity: Optional[Severity] = None,
|
|
73
|
+
time_generated: Optional[datetime.datetime] = None,
|
|
74
|
+
result_details: Optional[str] = None,
|
|
75
|
+
message: Optional[str] = None,
|
|
76
|
+
):
|
|
77
|
+
self._log_account_management(
|
|
78
|
+
request,
|
|
79
|
+
event,
|
|
80
|
+
account,
|
|
81
|
+
result,
|
|
82
|
+
{} if user == None else user,
|
|
83
|
+
{} if server == None else server,
|
|
84
|
+
{} if client == None else client,
|
|
85
|
+
time_generated or datetime.datetime.now(tz=datetime.timezone.utc),
|
|
86
|
+
severity,
|
|
87
|
+
result_details,
|
|
88
|
+
message,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _log_account_management(
|
|
92
|
+
self,
|
|
93
|
+
request: HttpRequest,
|
|
94
|
+
event: Event,
|
|
95
|
+
account: Account,
|
|
96
|
+
result: Result,
|
|
97
|
+
user: LoggedInUser,
|
|
98
|
+
server: Server,
|
|
99
|
+
client: Client,
|
|
100
|
+
event_created: datetime.datetime,
|
|
101
|
+
severity: Optional[Severity] = None,
|
|
102
|
+
result_details: Optional[str] = None,
|
|
103
|
+
message: Optional[str] = None,
|
|
104
|
+
):
|
|
105
|
+
log = {
|
|
106
|
+
"EventSchema": "UserManagement",
|
|
107
|
+
"EventSchemaVersion": "0.1.1",
|
|
108
|
+
"EventType": event,
|
|
109
|
+
}
|
|
110
|
+
log.update(
|
|
111
|
+
self._activity_fields(
|
|
112
|
+
request, event_created, result, server, client, severity, result_details, message
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if "username" in user:
|
|
117
|
+
log["ActorUsername"] = user["username"]
|
|
118
|
+
elif hasattr(request, "user") and request.user.username:
|
|
119
|
+
log["ActorUsername"] = request.user.username
|
|
120
|
+
|
|
121
|
+
if "username" in account:
|
|
122
|
+
log["TargetUsername"] = account["username"]
|
|
123
|
+
|
|
124
|
+
if "group" in account:
|
|
125
|
+
log["GroupName"] = account["group"]
|
|
126
|
+
|
|
127
|
+
if "changed" in account:
|
|
128
|
+
(propertyName, previousPropertyValue, newPropertyName) = account["changed"]
|
|
129
|
+
log["UpdatedPropertyName"] = propertyName
|
|
130
|
+
log["PreviousPropertyValue"] = previousPropertyValue
|
|
131
|
+
log["NewPropertyValue"] = newPropertyName
|
|
132
|
+
|
|
133
|
+
print(json.dumps(log), flush=True)
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import json
|
|
3
|
-
import os
|
|
4
3
|
from enum import Enum
|
|
4
|
+
from hashlib import sha3_512
|
|
5
5
|
from typing import Literal
|
|
6
6
|
from typing import Optional
|
|
7
7
|
from typing import TypedDict
|
|
8
8
|
|
|
9
9
|
from django.http import HttpRequest
|
|
10
10
|
|
|
11
|
-
from
|
|
12
|
-
|
|
11
|
+
from .common import Activity
|
|
13
12
|
from .common import Client
|
|
14
13
|
from .common import Result
|
|
15
14
|
from .common import Server
|
|
16
15
|
from .common import Severity
|
|
17
|
-
from .common import _default_severity
|
|
18
|
-
from .common import _get_client_ip_address
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
class AuthenticationEvent(str, Enum):
|
|
@@ -30,7 +27,7 @@ class AuthenticationLoginMethod(str, Enum):
|
|
|
30
27
|
ExternalIDP = "External IdP"
|
|
31
28
|
|
|
32
29
|
|
|
33
|
-
class AuthenticationUser(TypedDict):
|
|
30
|
+
class AuthenticationUser(TypedDict, total=False):
|
|
34
31
|
"""Dictionary to represent properties of the users session."""
|
|
35
32
|
|
|
36
33
|
"""What type of role best describes this Authentication event."""
|
|
@@ -61,138 +58,133 @@ class AuthenticationUser(TypedDict):
|
|
|
61
58
|
sessionId: Optional[str]
|
|
62
59
|
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if result == Result.Success:
|
|
195
|
-
return "001c"
|
|
196
|
-
elif result == Result.Failure:
|
|
197
|
-
return "001d"
|
|
198
|
-
return "001"
|
|
61
|
+
class LogAuthentication(Activity):
|
|
62
|
+
Event = AuthenticationEvent
|
|
63
|
+
Result = Result
|
|
64
|
+
LoginMethod = AuthenticationLoginMethod
|
|
65
|
+
Severity = Severity
|
|
66
|
+
|
|
67
|
+
def __call__(
|
|
68
|
+
self,
|
|
69
|
+
request: HttpRequest,
|
|
70
|
+
event: AuthenticationEvent,
|
|
71
|
+
result: Result,
|
|
72
|
+
login_method: AuthenticationLoginMethod,
|
|
73
|
+
user: Optional[AuthenticationUser] = None,
|
|
74
|
+
server: Optional[Server] = None,
|
|
75
|
+
client: Optional[Client] = None,
|
|
76
|
+
severity: Optional[Severity] = None,
|
|
77
|
+
time_generated: Optional[datetime.datetime] = None,
|
|
78
|
+
result_details: Optional[str] = None,
|
|
79
|
+
message: Optional[str] = None,
|
|
80
|
+
):
|
|
81
|
+
"""
|
|
82
|
+
Log an ASIM Authentication Event to standard output.
|
|
83
|
+
|
|
84
|
+
:param request: django.http.HttpRequest object which initiated this Authentication request
|
|
85
|
+
from which the following data will be logged if available
|
|
86
|
+
- Django Authentication systems current username
|
|
87
|
+
- Django Session middlewares Session Key
|
|
88
|
+
- Client IP address
|
|
89
|
+
- URL requested by the client
|
|
90
|
+
- Server domain name
|
|
91
|
+
:param event: What authentication action was attempted, either "Logon" or "Logoff"
|
|
92
|
+
:param result: What outcome did the action have, either "Success", "Failure", "Partial", "NA"
|
|
93
|
+
:param login_method: What authentication mechanism was being used, one of:
|
|
94
|
+
- "Username & Password"
|
|
95
|
+
- "Staff-SSO"
|
|
96
|
+
- "UK.GOV-SSO"
|
|
97
|
+
- "External IdP"
|
|
98
|
+
:param user: Dictionary containing information on the subject of this Authentication event
|
|
99
|
+
see AuthenticationUser class for more details.
|
|
100
|
+
:param server: Dictionary containing information on the server servicing this Authentication event
|
|
101
|
+
see Server class for more details.
|
|
102
|
+
:param client: Dictionary containing information on the client performing this Authentication event
|
|
103
|
+
see Client class for more details.
|
|
104
|
+
:param severity: Optional severity of the event, defaults to "Informational", otherwise one of:
|
|
105
|
+
- "Informational"
|
|
106
|
+
- "Low"
|
|
107
|
+
- "Medium"
|
|
108
|
+
- "High"
|
|
109
|
+
:param time_generated: Optional datetime for when the event happened, otherwise datetime.now
|
|
110
|
+
:param result_details: Optional string describing any details associated with the events outcome.
|
|
111
|
+
This field is typically populated when the result is a failure.
|
|
112
|
+
:param message: Optional string describing the reason why the log was generated.
|
|
113
|
+
|
|
114
|
+
See also: https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-authentication
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
self._log_authentication(
|
|
118
|
+
request,
|
|
119
|
+
event,
|
|
120
|
+
result,
|
|
121
|
+
login_method,
|
|
122
|
+
user={} if user == None else user,
|
|
123
|
+
server={} if server == None else server,
|
|
124
|
+
client={} if client == None else client,
|
|
125
|
+
event_created=time_generated or datetime.datetime.now(tz=datetime.timezone.utc),
|
|
126
|
+
severity=severity,
|
|
127
|
+
result_details=result_details,
|
|
128
|
+
message=message,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def _log_authentication(
|
|
132
|
+
self,
|
|
133
|
+
request: HttpRequest,
|
|
134
|
+
event: AuthenticationEvent,
|
|
135
|
+
result: Result,
|
|
136
|
+
login_method: AuthenticationLoginMethod,
|
|
137
|
+
user: AuthenticationUser,
|
|
138
|
+
server: Server,
|
|
139
|
+
client: Client,
|
|
140
|
+
event_created: datetime.datetime,
|
|
141
|
+
severity: Optional[Severity] = None,
|
|
142
|
+
result_details: Optional[str] = None,
|
|
143
|
+
message: Optional[str] = None,
|
|
144
|
+
):
|
|
145
|
+
log = {
|
|
146
|
+
"EventOriginalType": self._event_code(event, result),
|
|
147
|
+
"EventType": event,
|
|
148
|
+
"LogonMethod": login_method,
|
|
149
|
+
"EventSchema": "Authentication",
|
|
150
|
+
"EventSchemaVersion": "0.1.4",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
log.update(
|
|
154
|
+
self._activity_fields(
|
|
155
|
+
request, event_created, result, server, client, severity, result_details, message
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if "role" in user:
|
|
160
|
+
log["TargetUserType"] = user["role"]
|
|
161
|
+
|
|
162
|
+
if "sessionId" in user:
|
|
163
|
+
log["TargetSessionId"] = self._cryptographically_hash(user["sessionId"])
|
|
164
|
+
elif hasattr(request, "session") and request.session.session_key:
|
|
165
|
+
log["TargetSessionId"] = self._cryptographically_hash(request.session.session_key)
|
|
166
|
+
|
|
167
|
+
if "username" in user:
|
|
168
|
+
log["TargetUsername"] = user["username"]
|
|
169
|
+
elif hasattr(request, "user") and request.user.username:
|
|
170
|
+
log["TargetUsername"] = request.user.username
|
|
171
|
+
|
|
172
|
+
print(json.dumps(log), flush=True)
|
|
173
|
+
|
|
174
|
+
def _cryptographically_hash(self, data: Optional[str]) -> Optional[str]:
|
|
175
|
+
if data is None:
|
|
176
|
+
return None
|
|
177
|
+
return sha3_512(data.encode("UTF-8")).hexdigest()
|
|
178
|
+
|
|
179
|
+
def _event_code(self, event: AuthenticationEvent, result: Result) -> str:
|
|
180
|
+
if event == AuthenticationEvent.Logon:
|
|
181
|
+
if result == Result.Success:
|
|
182
|
+
return "001a"
|
|
183
|
+
elif result == Result.Failure:
|
|
184
|
+
return "001b"
|
|
185
|
+
elif event == AuthenticationEvent.Logoff:
|
|
186
|
+
if result == Result.Success:
|
|
187
|
+
return "001c"
|
|
188
|
+
elif result == Result.Failure:
|
|
189
|
+
return "001d"
|
|
190
|
+
return "001"
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
1
3
|
from enum import Enum
|
|
2
4
|
from typing import Optional
|
|
3
5
|
from typing import TypedDict
|
|
4
6
|
|
|
5
7
|
from django.http import HttpRequest
|
|
6
8
|
|
|
9
|
+
from django_log_formatter_asim.ecs import _get_container_id
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LoggedInUser(TypedDict, total=False):
|
|
13
|
+
"""
|
|
14
|
+
A unique identifier for the user.
|
|
15
|
+
|
|
16
|
+
Defaults to the logged in Django User.username if not provided.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
username: Optional[str]
|
|
20
|
+
|
|
7
21
|
|
|
8
22
|
class Result(str, Enum):
|
|
9
23
|
Success = "Success"
|
|
@@ -19,7 +33,7 @@ class Severity(str, Enum):
|
|
|
19
33
|
High = "High"
|
|
20
34
|
|
|
21
35
|
|
|
22
|
-
class Client(TypedDict):
|
|
36
|
+
class Client(TypedDict, total=False):
|
|
23
37
|
"""Dictionary to represent properties of the HTTP Client."""
|
|
24
38
|
|
|
25
39
|
"""Internet Protocol Address of the client making the Authentication
|
|
@@ -29,7 +43,7 @@ class Client(TypedDict):
|
|
|
29
43
|
requested_url: Optional[str]
|
|
30
44
|
|
|
31
45
|
|
|
32
|
-
class Server(TypedDict):
|
|
46
|
+
class Server(TypedDict, total=False):
|
|
33
47
|
"""Dictionary to represent properties of the HTTP Server."""
|
|
34
48
|
|
|
35
49
|
"""
|
|
@@ -51,13 +65,67 @@ class Server(TypedDict):
|
|
|
51
65
|
service_name: Optional[str]
|
|
52
66
|
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
class Activity:
|
|
69
|
+
def _activity_fields(
|
|
70
|
+
self,
|
|
71
|
+
request: HttpRequest,
|
|
72
|
+
event_created: datetime.datetime,
|
|
73
|
+
result: Result,
|
|
74
|
+
server: Server,
|
|
75
|
+
client: Client,
|
|
76
|
+
severity: Optional[Severity],
|
|
77
|
+
result_details: Optional[str],
|
|
78
|
+
message: Optional[str],
|
|
79
|
+
):
|
|
80
|
+
log = {
|
|
81
|
+
"EventStartTime": event_created.isoformat(),
|
|
82
|
+
"EventSeverity": severity or self._default_severity(result),
|
|
83
|
+
"EventResult": result,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if "domain_name" in server:
|
|
87
|
+
log["HttpHost"] = server["domain_name"]
|
|
88
|
+
elif "HTTP_HOST" in request.META:
|
|
89
|
+
log["HttpHost"] = request.get_host()
|
|
90
|
+
|
|
91
|
+
if "service_name" in server:
|
|
92
|
+
log["TargetAppName"] = server["service_name"]
|
|
93
|
+
elif os.environ.get("COPILOT_APPLICATION_NAME") and os.environ.get("COPILOT_SERVICE_NAME"):
|
|
94
|
+
app_name = (
|
|
95
|
+
f"{os.environ['COPILOT_APPLICATION_NAME']}-{os.environ['COPILOT_SERVICE_NAME']}"
|
|
96
|
+
)
|
|
97
|
+
log["TargetAppName"] = app_name
|
|
98
|
+
|
|
99
|
+
if container_id := _get_container_id():
|
|
100
|
+
log["TargetContainerId"] = container_id
|
|
101
|
+
|
|
102
|
+
if "ip_address" in client:
|
|
103
|
+
log["SrcIpAddr"] = client["ip_address"]
|
|
104
|
+
elif client_ip := self._get_client_ip_address(request):
|
|
105
|
+
log["SrcIpAddr"] = client_ip
|
|
106
|
+
|
|
107
|
+
if "requested_url" in client:
|
|
108
|
+
log["TargetUrl"] = client["requested_url"]
|
|
109
|
+
elif "HTTP_HOST" in request.META:
|
|
110
|
+
log["TargetUrl"] = request.scheme + "://" + request.get_host() + request.get_full_path()
|
|
111
|
+
|
|
112
|
+
if result_details:
|
|
113
|
+
log["EventResultDetails"] = result_details
|
|
114
|
+
|
|
115
|
+
if message:
|
|
116
|
+
log["EventMessage"] = message
|
|
117
|
+
|
|
118
|
+
if "ip_address" in server:
|
|
119
|
+
log["DvcIpAddr"] = server["ip_address"]
|
|
120
|
+
|
|
121
|
+
return log
|
|
122
|
+
|
|
123
|
+
def _default_severity(sef, result: Result) -> Severity:
|
|
124
|
+
return Severity.Informational if result == Result.Success else Severity.Medium
|
|
125
|
+
|
|
126
|
+
def _get_client_ip_address(self, request: HttpRequest) -> Optional[str]:
|
|
127
|
+
# Import here as ipware uses settings
|
|
128
|
+
from ipware import get_client_ip
|
|
129
|
+
|
|
130
|
+
client_ip, _ = get_client_ip(request)
|
|
131
|
+
return client_ip
|