binalyze-air-sdk 1.0.1__py3-none-any.whl → 1.0.3__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.
- binalyze_air/__init__.py +77 -77
- binalyze_air/apis/__init__.py +67 -27
- binalyze_air/apis/acquisitions.py +107 -0
- binalyze_air/apis/api_tokens.py +49 -0
- binalyze_air/apis/assets.py +161 -0
- binalyze_air/apis/audit_logs.py +26 -0
- binalyze_air/apis/{authentication.py → auth.py} +29 -27
- binalyze_air/apis/auto_asset_tags.py +79 -75
- binalyze_air/apis/backup.py +177 -0
- binalyze_air/apis/baseline.py +46 -0
- binalyze_air/apis/cases.py +225 -0
- binalyze_air/apis/cloud_forensics.py +116 -0
- binalyze_air/apis/event_subscription.py +96 -96
- binalyze_air/apis/evidence.py +249 -53
- binalyze_air/apis/interact.py +153 -36
- binalyze_air/apis/investigation_hub.py +234 -0
- binalyze_air/apis/license.py +104 -0
- binalyze_air/apis/logger.py +83 -0
- binalyze_air/apis/multipart_upload.py +201 -0
- binalyze_air/apis/notifications.py +115 -0
- binalyze_air/apis/organizations.py +267 -0
- binalyze_air/apis/params.py +44 -39
- binalyze_air/apis/policies.py +186 -0
- binalyze_air/apis/preset_filters.py +79 -0
- binalyze_air/apis/recent_activities.py +71 -0
- binalyze_air/apis/relay_server.py +104 -0
- binalyze_air/apis/settings.py +395 -27
- binalyze_air/apis/tasks.py +80 -0
- binalyze_air/apis/triage.py +197 -0
- binalyze_air/apis/user_management.py +183 -74
- binalyze_air/apis/webhook_executions.py +50 -0
- binalyze_air/apis/webhooks.py +322 -230
- binalyze_air/base.py +207 -133
- binalyze_air/client.py +217 -1337
- binalyze_air/commands/__init__.py +175 -145
- binalyze_air/commands/acquisitions.py +661 -387
- binalyze_air/commands/api_tokens.py +55 -0
- binalyze_air/commands/assets.py +324 -362
- binalyze_air/commands/{authentication.py → auth.py} +36 -36
- binalyze_air/commands/auto_asset_tags.py +230 -230
- binalyze_air/commands/backup.py +47 -0
- binalyze_air/commands/baseline.py +32 -396
- binalyze_air/commands/cases.py +609 -602
- binalyze_air/commands/cloud_forensics.py +88 -0
- binalyze_air/commands/event_subscription.py +101 -101
- binalyze_air/commands/evidences.py +918 -988
- binalyze_air/commands/interact.py +172 -58
- binalyze_air/commands/investigation_hub.py +315 -0
- binalyze_air/commands/license.py +183 -0
- binalyze_air/commands/logger.py +126 -0
- binalyze_air/commands/multipart_upload.py +363 -0
- binalyze_air/commands/notifications.py +45 -0
- binalyze_air/commands/organizations.py +200 -221
- binalyze_air/commands/policies.py +175 -203
- binalyze_air/commands/preset_filters.py +55 -0
- binalyze_air/commands/recent_activities.py +32 -0
- binalyze_air/commands/relay_server.py +144 -0
- binalyze_air/commands/settings.py +431 -29
- binalyze_air/commands/tasks.py +95 -56
- binalyze_air/commands/triage.py +224 -360
- binalyze_air/commands/user_management.py +351 -126
- binalyze_air/commands/webhook_executions.py +77 -0
- binalyze_air/config.py +244 -244
- binalyze_air/exceptions.py +49 -49
- binalyze_air/http_client.py +426 -305
- binalyze_air/models/__init__.py +287 -285
- binalyze_air/models/acquisitions.py +365 -250
- binalyze_air/models/api_tokens.py +73 -0
- binalyze_air/models/assets.py +438 -438
- binalyze_air/models/audit.py +247 -272
- binalyze_air/models/audit_logs.py +14 -0
- binalyze_air/models/{authentication.py → auth.py} +69 -69
- binalyze_air/models/auto_asset_tags.py +227 -116
- binalyze_air/models/backup.py +138 -0
- binalyze_air/models/baseline.py +231 -231
- binalyze_air/models/cases.py +275 -275
- binalyze_air/models/cloud_forensics.py +145 -0
- binalyze_air/models/event_subscription.py +170 -171
- binalyze_air/models/evidence.py +65 -65
- binalyze_air/models/evidences.py +367 -348
- binalyze_air/models/interact.py +266 -135
- binalyze_air/models/investigation_hub.py +265 -0
- binalyze_air/models/license.py +150 -0
- binalyze_air/models/logger.py +83 -0
- binalyze_air/models/multipart_upload.py +352 -0
- binalyze_air/models/notifications.py +138 -0
- binalyze_air/models/organizations.py +293 -293
- binalyze_air/models/params.py +153 -127
- binalyze_air/models/policies.py +260 -249
- binalyze_air/models/preset_filters.py +79 -0
- binalyze_air/models/recent_activities.py +70 -0
- binalyze_air/models/relay_server.py +121 -0
- binalyze_air/models/settings.py +538 -84
- binalyze_air/models/tasks.py +215 -149
- binalyze_air/models/triage.py +141 -142
- binalyze_air/models/user_management.py +200 -97
- binalyze_air/models/webhook_executions.py +33 -0
- binalyze_air/queries/__init__.py +121 -133
- binalyze_air/queries/acquisitions.py +155 -155
- binalyze_air/queries/api_tokens.py +46 -0
- binalyze_air/queries/assets.py +186 -105
- binalyze_air/queries/audit.py +400 -416
- binalyze_air/queries/{authentication.py → auth.py} +55 -55
- binalyze_air/queries/auto_asset_tags.py +59 -59
- binalyze_air/queries/backup.py +66 -0
- binalyze_air/queries/baseline.py +21 -185
- binalyze_air/queries/cases.py +292 -292
- binalyze_air/queries/cloud_forensics.py +137 -0
- binalyze_air/queries/event_subscription.py +54 -54
- binalyze_air/queries/evidence.py +139 -139
- binalyze_air/queries/evidences.py +279 -279
- binalyze_air/queries/interact.py +140 -28
- binalyze_air/queries/investigation_hub.py +329 -0
- binalyze_air/queries/license.py +85 -0
- binalyze_air/queries/logger.py +58 -0
- binalyze_air/queries/multipart_upload.py +180 -0
- binalyze_air/queries/notifications.py +71 -0
- binalyze_air/queries/organizations.py +222 -222
- binalyze_air/queries/params.py +154 -115
- binalyze_air/queries/policies.py +149 -149
- binalyze_air/queries/preset_filters.py +60 -0
- binalyze_air/queries/recent_activities.py +44 -0
- binalyze_air/queries/relay_server.py +42 -0
- binalyze_air/queries/settings.py +533 -20
- binalyze_air/queries/tasks.py +125 -81
- binalyze_air/queries/triage.py +230 -230
- binalyze_air/queries/user_management.py +193 -83
- binalyze_air/queries/webhook_executions.py +39 -0
- binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
- binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
- {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
- binalyze_air/apis/endpoints.py +0 -22
- binalyze_air/apis/evidences.py +0 -216
- binalyze_air/apis/users.py +0 -68
- binalyze_air/commands/users.py +0 -101
- binalyze_air/models/endpoints.py +0 -76
- binalyze_air/models/users.py +0 -82
- binalyze_air/queries/endpoints.py +0 -25
- binalyze_air/queries/users.py +0 -69
- binalyze_air_sdk-1.0.1.dist-info/METADATA +0 -635
- binalyze_air_sdk-1.0.1.dist-info/RECORD +0 -82
- {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
binalyze_air/queries/audit.py
CHANGED
@@ -1,417 +1,401 @@
|
|
1
|
-
"""
|
2
|
-
Audit-related queries for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List, Optional, Dict, Any
|
6
|
-
from datetime import datetime
|
7
|
-
|
8
|
-
from ..base import Query
|
9
|
-
from ..models.audit import (
|
10
|
-
AuditLog, AuditSummary, AuditUserActivity, AuditSystemEvent,
|
11
|
-
AuditRetentionPolicy, AuditFilter, AuditLogsFilter, AuditLevel, AuditCategory, AuditAction
|
12
|
-
)
|
13
|
-
from ..http_client import HTTPClient
|
14
|
-
|
15
|
-
|
16
|
-
class ListAuditLogsQuery(Query[List[AuditLog]]):
|
17
|
-
"""Query to list audit logs with optional filtering - UPDATED for new POST-based API."""
|
18
|
-
|
19
|
-
def __init__(self, http_client: HTTPClient, filter_params: Optional[AuditLogsFilter] = None, organization_ids: Optional[int] = None):
|
20
|
-
self.http_client = http_client
|
21
|
-
# Initialize filter with default organization IDs if not provided
|
22
|
-
if filter_params is None:
|
23
|
-
filter_params = AuditLogsFilter()
|
24
|
-
|
25
|
-
# Set organization parameters if not already set in filter
|
26
|
-
# Changed from List[int] to int to match new API spec
|
27
|
-
if filter_params.organization_ids is None and organization_ids is not None:
|
28
|
-
filter_params.organization_ids = organization_ids
|
29
|
-
elif filter_params.organization_ids is None:
|
30
|
-
filter_params.organization_ids = 0 # Default to organization 0
|
31
|
-
|
32
|
-
self.filter_params = filter_params
|
33
|
-
|
34
|
-
def execute(self) -> List[AuditLog]:
|
35
|
-
"""Execute the query to list audit logs using
|
36
|
-
# Use
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
mapped_data = {
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
self.
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
#
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
def
|
339
|
-
|
340
|
-
#
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
"
|
390
|
-
"
|
391
|
-
"
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
"result": None
|
402
|
-
}
|
403
|
-
except:
|
404
|
-
return {
|
405
|
-
"success": False,
|
406
|
-
"statusCode": raw_response.status_code,
|
407
|
-
"errors": [f"Export failed with status {raw_response.status_code}: {raw_response.text}"],
|
408
|
-
"result": None
|
409
|
-
}
|
410
|
-
|
411
|
-
except Exception as e:
|
412
|
-
return {
|
413
|
-
"success": False,
|
414
|
-
"statusCode": 500,
|
415
|
-
"errors": [f"Export request failed: {str(e)}"],
|
416
|
-
"result": None
|
1
|
+
"""
|
2
|
+
Audit-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional, Dict, Any
|
6
|
+
from datetime import datetime
|
7
|
+
|
8
|
+
from ..base import Query
|
9
|
+
from ..models.audit import (
|
10
|
+
AuditLog, AuditSummary, AuditUserActivity, AuditSystemEvent,
|
11
|
+
AuditRetentionPolicy, AuditFilter, AuditLogsFilter, AuditLevel, AuditCategory, AuditAction
|
12
|
+
)
|
13
|
+
from ..http_client import HTTPClient
|
14
|
+
|
15
|
+
|
16
|
+
class ListAuditLogsQuery(Query[List[AuditLog]]):
|
17
|
+
"""Query to list audit logs with optional filtering - UPDATED for new POST-based API."""
|
18
|
+
|
19
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[AuditLogsFilter] = None, organization_ids: Optional[int] = None):
|
20
|
+
self.http_client = http_client
|
21
|
+
# Initialize filter with default organization IDs if not provided
|
22
|
+
if filter_params is None:
|
23
|
+
filter_params = AuditLogsFilter()
|
24
|
+
|
25
|
+
# Set organization parameters if not already set in filter
|
26
|
+
# Changed from List[int] to int to match new API spec
|
27
|
+
if filter_params.organization_ids is None and organization_ids is not None:
|
28
|
+
filter_params.organization_ids = organization_ids
|
29
|
+
elif filter_params.organization_ids is None:
|
30
|
+
filter_params.organization_ids = 0 # Default to organization 0
|
31
|
+
|
32
|
+
self.filter_params = filter_params
|
33
|
+
|
34
|
+
def execute(self) -> List[AuditLog]:
|
35
|
+
"""Execute the query to list audit logs using GET method with query parameters."""
|
36
|
+
# Use GET method with query parameters as per API specification
|
37
|
+
params = self.filter_params.to_params()
|
38
|
+
|
39
|
+
print(f"[INFO] Using GET /audit-logs with params: {params}")
|
40
|
+
|
41
|
+
try:
|
42
|
+
response = self.http_client.get("audit-logs", params=params)
|
43
|
+
|
44
|
+
# Handle response structure
|
45
|
+
if isinstance(response, dict):
|
46
|
+
entities = (
|
47
|
+
response.get("result", {}).get("entities", []) or # Standard structure
|
48
|
+
response.get("entities", []) or # Direct entities
|
49
|
+
response.get("data", []) or # Alternative structure
|
50
|
+
response.get("logs", []) or # Logs structure
|
51
|
+
[]
|
52
|
+
)
|
53
|
+
else:
|
54
|
+
entities = []
|
55
|
+
|
56
|
+
print(f"[INFO] GET /audit-logs returned {len(entities)} audit logs")
|
57
|
+
|
58
|
+
except Exception as e:
|
59
|
+
print(f"[WARN] GET /audit-logs failed: {e}")
|
60
|
+
return []
|
61
|
+
|
62
|
+
logs = []
|
63
|
+
for entity_data in entities:
|
64
|
+
mapped_data = {
|
65
|
+
"id": entity_data.get("_id") or entity_data.get("id"),
|
66
|
+
"timestamp": entity_data.get("createdAt") or entity_data.get("timestamp"),
|
67
|
+
"user_id": entity_data.get("userId"),
|
68
|
+
"username": entity_data.get("performedBy") or entity_data.get("username"),
|
69
|
+
"organization_id": entity_data.get("organizationId", 0),
|
70
|
+
"category": entity_data.get("type") or entity_data.get("category"),
|
71
|
+
"action": entity_data.get("action"),
|
72
|
+
"resource_type": entity_data.get("resourceType"),
|
73
|
+
"resource_id": entity_data.get("resourceId"),
|
74
|
+
"resource_name": entity_data.get("resourceName"),
|
75
|
+
"level": entity_data.get("level", "info"),
|
76
|
+
"message": entity_data.get("description") or entity_data.get("message"),
|
77
|
+
"details": entity_data.get("details", {}),
|
78
|
+
"ip_address": entity_data.get("ipAddress"),
|
79
|
+
"user_agent": entity_data.get("userAgent"),
|
80
|
+
"session_id": entity_data.get("sessionId"),
|
81
|
+
"correlation_id": entity_data.get("correlationId"),
|
82
|
+
"success": entity_data.get("success", True),
|
83
|
+
"error_code": entity_data.get("errorCode"),
|
84
|
+
"duration": entity_data.get("duration"),
|
85
|
+
"tags": entity_data.get("tags", []),
|
86
|
+
}
|
87
|
+
|
88
|
+
# Remove None values
|
89
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
90
|
+
|
91
|
+
logs.append(AuditLog(**mapped_data))
|
92
|
+
|
93
|
+
return logs
|
94
|
+
|
95
|
+
|
96
|
+
class GetAuditLogQuery(Query[AuditLog]):
|
97
|
+
"""Query to get a specific audit log by ID."""
|
98
|
+
|
99
|
+
def __init__(self, http_client: HTTPClient, log_id: str):
|
100
|
+
self.http_client = http_client
|
101
|
+
self.log_id = log_id
|
102
|
+
|
103
|
+
def execute(self) -> AuditLog:
|
104
|
+
"""Execute the query to get audit log details."""
|
105
|
+
response = self.http_client.get(f"audit-logs/{self.log_id}")
|
106
|
+
|
107
|
+
entity_data = response.get("result", {})
|
108
|
+
|
109
|
+
mapped_data = {
|
110
|
+
"id": entity_data.get("_id"),
|
111
|
+
"timestamp": entity_data.get("createdAt"),
|
112
|
+
"user_id": entity_data.get("userId"),
|
113
|
+
"username": entity_data.get("performedBy"),
|
114
|
+
"organization_id": entity_data.get("organizationId", 0),
|
115
|
+
"category": entity_data.get("type"),
|
116
|
+
"action": entity_data.get("action"),
|
117
|
+
"resource_type": entity_data.get("resourceType"),
|
118
|
+
"resource_id": entity_data.get("resourceId"),
|
119
|
+
"resource_name": entity_data.get("resourceName"),
|
120
|
+
"level": entity_data.get("level", "info"),
|
121
|
+
"message": entity_data.get("description"),
|
122
|
+
"details": entity_data.get("details", {}),
|
123
|
+
"ip_address": entity_data.get("ipAddress"),
|
124
|
+
"user_agent": entity_data.get("userAgent"),
|
125
|
+
"session_id": entity_data.get("sessionId"),
|
126
|
+
"correlation_id": entity_data.get("correlationId"),
|
127
|
+
"success": entity_data.get("success", True),
|
128
|
+
"error_code": entity_data.get("errorCode"),
|
129
|
+
"duration": entity_data.get("duration"),
|
130
|
+
"tags": entity_data.get("tags", []),
|
131
|
+
}
|
132
|
+
|
133
|
+
# Remove None values
|
134
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
135
|
+
|
136
|
+
return AuditLog(**mapped_data)
|
137
|
+
|
138
|
+
|
139
|
+
class GetAuditSummaryQuery(Query[AuditSummary]):
|
140
|
+
"""Query to get audit summary for a date range."""
|
141
|
+
|
142
|
+
def __init__(self, http_client: HTTPClient, organization_id: int, start_date: datetime, end_date: datetime):
|
143
|
+
self.http_client = http_client
|
144
|
+
self.organization_id = organization_id
|
145
|
+
self.start_date = start_date
|
146
|
+
self.end_date = end_date
|
147
|
+
|
148
|
+
def execute(self) -> AuditSummary:
|
149
|
+
"""Execute the query to get audit summary."""
|
150
|
+
params = {
|
151
|
+
"organizationId": str(self.organization_id),
|
152
|
+
"startDate": self.start_date.isoformat(),
|
153
|
+
"endDate": self.end_date.isoformat()
|
154
|
+
}
|
155
|
+
|
156
|
+
response = self.http_client.get("audit/summary", params=params)
|
157
|
+
|
158
|
+
entity_data = response.get("result", {})
|
159
|
+
|
160
|
+
mapped_data = {
|
161
|
+
"organization_id": entity_data.get("organizationId", self.organization_id),
|
162
|
+
"date": entity_data.get("date", self.start_date),
|
163
|
+
"total_events": entity_data.get("totalEvents", 0),
|
164
|
+
"successful_events": entity_data.get("successfulEvents", 0),
|
165
|
+
"failed_events": entity_data.get("failedEvents", 0),
|
166
|
+
"authentication_events": entity_data.get("authenticationEvents", 0),
|
167
|
+
"authorization_events": entity_data.get("authorizationEvents", 0),
|
168
|
+
"data_access_events": entity_data.get("dataAccessEvents", 0),
|
169
|
+
"system_change_events": entity_data.get("systemChangeEvents", 0),
|
170
|
+
"user_action_events": entity_data.get("userActionEvents", 0),
|
171
|
+
"api_call_events": entity_data.get("apiCallEvents", 0),
|
172
|
+
"unique_users": entity_data.get("uniqueUsers", 0),
|
173
|
+
"unique_ips": entity_data.get("uniqueIps", 0),
|
174
|
+
"top_users": entity_data.get("topUsers", []),
|
175
|
+
"top_actions": entity_data.get("topActions", []),
|
176
|
+
"error_summary": entity_data.get("errorSummary", []),
|
177
|
+
}
|
178
|
+
|
179
|
+
# Remove None values
|
180
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
181
|
+
|
182
|
+
return AuditSummary(**mapped_data)
|
183
|
+
|
184
|
+
|
185
|
+
class GetUserActivityQuery(Query[List[AuditUserActivity]]):
|
186
|
+
"""Query to get user activity audit logs."""
|
187
|
+
|
188
|
+
def __init__(self, http_client: HTTPClient, organization_id: int, start_date: datetime, end_date: datetime, user_id: Optional[str] = None):
|
189
|
+
self.http_client = http_client
|
190
|
+
self.organization_id = organization_id
|
191
|
+
self.start_date = start_date
|
192
|
+
self.end_date = end_date
|
193
|
+
self.user_id = user_id
|
194
|
+
|
195
|
+
def execute(self) -> List[AuditUserActivity]:
|
196
|
+
"""Execute the query to get user activity."""
|
197
|
+
params = {
|
198
|
+
"organizationId": str(self.organization_id),
|
199
|
+
"startDate": self.start_date.isoformat(),
|
200
|
+
"endDate": self.end_date.isoformat()
|
201
|
+
}
|
202
|
+
|
203
|
+
if self.user_id:
|
204
|
+
params["userId"] = self.user_id
|
205
|
+
|
206
|
+
response = self.http_client.get("audit/user-activity", params=params)
|
207
|
+
|
208
|
+
entities = response.get("result", {}).get("entities", [])
|
209
|
+
|
210
|
+
activities = []
|
211
|
+
for entity_data in entities:
|
212
|
+
mapped_data = {
|
213
|
+
"user_id": entity_data.get("userId"),
|
214
|
+
"username": entity_data.get("username"),
|
215
|
+
"organization_id": entity_data.get("organizationId", self.organization_id),
|
216
|
+
"date": entity_data.get("date"),
|
217
|
+
"login_count": entity_data.get("loginCount", 0),
|
218
|
+
"action_count": entity_data.get("actionCount", 0),
|
219
|
+
"failed_login_count": entity_data.get("failedLoginCount", 0),
|
220
|
+
"last_login": entity_data.get("lastLogin"),
|
221
|
+
"last_action": entity_data.get("lastAction"),
|
222
|
+
"unique_ips": entity_data.get("uniqueIps", []),
|
223
|
+
"actions_by_category": entity_data.get("actionsByCategory", {}),
|
224
|
+
"risk_score": entity_data.get("riskScore", 0.0),
|
225
|
+
}
|
226
|
+
|
227
|
+
# Remove None values
|
228
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
229
|
+
|
230
|
+
activities.append(AuditUserActivity(**mapped_data))
|
231
|
+
|
232
|
+
return activities
|
233
|
+
|
234
|
+
|
235
|
+
class GetSystemEventsQuery(Query[List[AuditSystemEvent]]):
|
236
|
+
"""Query to get system events audit logs."""
|
237
|
+
|
238
|
+
def __init__(self, http_client: HTTPClient, organization_id: int, start_date: datetime, end_date: datetime, severity: Optional[AuditLevel] = None):
|
239
|
+
self.http_client = http_client
|
240
|
+
self.organization_id = organization_id
|
241
|
+
self.start_date = start_date
|
242
|
+
self.end_date = end_date
|
243
|
+
self.severity = severity
|
244
|
+
|
245
|
+
def execute(self) -> List[AuditSystemEvent]:
|
246
|
+
"""Execute the query to get system events."""
|
247
|
+
params = {
|
248
|
+
"organizationId": str(self.organization_id),
|
249
|
+
"startDate": self.start_date.isoformat(),
|
250
|
+
"endDate": self.end_date.isoformat()
|
251
|
+
}
|
252
|
+
|
253
|
+
if self.severity:
|
254
|
+
# Handle both enum and string values - FIXED
|
255
|
+
if hasattr(self.severity, 'value'):
|
256
|
+
params["severity"] = self.severity.value
|
257
|
+
else:
|
258
|
+
params["severity"] = self.severity
|
259
|
+
|
260
|
+
response = self.http_client.get("audit/system-events", params=params)
|
261
|
+
|
262
|
+
entities = response.get("result", {}).get("entities", [])
|
263
|
+
|
264
|
+
events = []
|
265
|
+
for entity_data in entities:
|
266
|
+
mapped_data = {
|
267
|
+
"id": entity_data.get("_id"),
|
268
|
+
"timestamp": entity_data.get("timestamp"),
|
269
|
+
"event_type": entity_data.get("eventType"),
|
270
|
+
"severity": entity_data.get("severity", "info"),
|
271
|
+
"component": entity_data.get("component"),
|
272
|
+
"message": entity_data.get("message"),
|
273
|
+
"details": entity_data.get("details", {}),
|
274
|
+
"organization_id": entity_data.get("organizationId", self.organization_id),
|
275
|
+
"resolved": entity_data.get("resolved", False),
|
276
|
+
"resolved_by": entity_data.get("resolvedBy"),
|
277
|
+
"resolved_at": entity_data.get("resolvedAt"),
|
278
|
+
}
|
279
|
+
|
280
|
+
# Remove None values
|
281
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
282
|
+
|
283
|
+
events.append(AuditSystemEvent(**mapped_data))
|
284
|
+
|
285
|
+
return events
|
286
|
+
|
287
|
+
|
288
|
+
class GetAuditRetentionPolicyQuery(Query[AuditRetentionPolicy]):
|
289
|
+
"""Query to get audit retention policy."""
|
290
|
+
|
291
|
+
def __init__(self, http_client: HTTPClient, organization_id: int):
|
292
|
+
self.http_client = http_client
|
293
|
+
self.organization_id = organization_id
|
294
|
+
|
295
|
+
def execute(self) -> AuditRetentionPolicy:
|
296
|
+
"""Execute the query to get audit retention policy."""
|
297
|
+
response = self.http_client.get(f"audit/retention-policy/{self.organization_id}")
|
298
|
+
|
299
|
+
entity_data = response.get("result", {})
|
300
|
+
|
301
|
+
mapped_data = {
|
302
|
+
"organization_id": entity_data.get("organizationId", self.organization_id),
|
303
|
+
"retention_days": entity_data.get("retentionDays", 365),
|
304
|
+
"auto_archive": entity_data.get("autoArchive", True),
|
305
|
+
"archive_location": entity_data.get("archiveLocation"),
|
306
|
+
"compress_archives": entity_data.get("compressArchives", True),
|
307
|
+
"delete_after_archive": entity_data.get("deleteAfterArchive", False),
|
308
|
+
"created_at": entity_data.get("createdAt"),
|
309
|
+
"updated_at": entity_data.get("updatedAt"),
|
310
|
+
"created_by": entity_data.get("createdBy"),
|
311
|
+
}
|
312
|
+
|
313
|
+
# Remove None values
|
314
|
+
mapped_data = {k: v for k, v in mapped_data.items() if v is not None}
|
315
|
+
|
316
|
+
return AuditRetentionPolicy(**mapped_data)
|
317
|
+
|
318
|
+
|
319
|
+
class ExportAuditLogsQuery(Query[Dict[str, Any]]):
|
320
|
+
"""Query to export audit logs with filtering - UPDATED for new API."""
|
321
|
+
|
322
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[AuditLogsFilter] = None, format: str = "json", organization_ids: Optional[int] = None):
|
323
|
+
self.http_client = http_client
|
324
|
+
# Initialize filter with default organization IDs if not provided
|
325
|
+
if filter_params is None:
|
326
|
+
filter_params = AuditLogsFilter()
|
327
|
+
|
328
|
+
# Set organization parameters if not already set in filter
|
329
|
+
# Changed from List[int] to int to match new API spec
|
330
|
+
if filter_params.organization_ids is None and organization_ids is not None:
|
331
|
+
filter_params.organization_ids = organization_ids
|
332
|
+
elif filter_params.organization_ids is None:
|
333
|
+
filter_params.organization_ids = 0 # Default to organization 0
|
334
|
+
|
335
|
+
self.filter_params = filter_params
|
336
|
+
self.format = format
|
337
|
+
|
338
|
+
def execute(self) -> Dict[str, Any]:
|
339
|
+
"""Execute the query to export audit logs."""
|
340
|
+
# Use filter's parameter generation
|
341
|
+
params = self.filter_params.to_params()
|
342
|
+
|
343
|
+
# Export endpoint returns binary data, not JSON - handle appropriately
|
344
|
+
try:
|
345
|
+
# Use raw HTTP request for binary data
|
346
|
+
import requests
|
347
|
+
url = f"{self.http_client.config.host}/api/public/audit-logs/export"
|
348
|
+
headers = {
|
349
|
+
"Authorization": f"Bearer {self.http_client.config.api_token}",
|
350
|
+
"User-Agent": "binalyze-air-sdk/1.0.0"
|
351
|
+
}
|
352
|
+
|
353
|
+
raw_response = requests.get(
|
354
|
+
url,
|
355
|
+
headers=headers,
|
356
|
+
params=params,
|
357
|
+
verify=self.http_client.config.verify_ssl,
|
358
|
+
timeout=self.http_client.config.timeout
|
359
|
+
)
|
360
|
+
|
361
|
+
if raw_response.status_code == 200:
|
362
|
+
# For binary/compressed data, return metadata about the export
|
363
|
+
content_type = raw_response.headers.get("content-type", "application/octet-stream")
|
364
|
+
content_length = len(raw_response.content) if raw_response.content else 0
|
365
|
+
|
366
|
+
return {
|
367
|
+
"success": True,
|
368
|
+
"statusCode": 200,
|
369
|
+
"errors": [],
|
370
|
+
"result": {
|
371
|
+
"exported": True,
|
372
|
+
"format": "binary",
|
373
|
+
"content_type": content_type,
|
374
|
+
"content_length": content_length,
|
375
|
+
"data_preview": raw_response.content[:100].decode('utf-8', errors='ignore') if raw_response.content else ""
|
376
|
+
}
|
377
|
+
}
|
378
|
+
else:
|
379
|
+
try:
|
380
|
+
error_data = raw_response.json()
|
381
|
+
return {
|
382
|
+
"success": False,
|
383
|
+
"statusCode": raw_response.status_code,
|
384
|
+
"errors": error_data.get("errors", [f"Export failed with status {raw_response.status_code}"]),
|
385
|
+
"result": None
|
386
|
+
}
|
387
|
+
except:
|
388
|
+
return {
|
389
|
+
"success": False,
|
390
|
+
"statusCode": raw_response.status_code,
|
391
|
+
"errors": [f"Export failed with status {raw_response.status_code}: {raw_response.text}"],
|
392
|
+
"result": None
|
393
|
+
}
|
394
|
+
|
395
|
+
except Exception as e:
|
396
|
+
return {
|
397
|
+
"success": False,
|
398
|
+
"statusCode": 500,
|
399
|
+
"errors": [f"Export request failed: {str(e)}"],
|
400
|
+
"result": None
|
417
401
|
}
|