django-log-formatter-asim 1.2.0a0__tar.gz → 1.2.0a1__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.
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: django-log-formatter-asim
3
- Version: 1.2.0a0
3
+ Version: 1.2.0a1
4
4
  Summary: Formats Django logs in ASIM format.
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Author: Department for Business and Trade Platform Team
7
8
  Author-email: sre-team@digital.trade.gov.uk
8
9
  Requires-Python: >=3.9,<4
@@ -13,7 +14,8 @@ Classifier: Programming Language :: Python :: 3.10
13
14
  Classifier: Programming Language :: Python :: 3.11
14
15
  Classifier: Programming Language :: Python :: 3.12
15
16
  Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: ddtrace (>=3.2.1,<4.0.0)
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Dist: ddtrace (>=3.2.1,<5)
17
19
  Requires-Dist: django (>=3,<5) ; python_version == "3.9"
18
20
  Requires-Dist: django (>=3,<6) ; python_version >= "3.10" and python_version < "4"
19
21
  Requires-Dist: django-ipware (>=7.0.1,<8.0.0)
@@ -77,6 +79,101 @@ LOGGING = {
77
79
  In this example we assign the ASIM formatter to a `handler` and ensure both `root` and `django` loggers use this `handler`.
78
80
  We then set `propagate` to `False` on the `django` logger, to avoid duplicating logs at the root level.
79
81
 
82
+ ### Settings
83
+
84
+ `DLFA_LOG_PERSONALLY_IDENTIFIABLE_INFORMATION` - the formatter checks this setting to see if personally identifiable information should be logged. If this is not set to true, only the user's id is logged.
85
+
86
+ `DLFA_TRACE_HEADERS` - used for defining custom zipkin headers, the defaults is `("X-Amzn-Trace-Id")`, but for applications hosted in GOV.UK PaaS you should use `("X-B3-TraceId", "X-B3-SpanId")`. If you are running your application in both places side by side during migration, the following should work in your Django settings:
87
+
88
+ ```python
89
+ from dbt_copilot_python.utility import is_copilot
90
+
91
+ if is_copilot():
92
+ DLFA_TRACE_HEADERS = ("X-B3-TraceId", "X-B3-SpanId")
93
+ ```
94
+
95
+ `DLFA_INCLUDE_RAW_LOG` - By default the original unformatted log is not included in the ASIM formatted log. You can enable that by setting this to `True` and it will be included in `AddidtionalFields.RawLog`.
96
+
97
+ > [!WARNING]
98
+ > Setting `DLFA_INCLUDE_RAW_LOG` to `True` will cause additional private fields to be output to your logs.
99
+ > This could include secrets, such as AWS Access Keys, private HTTP Request data, or personally identifiable information.
100
+ > This setting is not recommended for a production environment.
101
+
102
+ ### Serialisation behaviour
103
+
104
+ The package provides one `logging.Formatter` class, `ASIMFormatter` which routes log messages to a serialiser
105
+ which generates a python dict which the formatter converts to a JSON string and prints to standard output.
106
+
107
+ It has a generic serialiser called `ASIMRootFormatter` and a custom serlializer for log messages where the
108
+ logger is `django.request`.
109
+
110
+ ``` python
111
+ ASIM_FORMATTERS = {
112
+ "root": ASIMRootFormatter,
113
+ "django.request": ASIMRequestFormatter,
114
+ }
115
+ ```
116
+
117
+ #### ASIMRootFormatter
118
+
119
+ This serialiser outputs the following ASIM fields.
120
+
121
+ - `EventSchema` = `ProcessEvent`
122
+ - `ActingAppType` = `Django`
123
+ - `AdditionalFields[DjangoLogFormatterAsimVersion]`
124
+ - `EventSchemaVersion`
125
+ - `EventMessage`
126
+ - `EventCount`
127
+ - `EventStartTime`
128
+ - `EventEndTime`
129
+ - `EventType`
130
+ - `EventResult`
131
+ - `EventSeverity`
132
+ - `EventOriginalSeverity`
133
+
134
+ Additionally, the following DataDog fields where available:
135
+
136
+ - `dd.trace_id`
137
+ - `dd.span_id`
138
+ - `env`
139
+ - `service`
140
+ - `version`
141
+
142
+
143
+ #### ASIMRequestFormatter
144
+
145
+ This serialiser outputs the following ASIM fields in addition to the ones from ASIMRootFormatter.
146
+ It is coupled to the datastructure provided by the `django.request` logger.
147
+ The `django.request` logger only outputs requests where the response code is 4xx/5xx.
148
+
149
+ - `SrcIpAddr` and `IpAddr`
150
+ - `SrcPortNumber`
151
+ - `SrcUserId` and `SrcUsername`
152
+ - `HttpUserAgent`
153
+ - `AdditionalFields["TraceHeaders"][trace_header_name]` - See `DLFA_TRACE_HEADERS` setting for more information.
154
+
155
+ #### Creating a custom serialiser
156
+
157
+ If you wish to create your own ASIM serialiser, you can inherit from `ASIMRootFormatter` and call
158
+ `super().get_log_dict()` to get the base level logging data for augmentation:
159
+
160
+ ``` python
161
+ class MyASIMFormatter(ASIMRootFormatter):
162
+ def get_log_dict(self):
163
+ log_dict = super().get_log_dict()
164
+
165
+ # Customise logger event
166
+
167
+ return log_dict
168
+ ```
169
+
170
+ This serialiser can then be added to `ASIM_FORMATTERS`...
171
+
172
+ ```python
173
+ ASIM_FORMATTERS["my_logger"] = MyASIMFormatter
174
+ ```
175
+
176
+
80
177
  ### ASIM Events
81
178
 
82
179
  The events mostly follow the Microsoft schema but have been tailored to Department of Business and Trade needs.
@@ -127,48 +224,114 @@ log_authentication(
127
224
  }
128
225
  ```
129
226
 
130
- ### Settings
227
+ #### File Activity event
131
228
 
132
- `DLFA_LOG_PERSONALLY_IDENTIFIABLE_INFORMATION` - the formatter checks this setting to see if personally identifiable information should be logged. If this is not set to true, only the user's id is logged.
229
+ Following the [ASIM File Event Schema](https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-file-event).
133
230
 
134
- `DLFA_TRACE_HEADERS` - used for defining custom zipkin headers, the defaults is `("X-Amzn-Trace-Id")`, but for applications hosted in GOV.UK PaaS you should use `("X-B3-TraceId", "X-B3-SpanId")`. If you are running your application in both places side by side during migration, the following should work in your Django settings:
231
+ ```python
232
+ # Example usage
233
+ from django_log_formatter_asim.events import log_file_activity
135
234
 
136
- `DLFA_INCLUDE_RAW_LOG` - By default the original unformatted log is not included in the ASIM formatted log. You can enable that by setting this to `True` and it will be included in `AddidtionalFields.RawLog`.
235
+ log_file_activity(
236
+ request,
237
+ event=log_file_activity.Event.FileCopied,
238
+ result=log_file_activity.Result.Success,
239
+ file={
240
+ "path": "/tmp/copied.txt",
241
+ "content_type": "text/plain",
242
+ "extension": "txt",
243
+ "name": "copied.txt",
244
+ "sha256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
245
+ "size": 14,
246
+ },
247
+ # source_file is only necessary if the event is one of FileRenamed, FileMoved, FileCopied, FolderMoved
248
+ source_file={
249
+ "path": "/tmp/original.txt",
250
+ "content_type": "text/plain",
251
+ "extension": "txt",
252
+ "name": "original.txt",
253
+ "sha256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
254
+ "size": 14,
255
+ },
256
+ )
137
257
 
138
- ```python
139
- from dbt_copilot_python.utility import is_copilot
258
+ # Example JSON printed to standard output
259
+ {
260
+ # Values provided as arguments
261
+ "EventType": "FileCopied",
262
+ "EventResult": "Success",
140
263
 
141
- if is_copilot():
142
- DLFA_TRACE_HEADERS = ("X-B3-TraceId", "X-B3-SpanId")
143
- ```
264
+ "TargetFilePath": "/tmp/copied.txt",
265
+ "TargetFileName": "copied.txt",
266
+ "TargetFileExtension": "txt",
267
+ "TargetFileMimeType": "text/plain",
268
+ "TargetFileSHA256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
269
+ "TargetFileSize": 14,
144
270
 
145
- ### Formatter classes
271
+ "SrcFilePath": "/tmp/original.txt",
272
+ "SrcFileName": "original.txt",
273
+ "SrcFileExtension": "txt",
274
+ "SrcFileMimeType": "text/plain",
275
+ "SrcFileSHA256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
276
+ "SrcFileSize": 14,
146
277
 
147
- ``` python
148
- ASIM_FORMATTERS = {
149
- "root": ASIMSystemFormatter,
150
- "django.request": ASIMRequestFormatter,
151
- }
152
- ```
278
+ # Calculated / Hard coded fields
279
+ "EventStartTime": "2025-07-30T11:05:09.406460+00:00",
280
+ "EventSchema": "FileEvent",
281
+ "EventSchemaVersion": "0.2.1",
282
+ "EventSeverity": "Informational",
153
283
 
154
- The default class for other loggers is:
284
+ # Taken from Django HttpRequest object
285
+ "HttpHost": "WebServer.local",
286
+ "SrcIpAddr": "192.168.1.101",
287
+ "TargetUrl": "https://WebServer.local/steel",
288
+ "TargetUsername": "Adrian"
155
289
 
156
- ``` python
157
- ASIMSystemFormatter
290
+ # Taken from DBT Platform environment variables
291
+ "TargetAppName": "export-analytics-frontend",
292
+ }
158
293
  ```
159
294
 
160
- ### Creating a custom `logging.Formatter`
295
+ #### Account Management event
161
296
 
162
- If you wish to create your own ASIM formatter, you can inherit from ASIMSystemFormatter and call _get_event_base to get the base level logging data for use in augmentation:
297
+ Following the [ASIM User Management Schema](https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-user-management).
163
298
 
164
- ``` python
165
- class ASIMSystemFormatter(ASIMFormatterBase):
166
- def get_event(self):
167
- logger_event = self._get_event_base()
168
299
 
169
- # Customise logger event
300
+ ```python
301
+ # Example usage
302
+ from django_log_formatter_asim.events import log_account_management
303
+
304
+ log_account_management(
305
+ request,
306
+ event=log_account_management.Event.UserCreated,
307
+ result=log_account_management.Result.Success,
308
+ account={
309
+ "username": "Roger",
310
+ },
311
+ )
312
+
313
+ # Example JSON printed to standard output
314
+ {
315
+ # Values provided as arguments
316
+ "EventType": "UserCreated",
317
+ "EventResult": "Success",
318
+ "TargetUsername": "Roger",
170
319
 
171
- return logger_event
320
+ # Calculated / Hard coded fields
321
+ "EventStartTime": "2025-07-30T11:05:09.406460+00:00",
322
+ "EventSchema": "UserManagement",
323
+ "EventSchemaVersion": "0.1.1",
324
+ "EventSeverity": "Informational",
325
+
326
+ # Taken from Django HttpRequest object
327
+ "HttpHost": "WebServer.local",
328
+ "SrcIpAddr": "192.168.1.101",
329
+ "TargetUrl": "https://WebServer.local/admin/create-user",
330
+ "ActorUsername": "Adrian"
331
+
332
+ # Taken from DBT Platform environment variables
333
+ "TargetAppName": "export-analytics-frontend",
334
+ }
172
335
  ```
173
336
 
174
337
  ## Dependencies
@@ -233,3 +396,4 @@ poetry publish
233
396
  Check the [PyPI Release history](https://pypi.org/project/django-log-formatter-asim/#history) to make sure the package has been updated.
234
397
 
235
398
  For an optional manual check, install the package locally and test everything works as expected.
399
+
@@ -56,6 +56,101 @@ LOGGING = {
56
56
  In this example we assign the ASIM formatter to a `handler` and ensure both `root` and `django` loggers use this `handler`.
57
57
  We then set `propagate` to `False` on the `django` logger, to avoid duplicating logs at the root level.
58
58
 
59
+ ### Settings
60
+
61
+ `DLFA_LOG_PERSONALLY_IDENTIFIABLE_INFORMATION` - the formatter checks this setting to see if personally identifiable information should be logged. If this is not set to true, only the user's id is logged.
62
+
63
+ `DLFA_TRACE_HEADERS` - used for defining custom zipkin headers, the defaults is `("X-Amzn-Trace-Id")`, but for applications hosted in GOV.UK PaaS you should use `("X-B3-TraceId", "X-B3-SpanId")`. If you are running your application in both places side by side during migration, the following should work in your Django settings:
64
+
65
+ ```python
66
+ from dbt_copilot_python.utility import is_copilot
67
+
68
+ if is_copilot():
69
+ DLFA_TRACE_HEADERS = ("X-B3-TraceId", "X-B3-SpanId")
70
+ ```
71
+
72
+ `DLFA_INCLUDE_RAW_LOG` - By default the original unformatted log is not included in the ASIM formatted log. You can enable that by setting this to `True` and it will be included in `AddidtionalFields.RawLog`.
73
+
74
+ > [!WARNING]
75
+ > Setting `DLFA_INCLUDE_RAW_LOG` to `True` will cause additional private fields to be output to your logs.
76
+ > This could include secrets, such as AWS Access Keys, private HTTP Request data, or personally identifiable information.
77
+ > This setting is not recommended for a production environment.
78
+
79
+ ### Serialisation behaviour
80
+
81
+ The package provides one `logging.Formatter` class, `ASIMFormatter` which routes log messages to a serialiser
82
+ which generates a python dict which the formatter converts to a JSON string and prints to standard output.
83
+
84
+ It has a generic serialiser called `ASIMRootFormatter` and a custom serlializer for log messages where the
85
+ logger is `django.request`.
86
+
87
+ ``` python
88
+ ASIM_FORMATTERS = {
89
+ "root": ASIMRootFormatter,
90
+ "django.request": ASIMRequestFormatter,
91
+ }
92
+ ```
93
+
94
+ #### ASIMRootFormatter
95
+
96
+ This serialiser outputs the following ASIM fields.
97
+
98
+ - `EventSchema` = `ProcessEvent`
99
+ - `ActingAppType` = `Django`
100
+ - `AdditionalFields[DjangoLogFormatterAsimVersion]`
101
+ - `EventSchemaVersion`
102
+ - `EventMessage`
103
+ - `EventCount`
104
+ - `EventStartTime`
105
+ - `EventEndTime`
106
+ - `EventType`
107
+ - `EventResult`
108
+ - `EventSeverity`
109
+ - `EventOriginalSeverity`
110
+
111
+ Additionally, the following DataDog fields where available:
112
+
113
+ - `dd.trace_id`
114
+ - `dd.span_id`
115
+ - `env`
116
+ - `service`
117
+ - `version`
118
+
119
+
120
+ #### ASIMRequestFormatter
121
+
122
+ This serialiser outputs the following ASIM fields in addition to the ones from ASIMRootFormatter.
123
+ It is coupled to the datastructure provided by the `django.request` logger.
124
+ The `django.request` logger only outputs requests where the response code is 4xx/5xx.
125
+
126
+ - `SrcIpAddr` and `IpAddr`
127
+ - `SrcPortNumber`
128
+ - `SrcUserId` and `SrcUsername`
129
+ - `HttpUserAgent`
130
+ - `AdditionalFields["TraceHeaders"][trace_header_name]` - See `DLFA_TRACE_HEADERS` setting for more information.
131
+
132
+ #### Creating a custom serialiser
133
+
134
+ If you wish to create your own ASIM serialiser, you can inherit from `ASIMRootFormatter` and call
135
+ `super().get_log_dict()` to get the base level logging data for augmentation:
136
+
137
+ ``` python
138
+ class MyASIMFormatter(ASIMRootFormatter):
139
+ def get_log_dict(self):
140
+ log_dict = super().get_log_dict()
141
+
142
+ # Customise logger event
143
+
144
+ return log_dict
145
+ ```
146
+
147
+ This serialiser can then be added to `ASIM_FORMATTERS`...
148
+
149
+ ```python
150
+ ASIM_FORMATTERS["my_logger"] = MyASIMFormatter
151
+ ```
152
+
153
+
59
154
  ### ASIM Events
60
155
 
61
156
  The events mostly follow the Microsoft schema but have been tailored to Department of Business and Trade needs.
@@ -106,48 +201,114 @@ log_authentication(
106
201
  }
107
202
  ```
108
203
 
109
- ### Settings
204
+ #### File Activity event
110
205
 
111
- `DLFA_LOG_PERSONALLY_IDENTIFIABLE_INFORMATION` - the formatter checks this setting to see if personally identifiable information should be logged. If this is not set to true, only the user's id is logged.
206
+ Following the [ASIM File Event Schema](https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-file-event).
112
207
 
113
- `DLFA_TRACE_HEADERS` - used for defining custom zipkin headers, the defaults is `("X-Amzn-Trace-Id")`, but for applications hosted in GOV.UK PaaS you should use `("X-B3-TraceId", "X-B3-SpanId")`. If you are running your application in both places side by side during migration, the following should work in your Django settings:
208
+ ```python
209
+ # Example usage
210
+ from django_log_formatter_asim.events import log_file_activity
114
211
 
115
- `DLFA_INCLUDE_RAW_LOG` - By default the original unformatted log is not included in the ASIM formatted log. You can enable that by setting this to `True` and it will be included in `AddidtionalFields.RawLog`.
212
+ log_file_activity(
213
+ request,
214
+ event=log_file_activity.Event.FileCopied,
215
+ result=log_file_activity.Result.Success,
216
+ file={
217
+ "path": "/tmp/copied.txt",
218
+ "content_type": "text/plain",
219
+ "extension": "txt",
220
+ "name": "copied.txt",
221
+ "sha256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
222
+ "size": 14,
223
+ },
224
+ # source_file is only necessary if the event is one of FileRenamed, FileMoved, FileCopied, FolderMoved
225
+ source_file={
226
+ "path": "/tmp/original.txt",
227
+ "content_type": "text/plain",
228
+ "extension": "txt",
229
+ "name": "original.txt",
230
+ "sha256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
231
+ "size": 14,
232
+ },
233
+ )
116
234
 
117
- ```python
118
- from dbt_copilot_python.utility import is_copilot
235
+ # Example JSON printed to standard output
236
+ {
237
+ # Values provided as arguments
238
+ "EventType": "FileCopied",
239
+ "EventResult": "Success",
119
240
 
120
- if is_copilot():
121
- DLFA_TRACE_HEADERS = ("X-B3-TraceId", "X-B3-SpanId")
122
- ```
241
+ "TargetFilePath": "/tmp/copied.txt",
242
+ "TargetFileName": "copied.txt",
243
+ "TargetFileExtension": "txt",
244
+ "TargetFileMimeType": "text/plain",
245
+ "TargetFileSHA256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
246
+ "TargetFileSize": 14,
123
247
 
124
- ### Formatter classes
248
+ "SrcFilePath": "/tmp/original.txt",
249
+ "SrcFileName": "original.txt",
250
+ "SrcFileExtension": "txt",
251
+ "SrcFileMimeType": "text/plain",
252
+ "SrcFileSHA256": "6798b7a132f37a0474002dec538ec52bdcd5f7b76e49e52c8a3d2016ca8d1d18",
253
+ "SrcFileSize": 14,
125
254
 
126
- ``` python
127
- ASIM_FORMATTERS = {
128
- "root": ASIMSystemFormatter,
129
- "django.request": ASIMRequestFormatter,
130
- }
131
- ```
255
+ # Calculated / Hard coded fields
256
+ "EventStartTime": "2025-07-30T11:05:09.406460+00:00",
257
+ "EventSchema": "FileEvent",
258
+ "EventSchemaVersion": "0.2.1",
259
+ "EventSeverity": "Informational",
132
260
 
133
- The default class for other loggers is:
261
+ # Taken from Django HttpRequest object
262
+ "HttpHost": "WebServer.local",
263
+ "SrcIpAddr": "192.168.1.101",
264
+ "TargetUrl": "https://WebServer.local/steel",
265
+ "TargetUsername": "Adrian"
134
266
 
135
- ``` python
136
- ASIMSystemFormatter
267
+ # Taken from DBT Platform environment variables
268
+ "TargetAppName": "export-analytics-frontend",
269
+ }
137
270
  ```
138
271
 
139
- ### Creating a custom `logging.Formatter`
272
+ #### Account Management event
140
273
 
141
- If you wish to create your own ASIM formatter, you can inherit from ASIMSystemFormatter and call _get_event_base to get the base level logging data for use in augmentation:
274
+ Following the [ASIM User Management Schema](https://learn.microsoft.com/en-us/azure/sentinel/normalization-schema-user-management).
142
275
 
143
- ``` python
144
- class ASIMSystemFormatter(ASIMFormatterBase):
145
- def get_event(self):
146
- logger_event = self._get_event_base()
147
276
 
148
- # Customise logger event
277
+ ```python
278
+ # Example usage
279
+ from django_log_formatter_asim.events import log_account_management
280
+
281
+ log_account_management(
282
+ request,
283
+ event=log_account_management.Event.UserCreated,
284
+ result=log_account_management.Result.Success,
285
+ account={
286
+ "username": "Roger",
287
+ },
288
+ )
289
+
290
+ # Example JSON printed to standard output
291
+ {
292
+ # Values provided as arguments
293
+ "EventType": "UserCreated",
294
+ "EventResult": "Success",
295
+ "TargetUsername": "Roger",
149
296
 
150
- return logger_event
297
+ # Calculated / Hard coded fields
298
+ "EventStartTime": "2025-07-30T11:05:09.406460+00:00",
299
+ "EventSchema": "UserManagement",
300
+ "EventSchemaVersion": "0.1.1",
301
+ "EventSeverity": "Informational",
302
+
303
+ # Taken from Django HttpRequest object
304
+ "HttpHost": "WebServer.local",
305
+ "SrcIpAddr": "192.168.1.101",
306
+ "TargetUrl": "https://WebServer.local/admin/create-user",
307
+ "ActorUsername": "Adrian"
308
+
309
+ # Taken from DBT Platform environment variables
310
+ "TargetAppName": "export-analytics-frontend",
311
+ }
151
312
  ```
152
313
 
153
314
  ## Dependencies
@@ -211,4 +372,4 @@ poetry publish
211
372
 
212
373
  Check the [PyPI Release history](https://pypi.org/project/django-log-formatter-asim/#history) to make sure the package has been updated.
213
374
 
214
- For an optional manual check, install the package locally and test everything works as expected.
375
+ For an optional manual check, install the package locally and test everything works as expected.
@@ -0,0 +1,7 @@
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)