django-log-formatter-asim 1.1.0a0__tar.gz → 1.1.0a2__tar.gz
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-1.1.0a0 → django_log_formatter_asim-1.1.0a2}/PKG-INFO +2 -1
- {django_log_formatter_asim-1.1.0a0 → django_log_formatter_asim-1.1.0a2}/django_log_formatter_asim/__init__.py +3 -10
- django_log_formatter_asim-1.1.0a2/django_log_formatter_asim/events/__init__.py +2 -0
- django_log_formatter_asim-1.1.0a0/django_log_formatter_asim/events.py → django_log_formatter_asim-1.1.0a2/django_log_formatter_asim/events/authentication.py +56 -74
- django_log_formatter_asim-1.1.0a2/django_log_formatter_asim/events/common.py +54 -0
- django_log_formatter_asim-1.1.0a2/django_log_formatter_asim/events/file_activity.py +204 -0
- {django_log_formatter_asim-1.1.0a0 → django_log_formatter_asim-1.1.0a2}/pyproject.toml +3 -2
- {django_log_formatter_asim-1.1.0a0 → django_log_formatter_asim-1.1.0a2}/LICENSE +0 -0
- {django_log_formatter_asim-1.1.0a0 → django_log_formatter_asim-1.1.0a2}/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: django-log-formatter-asim
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.0a2
|
|
4
4
|
Summary: Formats Django logs in ASIM format.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Department for Business and Trade Platform Team
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Requires-Dist: ddtrace (>=3.2.1,<4.0.0)
|
|
17
17
|
Requires-Dist: django (>=3,<5) ; python_version == "3.9"
|
|
18
18
|
Requires-Dist: django (>=3,<6) ; python_version >= "3.10" and python_version < "4"
|
|
19
|
+
Requires-Dist: django-ipware (>=7.0.1,<8.0.0)
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
21
22
|
# Django ASIM log formatter
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
3
4
|
from datetime import datetime
|
|
5
|
+
from datetime import timezone
|
|
4
6
|
from importlib.metadata import distribution
|
|
5
|
-
import os
|
|
6
7
|
|
|
7
8
|
import ddtrace
|
|
8
9
|
from ddtrace.trace import tracer
|
|
@@ -70,7 +71,7 @@ class ASIMRootFormatter:
|
|
|
70
71
|
|
|
71
72
|
def get_log_dict(self):
|
|
72
73
|
record = self.record
|
|
73
|
-
log_time = datetime.
|
|
74
|
+
log_time = datetime.fromtimestamp(record.created, timezone.utc).isoformat()
|
|
74
75
|
log_dict = {
|
|
75
76
|
# Event fields...
|
|
76
77
|
"EventMessage": record.getMessage(),
|
|
@@ -200,14 +201,6 @@ class ASIMRequestFormatter(ASIMRootFormatter):
|
|
|
200
201
|
http_user_agent = request.META.get("HTTP_USER_AGENT", None)
|
|
201
202
|
return http_user_agent
|
|
202
203
|
|
|
203
|
-
def _get_ip_address(self, request):
|
|
204
|
-
# Import here as ipware uses settings
|
|
205
|
-
from ipware import get_client_ip
|
|
206
|
-
|
|
207
|
-
client_ip, is_routable = get_client_ip(request)
|
|
208
|
-
return client_ip or "Unknown"
|
|
209
|
-
|
|
210
|
-
|
|
211
204
|
ASIM_FORMATTERS = {
|
|
212
205
|
"root": ASIMRootFormatter,
|
|
213
206
|
"django.request": ASIMRequestFormatter,
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import json
|
|
3
|
-
import sys
|
|
4
3
|
from enum import Enum
|
|
5
4
|
from typing import Literal
|
|
6
5
|
from typing import Optional
|
|
7
6
|
from typing import TypedDict
|
|
8
7
|
|
|
8
|
+
from django.http import HttpRequest
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
from .common import Client
|
|
11
|
+
from .common import Result
|
|
12
|
+
from .common import Server
|
|
13
|
+
from .common import Severity
|
|
14
|
+
from .common import _default_severity
|
|
15
|
+
from .common import _get_client_ip_address
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
class
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Failure = "Failure"
|
|
19
|
-
NA = "NA"
|
|
18
|
+
class AuthenticationEvent(str, Enum):
|
|
19
|
+
Logon = "Logon"
|
|
20
|
+
Logoff = "Logoff"
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class AuthenticationLoginMethod(str, Enum):
|
|
@@ -26,34 +27,6 @@ class AuthenticationLoginMethod(str, Enum):
|
|
|
26
27
|
ExternalIDP = "External IdP"
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
class Severity(str, Enum):
|
|
30
|
-
Informational = "Informational"
|
|
31
|
-
Low = "Low"
|
|
32
|
-
Medium = "Medium"
|
|
33
|
-
High = "High"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class AuthenticationServer(TypedDict):
|
|
37
|
-
"""Dictionary to represent properties of the HTTP Server."""
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
A unique identifier for the server which serviced the Authentication event.
|
|
41
|
-
|
|
42
|
-
Defaults to the WSGI SERVER_NAME field if not provided.
|
|
43
|
-
"""
|
|
44
|
-
hostname: Optional[str]
|
|
45
|
-
"""Internet Protocol Address of the server serving this request."""
|
|
46
|
-
ipAddr: Optional[str]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class AuthenticationClient(TypedDict):
|
|
50
|
-
"""Dictionary to represent properties of the HTTP Client."""
|
|
51
|
-
|
|
52
|
-
"""Internet Protocol Address of the client making the Authentication
|
|
53
|
-
event."""
|
|
54
|
-
ipAddr: Optional[str]
|
|
55
|
-
|
|
56
|
-
|
|
57
30
|
class AuthenticationUser(TypedDict):
|
|
58
31
|
"""Dictionary to represent properties of the users session."""
|
|
59
32
|
|
|
@@ -86,13 +59,13 @@ class AuthenticationUser(TypedDict):
|
|
|
86
59
|
|
|
87
60
|
|
|
88
61
|
def log_authentication(
|
|
89
|
-
request,
|
|
90
|
-
|
|
91
|
-
result:
|
|
62
|
+
request: HttpRequest,
|
|
63
|
+
event: AuthenticationEvent,
|
|
64
|
+
result: Result,
|
|
92
65
|
login_method: AuthenticationLoginMethod,
|
|
93
66
|
user: Optional[AuthenticationUser] = None,
|
|
94
|
-
server: Optional[
|
|
95
|
-
client: Optional[
|
|
67
|
+
server: Optional[Server] = None,
|
|
68
|
+
client: Optional[Client] = None,
|
|
96
69
|
severity: Optional[Severity] = None,
|
|
97
70
|
time_generated: Optional[datetime.datetime] = None,
|
|
98
71
|
result_details: Optional[str] = None,
|
|
@@ -106,8 +79,9 @@ def log_authentication(
|
|
|
106
79
|
- Django Authentication systems current username
|
|
107
80
|
- Django Session middlewares Session Key
|
|
108
81
|
- Client IP address
|
|
82
|
+
- URL requested by the client
|
|
109
83
|
- Server hostname
|
|
110
|
-
:param
|
|
84
|
+
:param event: What authentication action was attempted, either "Logon" or "Logoff"
|
|
111
85
|
:param result: What outcome did the action have, either "Success", "Failure", "Partial", "NA"
|
|
112
86
|
:param login_method: What authentication mechanism was being used, one of:
|
|
113
87
|
- "Username & Password"
|
|
@@ -117,9 +91,9 @@ def log_authentication(
|
|
|
117
91
|
:param user: Dictionary containing information on the subject of this Authentication event
|
|
118
92
|
see AuthenticationUser class for more details.
|
|
119
93
|
:param server: Dictionary containing information on the server servicing this Authentication event
|
|
120
|
-
see
|
|
94
|
+
see Server class for more details.
|
|
121
95
|
:param client: Dictionary containing information on the client performing this Authentication event
|
|
122
|
-
see
|
|
96
|
+
see Client class for more details.
|
|
123
97
|
:param severity: Optional severity of the event, defaults to "Informational", otherwise one of:
|
|
124
98
|
- "Informational"
|
|
125
99
|
- "Low"
|
|
@@ -141,64 +115,72 @@ def log_authentication(
|
|
|
141
115
|
|
|
142
116
|
event_created = time_generated or datetime.datetime.now(tz=datetime.timezone.utc)
|
|
143
117
|
|
|
144
|
-
|
|
118
|
+
log = {
|
|
145
119
|
"EventCreated": event_created.isoformat(), # TODO: Should this really be EventCreated, or TimeGenerated
|
|
146
|
-
"DvcHostname": server.get("hostname", request.environ.get("SERVER_NAME")),
|
|
147
120
|
"EventSeverity": severity or _default_severity(result),
|
|
148
|
-
"EventOriginalType": _event_code(
|
|
149
|
-
"
|
|
150
|
-
"EventType": type,
|
|
121
|
+
"EventOriginalType": _event_code(event, result),
|
|
122
|
+
"EventType": event,
|
|
151
123
|
"EventResult": result,
|
|
152
124
|
"LogonMethod": login_method,
|
|
153
125
|
"EventSchema": "Authentication",
|
|
154
126
|
"EventSchemaVersion": "0.1.4",
|
|
155
127
|
}
|
|
156
128
|
|
|
129
|
+
if "hostname" in server:
|
|
130
|
+
log["DvcHostname"] = server["hostname"]
|
|
131
|
+
elif "SERVER_NAME" in request.META:
|
|
132
|
+
log["DvcHostname"] = request.META["SERVER_NAME"]
|
|
133
|
+
|
|
134
|
+
if "ip_address" in client:
|
|
135
|
+
log["SrcIpAddr"] = client["ip_address"]
|
|
136
|
+
elif client_ip := _get_client_ip_address(request):
|
|
137
|
+
log["SrcIpAddr"] = client_ip
|
|
138
|
+
|
|
139
|
+
if "requested_url" in client:
|
|
140
|
+
log["TargetUrl"] = client["requested_url"]
|
|
141
|
+
elif "SERVER_NAME" in request.META:
|
|
142
|
+
log["TargetUrl"] = request.scheme + "://" + request.get_host() + request.get_full_path()
|
|
143
|
+
|
|
157
144
|
if "role" in user:
|
|
158
|
-
|
|
145
|
+
log["ActorUserType"] = user["role"]
|
|
159
146
|
|
|
160
147
|
if "sessionId" in user:
|
|
161
|
-
|
|
148
|
+
log["ActorSessionId"] = user["sessionId"]
|
|
162
149
|
elif request.session.session_key:
|
|
163
|
-
|
|
150
|
+
log["ActorSessionId"] = request.session.session_key
|
|
164
151
|
|
|
165
152
|
if "username" in user:
|
|
166
|
-
|
|
153
|
+
log["ActorUsername"] = user["username"]
|
|
167
154
|
elif request.user.username:
|
|
168
|
-
|
|
155
|
+
log["ActorUsername"] = request.user.username
|
|
169
156
|
|
|
170
157
|
if result_details:
|
|
171
|
-
|
|
158
|
+
log["EventResultDetails"] = result_details
|
|
172
159
|
|
|
173
160
|
if message:
|
|
174
|
-
|
|
161
|
+
log["EventMessage"] = message
|
|
175
162
|
|
|
176
|
-
if "
|
|
177
|
-
|
|
163
|
+
if "ip_address" in server:
|
|
164
|
+
log["DvcIpAddr"] = server["ip_address"]
|
|
178
165
|
|
|
179
|
-
|
|
180
|
-
sys.stdout.flush()
|
|
166
|
+
print(json.dumps(log), flush=True)
|
|
181
167
|
|
|
182
168
|
|
|
183
|
-
log_authentication.
|
|
184
|
-
log_authentication.Result =
|
|
169
|
+
log_authentication.Event = AuthenticationEvent
|
|
170
|
+
log_authentication.Result = Result
|
|
185
171
|
log_authentication.LoginMethod = AuthenticationLoginMethod
|
|
186
172
|
log_authentication.Severity = Severity
|
|
187
173
|
|
|
188
174
|
|
|
189
|
-
def
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def _event_code(type, result):
|
|
194
|
-
if type == AuthenticationType.Logon:
|
|
195
|
-
if result == log_authentication.Result.Success:
|
|
175
|
+
def _event_code(event: AuthenticationEvent, result: Result) -> str:
|
|
176
|
+
if event == AuthenticationEvent.Logon:
|
|
177
|
+
if result == Result.Success:
|
|
196
178
|
return "001a"
|
|
197
|
-
elif result ==
|
|
179
|
+
elif result == Result.Failure:
|
|
198
180
|
return "001b"
|
|
199
|
-
elif
|
|
200
|
-
if result ==
|
|
181
|
+
elif event == AuthenticationEvent.Logoff:
|
|
182
|
+
if result == Result.Success:
|
|
201
183
|
return "001c"
|
|
202
|
-
elif result ==
|
|
184
|
+
elif result == Result.Failure:
|
|
203
185
|
return "001d"
|
|
204
186
|
return "001"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
from django.http import HttpRequest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Result(str, Enum):
|
|
9
|
+
Success = "Success"
|
|
10
|
+
Partial = "Partial"
|
|
11
|
+
Failure = "Failure"
|
|
12
|
+
NA = "NA"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Severity(str, Enum):
|
|
16
|
+
Informational = "Informational"
|
|
17
|
+
Low = "Low"
|
|
18
|
+
Medium = "Medium"
|
|
19
|
+
High = "High"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Client(TypedDict):
|
|
23
|
+
"""Dictionary to represent properties of the HTTP Client."""
|
|
24
|
+
|
|
25
|
+
"""Internet Protocol Address of the client making the Authentication
|
|
26
|
+
event."""
|
|
27
|
+
ip_address: Optional[str]
|
|
28
|
+
"""URL requested by the client."""
|
|
29
|
+
requested_url: Optional[str]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Server(TypedDict):
|
|
33
|
+
"""Dictionary to represent properties of the HTTP Server."""
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
A unique identifier for the server which serviced the Authentication event.
|
|
37
|
+
|
|
38
|
+
Defaults to the WSGI SERVER_NAME field if not provided.
|
|
39
|
+
"""
|
|
40
|
+
hostname: Optional[str]
|
|
41
|
+
"""Internet Protocol Address of the server serving this request."""
|
|
42
|
+
ip_address: Optional[str]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _default_severity(result: Result) -> Severity:
|
|
46
|
+
return Severity.Informational if result == Result.Success else Severity.Medium
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _get_client_ip_address(request: HttpRequest) -> Optional[str]:
|
|
50
|
+
# Import here as ipware uses settings
|
|
51
|
+
from ipware import get_client_ip
|
|
52
|
+
|
|
53
|
+
client_ip, _ = get_client_ip(request)
|
|
54
|
+
return client_ip
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from typing import TypedDict
|
|
7
|
+
|
|
8
|
+
from django.http import HttpRequest
|
|
9
|
+
|
|
10
|
+
from .common import Client
|
|
11
|
+
from .common import Result
|
|
12
|
+
from .common import Server
|
|
13
|
+
from .common import Severity
|
|
14
|
+
from .common import _default_severity
|
|
15
|
+
from .common import _get_client_ip_address
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FileActivityEvent(str, Enum):
|
|
19
|
+
FileAccessed = "FileAccessed"
|
|
20
|
+
FileCreated = "FileCreated"
|
|
21
|
+
FileModified = "FileModified"
|
|
22
|
+
FileDeleted = "FileDeleted"
|
|
23
|
+
FileRenamed = "FileRenamed"
|
|
24
|
+
FileCopied = "FileCopied"
|
|
25
|
+
FileMoved = "FileMoved"
|
|
26
|
+
FolderCreated = "FolderCreated"
|
|
27
|
+
FolderDeleted = "FolderDeleted"
|
|
28
|
+
FolderMoved = "FolderMoved"
|
|
29
|
+
FolderModified = "FolderModified"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FileActivityFile(TypedDict):
|
|
33
|
+
"""Dictionary to represent properties of the target file."""
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
The full, normalized path of the target file, including the folder or location,
|
|
37
|
+
the file name, and the extension.
|
|
38
|
+
"""
|
|
39
|
+
path: str
|
|
40
|
+
"""
|
|
41
|
+
The name of the target file, without a path or a location, but with an
|
|
42
|
+
extension if available. This field should be similar to the final element in
|
|
43
|
+
the TargetFilePath field.
|
|
44
|
+
|
|
45
|
+
Defaults to extracting the name based off the path if not provided.
|
|
46
|
+
"""
|
|
47
|
+
name: Optional[str]
|
|
48
|
+
"""
|
|
49
|
+
The target file extension.
|
|
50
|
+
|
|
51
|
+
Defaults to extracting the extension based off the path if not provided.
|
|
52
|
+
"""
|
|
53
|
+
extension: Optional[str]
|
|
54
|
+
"""
|
|
55
|
+
The Mime, or Media, type of the target file.
|
|
56
|
+
|
|
57
|
+
Allowed values are listed in the IANA Media Types repository.
|
|
58
|
+
"""
|
|
59
|
+
content_type: Optional[str]
|
|
60
|
+
"""The SHA256 value of the target file."""
|
|
61
|
+
sha256: Optional[str]
|
|
62
|
+
"""The size of the target file in bytes."""
|
|
63
|
+
size: Optional[int]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FileActivityUser(TypedDict):
|
|
67
|
+
"""
|
|
68
|
+
A unique identifier for the user.
|
|
69
|
+
|
|
70
|
+
Defaults to the logged in Django User.username if not provided.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
username: Optional[str]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def log_file_activity(
|
|
77
|
+
request: HttpRequest,
|
|
78
|
+
event: FileActivityEvent,
|
|
79
|
+
result: Result,
|
|
80
|
+
file: FileActivityFile,
|
|
81
|
+
user: Optional[FileActivityUser] = None,
|
|
82
|
+
server: Optional[Server] = None,
|
|
83
|
+
client: Optional[Client] = None,
|
|
84
|
+
severity: Optional[Severity] = None,
|
|
85
|
+
time_generated: Optional[datetime.datetime] = None,
|
|
86
|
+
result_details: Optional[str] = None,
|
|
87
|
+
message: Optional[str] = None,
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
Log an ASIM File Event to standard output.
|
|
91
|
+
|
|
92
|
+
:param request: django.http.HttpRequest object which initiated this Authentication request
|
|
93
|
+
from which the following data will be logged if available
|
|
94
|
+
- Django Authentication systems current username
|
|
95
|
+
- Client IP address
|
|
96
|
+
- URL requested by the client
|
|
97
|
+
- Server hostname
|
|
98
|
+
:param event: What File Event action was attempted, one of:
|
|
99
|
+
- FileAccessed
|
|
100
|
+
- FileCreated
|
|
101
|
+
- FileModified
|
|
102
|
+
- FileDeleted
|
|
103
|
+
- FileRenamed
|
|
104
|
+
- FileCopied
|
|
105
|
+
- FileMoved
|
|
106
|
+
- FolderCreated
|
|
107
|
+
- FolderDeleted
|
|
108
|
+
- FolderMoved
|
|
109
|
+
- FolderModified
|
|
110
|
+
:param result: What outcome did the action have, either "Success", "Failure", "Partial", "NA"
|
|
111
|
+
:param file: Dictionary containing information on the target of this File event see
|
|
112
|
+
FileActivityFile for more details.
|
|
113
|
+
:param user: Dictionary containing information on the logged in users username.
|
|
114
|
+
:param server: Dictionary containing information on the server servicing this File event
|
|
115
|
+
see Server class for more details.
|
|
116
|
+
:param client: Dictionary containing information on the client performing this File event
|
|
117
|
+
see Client class for more details.
|
|
118
|
+
:param severity: Optional severity of the event, defaults to "Informational", otherwise one of:
|
|
119
|
+
- "Informational"
|
|
120
|
+
- "Low"
|
|
121
|
+
- "Medium"
|
|
122
|
+
- "High"
|
|
123
|
+
:param time_generated: Optional datetime for when the event happened, otherwise datetime.now
|
|
124
|
+
:param result_details: Optional string describing any details associated with the events outcome.
|
|
125
|
+
This field is typically populated when the result is a failure.
|
|
126
|
+
:param message: Optional string describing the reason why the log was generated.
|
|
127
|
+
|
|
128
|
+
See also: https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-file-event
|
|
129
|
+
"""
|
|
130
|
+
if user == None:
|
|
131
|
+
user = {}
|
|
132
|
+
if server == None:
|
|
133
|
+
server = {}
|
|
134
|
+
if client == None:
|
|
135
|
+
client = {}
|
|
136
|
+
|
|
137
|
+
event_created = time_generated or datetime.datetime.now(tz=datetime.timezone.utc)
|
|
138
|
+
|
|
139
|
+
log = {
|
|
140
|
+
"EventSchema": "FileEvent",
|
|
141
|
+
"EventSchemaVersion": "0.2.1",
|
|
142
|
+
"EventType": event,
|
|
143
|
+
"EventResult": result,
|
|
144
|
+
"EventCreated": event_created.isoformat(), # TODO: Should this really be EventCreated, or TimeGenerated
|
|
145
|
+
"EventSeverity": severity or _default_severity(result),
|
|
146
|
+
"TargetFilePath": file["path"],
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if "name" in file:
|
|
150
|
+
log["TargetFileName"] = file["name"]
|
|
151
|
+
else:
|
|
152
|
+
log["TargetFileName"] = os.path.basename(file["path"])
|
|
153
|
+
|
|
154
|
+
if "extension" in file:
|
|
155
|
+
log["TargetFileExtension"] = file["extension"]
|
|
156
|
+
else:
|
|
157
|
+
file_name_parts = list(filter(None, log["TargetFileName"].split(".", 1)))
|
|
158
|
+
if len(file_name_parts) > 1:
|
|
159
|
+
log["TargetFileExtension"] = file_name_parts[1]
|
|
160
|
+
|
|
161
|
+
if "content_type" in file:
|
|
162
|
+
log["TargetFileMimeType"] = file["content_type"]
|
|
163
|
+
|
|
164
|
+
if "sha256" in file:
|
|
165
|
+
log["TargetFileSHA256"] = file["sha256"]
|
|
166
|
+
|
|
167
|
+
if "size" in file:
|
|
168
|
+
log["TargetFileSize"] = file["size"]
|
|
169
|
+
|
|
170
|
+
if "hostname" in server:
|
|
171
|
+
log["DvcHostname"] = server["hostname"]
|
|
172
|
+
elif "SERVER_NAME" in request.META:
|
|
173
|
+
log["DvcHostname"] = request.META["SERVER_NAME"]
|
|
174
|
+
|
|
175
|
+
if "ip_address" in client:
|
|
176
|
+
log["SrcIpAddr"] = client["ip_address"]
|
|
177
|
+
elif client_ip := _get_client_ip_address(request):
|
|
178
|
+
log["SrcIpAddr"] = client_ip
|
|
179
|
+
|
|
180
|
+
if "requested_url" in client:
|
|
181
|
+
log["TargetUrl"] = client["requested_url"]
|
|
182
|
+
elif "SERVER_NAME" in request.META:
|
|
183
|
+
log["TargetUrl"] = request.scheme + "://" + request.get_host() + request.get_full_path()
|
|
184
|
+
|
|
185
|
+
if "username" in user:
|
|
186
|
+
log["ActorUsername"] = user["username"]
|
|
187
|
+
elif request.user.username:
|
|
188
|
+
log["ActorUsername"] = request.user.username
|
|
189
|
+
|
|
190
|
+
if result_details:
|
|
191
|
+
log["EventResultDetails"] = result_details
|
|
192
|
+
|
|
193
|
+
if message:
|
|
194
|
+
log["EventMessage"] = message
|
|
195
|
+
|
|
196
|
+
if "ip_address" in server:
|
|
197
|
+
log["DvcIpAddr"] = server["ip_address"]
|
|
198
|
+
|
|
199
|
+
print(json.dumps(log), flush=True)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
log_file_activity.Event = FileActivityEvent
|
|
203
|
+
log_file_activity.Result = Result
|
|
204
|
+
log_file_activity.Severity = Severity
|
|
@@ -3,7 +3,7 @@ line-length = 100
|
|
|
3
3
|
|
|
4
4
|
[tool.poetry]
|
|
5
5
|
name = "django-log-formatter-asim"
|
|
6
|
-
version = "1.1.
|
|
6
|
+
version = "1.1.0a2"
|
|
7
7
|
description = "Formats Django logs in ASIM format."
|
|
8
8
|
authors = ["Department for Business and Trade Platform Team <sre-team@digital.trade.gov.uk>"]
|
|
9
9
|
license = "MIT"
|
|
@@ -19,12 +19,13 @@ django = [
|
|
|
19
19
|
{ version = ">=3,<6", python = ">=3.10,<4" },
|
|
20
20
|
]
|
|
21
21
|
ddtrace = "^3.2.1"
|
|
22
|
+
django-ipware = "^7.0.1"
|
|
22
23
|
|
|
23
24
|
[tool.poetry.group.dev.dependencies]
|
|
24
25
|
pre-commit = "^3.5.0"
|
|
25
26
|
pytest = "^7.4.3"
|
|
26
27
|
tox = "^4.11.3"
|
|
27
|
-
freezegun = "^1.
|
|
28
|
+
freezegun = "^1.5.2"
|
|
28
29
|
pytest-django = "^4.7.0"
|
|
29
30
|
importlib-metadata = "^6.8.0"
|
|
30
31
|
|
|
File without changes
|
|
File without changes
|