binalyze-air-sdk 1.0.2__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.2.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.2.dist-info/METADATA +0 -706
- binalyze_air_sdk-1.0.2.dist-info/RECORD +0 -82
- {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
binalyze_air/client.py
CHANGED
@@ -1,1338 +1,218 @@
|
|
1
|
-
"""
|
2
|
-
Main client for the Binalyze AIR SDK using CQRS architecture.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
|
-
from typing import List, Optional, Union, Dict, Any
|
7
|
-
from datetime import datetime
|
8
|
-
|
9
|
-
from .config import AIRConfig
|
10
|
-
from .http_client import HTTPClient
|
11
|
-
|
12
|
-
# Import models
|
13
|
-
from .models.assets import Asset, AssetDetail, AssetTask, AssetFilter, AssetTaskFilter
|
14
|
-
from .models.cases import (
|
15
|
-
Case, CaseActivity, CaseEndpoint, CaseTask, User, CaseFilter, CaseActivityFilter,
|
16
|
-
CreateCaseRequest, UpdateCaseRequest, CaseNote, CaseEndpointFilter, CaseTaskFilter, CaseUserFilter
|
17
|
-
)
|
18
|
-
from .models.tasks import Task, TaskFilter, TaskAssignment
|
19
|
-
from .models.acquisitions import (
|
20
|
-
AcquisitionProfile, AcquisitionProfileDetails, AcquisitionFilter,
|
21
|
-
AcquisitionTaskRequest, ImageAcquisitionTaskRequest, CreateAcquisitionProfileRequest
|
22
|
-
)
|
23
|
-
from .models.policies import (
|
24
|
-
Policy, PolicyAssignment, PolicyExecution, PolicyFilter,
|
25
|
-
CreatePolicyRequest, UpdatePolicyRequest, AssignPolicyRequest
|
26
|
-
)
|
27
|
-
from .models.organizations import (
|
28
|
-
Organization, OrganizationUser, OrganizationRole, OrganizationLicense,
|
29
|
-
OrganizationSettings, OrganizationFilter, CreateOrganizationRequest,
|
30
|
-
UpdateOrganizationRequest, AddUserToOrganizationRequest, OrganizationsPaginatedResponse,
|
31
|
-
OrganizationUsersPaginatedResponse
|
32
|
-
)
|
33
|
-
from .models.triage import (
|
34
|
-
TriageRule, TriageTag,
|
35
|
-
TriageFilter, CreateTriageRuleRequest, UpdateTriageRuleRequest,
|
36
|
-
CreateTriageTagRequest, CreateTriageProfileRequest
|
37
|
-
)
|
38
|
-
from .models.audit import (
|
39
|
-
AuditLog, AuditSummary, AuditUserActivity, AuditSystemEvent,
|
40
|
-
AuditRetentionPolicy, AuditFilter, AuditLogsFilter, AuditLevel
|
41
|
-
)
|
42
|
-
from .models.baseline import (
|
43
|
-
Baseline, BaselineProfile, BaselineComparison, BaselineSchedule,
|
44
|
-
BaselineFilter, CreateBaselineRequest, UpdateBaselineRequest,
|
45
|
-
CreateBaselineProfileRequest, CompareBaselineRequest
|
46
|
-
)
|
47
|
-
from .models.
|
48
|
-
AuthStatus, LoginRequest, LoginResponse
|
49
|
-
)
|
50
|
-
from .models.user_management import (
|
51
|
-
UserManagementUser, CreateUserRequest, UpdateUserRequest,
|
52
|
-
AIUser, CreateAIUserRequest, APIUser, CreateAPIUserRequest, UserFilter
|
53
|
-
)
|
54
|
-
from .models.evidence import (
|
55
|
-
EvidencePPC, EvidenceReportFileInfo, EvidenceReport
|
56
|
-
)
|
57
|
-
from .models.auto_asset_tags import (
|
58
|
-
AutoAssetTag, CreateAutoAssetTagRequest, UpdateAutoAssetTagRequest,
|
59
|
-
StartTaggingRequest, TaggingResult, AutoAssetTagFilter
|
60
|
-
)
|
61
|
-
from .models.evidences import (
|
62
|
-
EvidenceRepository, AmazonS3Repository, AzureStorageRepository,
|
63
|
-
FTPSRepository, SFTPRepository, SMBRepository, RepositoryFilter,
|
64
|
-
CreateAmazonS3RepositoryRequest, UpdateAmazonS3RepositoryRequest,
|
65
|
-
CreateAzureStorageRepositoryRequest, UpdateAzureStorageRepositoryRequest,
|
66
|
-
CreateFTPSRepositoryRequest, UpdateFTPSRepositoryRequest,
|
67
|
-
CreateSFTPRepositoryRequest, UpdateSFTPRepositoryRequest,
|
68
|
-
CreateSMBRepositoryRequest, UpdateSMBRepositoryRequest,
|
69
|
-
ValidateRepositoryRequest, ValidationResult
|
70
|
-
)
|
71
|
-
from .models.event_subscription import (
|
72
|
-
EventSubscription, EventSubscriptionFilter, CreateEventSubscriptionRequest,
|
73
|
-
UpdateEventSubscriptionRequest
|
74
|
-
)
|
75
|
-
from .models.interact import (
|
76
|
-
ShellInteraction, AssignShellTaskRequest, ShellTaskResponse
|
77
|
-
)
|
78
|
-
from .models.params import (
|
79
|
-
AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer
|
80
|
-
)
|
81
|
-
from .models.settings import (
|
82
|
-
BannerSettings, UpdateBannerSettingsRequest
|
83
|
-
)
|
84
|
-
from .models.
|
85
|
-
|
86
|
-
)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
from .
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
from .
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
from .
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
from .
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
def
|
210
|
-
"""
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
219
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
220
|
-
command = ShutdownAssetsCommand(self.http_client, asset_filter)
|
221
|
-
return command.execute()
|
222
|
-
|
223
|
-
def add_tags(self, endpoint_ids: List[str], tags: List[str], organization_ids: Optional[List[Union[int, str]]] = None) -> Dict[str, Any]:
|
224
|
-
"""Add tags to assets."""
|
225
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
226
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
227
|
-
command = AddTagsToAssetsCommand(self.http_client, asset_filter, tags)
|
228
|
-
return command.execute()
|
229
|
-
|
230
|
-
def remove_tags(self, endpoint_ids: List[str], tags: List[str], organization_ids: Optional[List[Union[int, str]]] = None) -> Dict[str, Any]:
|
231
|
-
"""Remove tags from assets."""
|
232
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
233
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
234
|
-
command = RemoveTagsFromAssetsCommand(self.http_client, asset_filter, tags)
|
235
|
-
return command.execute()
|
236
|
-
|
237
|
-
def uninstall(self, endpoint_ids: List[str], purge_data: bool = False, organization_ids: Optional[List[Union[int, str]]] = None) -> Dict[str, Any]:
|
238
|
-
"""Uninstall assets with optional data purging."""
|
239
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
240
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
241
|
-
if purge_data:
|
242
|
-
from .commands.assets import PurgeAndUninstallAssetsCommand
|
243
|
-
command = PurgeAndUninstallAssetsCommand(self.http_client, asset_filter)
|
244
|
-
else:
|
245
|
-
from .commands.assets import UninstallAssetsCommand
|
246
|
-
command = UninstallAssetsCommand(self.http_client, asset_filter)
|
247
|
-
return command.execute()
|
248
|
-
|
249
|
-
def retrieve_logs(self, endpoint_ids: List[str], organization_ids: Optional[List[Union[int, str]]] = None) -> Dict[str, Any]:
|
250
|
-
"""Retrieve logs from assets."""
|
251
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
252
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
253
|
-
command = LogRetrievalCommand(self.http_client, asset_filter)
|
254
|
-
return command.execute()
|
255
|
-
|
256
|
-
def version_update(self, endpoint_ids: List[str], organization_ids: Optional[List[Union[int, str]]] = None) -> Dict[str, Any]:
|
257
|
-
"""Update version on assets."""
|
258
|
-
from .commands.assets import create_asset_filter_from_endpoint_ids
|
259
|
-
asset_filter = create_asset_filter_from_endpoint_ids(endpoint_ids, organization_ids)
|
260
|
-
command = VersionUpdateCommand(self.http_client, asset_filter)
|
261
|
-
return command.execute()
|
262
|
-
|
263
|
-
|
264
|
-
class CasesAPI:
|
265
|
-
"""Cases API with CQRS pattern - separated queries and commands."""
|
266
|
-
|
267
|
-
def __init__(self, http_client: HTTPClient):
|
268
|
-
self.http_client = http_client
|
269
|
-
|
270
|
-
# QUERIES (Read operations)
|
271
|
-
def list(self, filter_params: Optional[CaseFilter] = None, organization_ids: Optional[List[int]] = None) -> List[Case]:
|
272
|
-
"""List cases with optional filtering."""
|
273
|
-
query = ListCasesQuery(self.http_client, filter_params, organization_ids)
|
274
|
-
return query.execute()
|
275
|
-
|
276
|
-
def get(self, case_id: str) -> Case:
|
277
|
-
"""Get a specific case by ID."""
|
278
|
-
query = GetCaseQuery(self.http_client, case_id)
|
279
|
-
return query.execute()
|
280
|
-
|
281
|
-
def get_activities(self, case_id: str, filter_params: Optional[CaseActivityFilter] = None) -> List[CaseActivity]:
|
282
|
-
"""Get activities for a specific case with optional filtering, pagination, and sorting."""
|
283
|
-
query = GetCaseActivitiesQuery(self.http_client, case_id, filter_params)
|
284
|
-
return query.execute()
|
285
|
-
|
286
|
-
def get_endpoints(self, case_id: str, filter_params: Optional[CaseEndpointFilter] = None, organization_ids: Optional[List[int]] = None) -> List[CaseEndpoint]:
|
287
|
-
"""Get endpoints for a specific case with comprehensive filtering support.
|
288
|
-
|
289
|
-
Args:
|
290
|
-
case_id: The case ID to get endpoints for
|
291
|
-
filter_params: Optional CaseEndpointFilter with comprehensive filtering options
|
292
|
-
organization_ids: Optional list of organization IDs (for backward compatibility)
|
293
|
-
|
294
|
-
Returns:
|
295
|
-
List of CaseEndpoint objects
|
296
|
-
|
297
|
-
Note: If both filter_params and organization_ids are provided, organization_ids in filter_params takes precedence.
|
298
|
-
"""
|
299
|
-
# Handle backward compatibility
|
300
|
-
if filter_params is None:
|
301
|
-
filter_params = CaseEndpointFilter()
|
302
|
-
|
303
|
-
# If organization_ids is provided and not set in filter_params, use it
|
304
|
-
if organization_ids is not None and filter_params.organization_ids is None:
|
305
|
-
filter_params.organization_ids = organization_ids
|
306
|
-
|
307
|
-
query = GetCaseEndpointsQuery(self.http_client, case_id, filter_params)
|
308
|
-
return query.execute()
|
309
|
-
|
310
|
-
def get_tasks(self, case_id: str, filter_params: Optional[CaseTaskFilter] = None, organization_ids: Optional[List[int]] = None) -> List[CaseTask]:
|
311
|
-
"""Get tasks for a specific case with comprehensive filtering support.
|
312
|
-
|
313
|
-
Args:
|
314
|
-
case_id: The case ID to get tasks for
|
315
|
-
filter_params: Optional CaseTaskFilter with comprehensive filtering options
|
316
|
-
organization_ids: Optional list of organization IDs (for backward compatibility)
|
317
|
-
|
318
|
-
Returns:
|
319
|
-
List of CaseTask objects
|
320
|
-
|
321
|
-
Note: If both filter_params and organization_ids are provided, organization_ids in filter_params takes precedence.
|
322
|
-
"""
|
323
|
-
# Handle backward compatibility
|
324
|
-
if filter_params is None:
|
325
|
-
filter_params = CaseTaskFilter()
|
326
|
-
|
327
|
-
# If organization_ids is provided and not set in filter_params, use it
|
328
|
-
if organization_ids is not None and filter_params.organization_ids is None:
|
329
|
-
filter_params.organization_ids = organization_ids
|
330
|
-
|
331
|
-
query = GetCaseTasksQuery(self.http_client, case_id, filter_params)
|
332
|
-
return query.execute()
|
333
|
-
|
334
|
-
def get_users(self, case_id: str, filter_params: Optional[CaseUserFilter] = None, organization_ids: Optional[List[int]] = None) -> List[User]:
|
335
|
-
"""Get users for a specific case with comprehensive filtering support.
|
336
|
-
|
337
|
-
Args:
|
338
|
-
case_id: The case ID to get users for
|
339
|
-
filter_params: Optional CaseUserFilter with comprehensive filtering options
|
340
|
-
organization_ids: Optional list of organization IDs (for backward compatibility)
|
341
|
-
|
342
|
-
Returns:
|
343
|
-
List of User objects
|
344
|
-
|
345
|
-
Note: If both filter_params and organization_ids are provided, organization_ids in filter_params takes precedence.
|
346
|
-
"""
|
347
|
-
# Handle backward compatibility
|
348
|
-
if filter_params is None:
|
349
|
-
filter_params = CaseUserFilter()
|
350
|
-
|
351
|
-
# If organization_ids is provided and not set in filter_params, use it
|
352
|
-
if organization_ids is not None and filter_params.organization_ids is None:
|
353
|
-
filter_params.organization_ids = organization_ids
|
354
|
-
|
355
|
-
query = GetCaseUsersQuery(self.http_client, case_id, filter_params)
|
356
|
-
return query.execute()
|
357
|
-
|
358
|
-
def check_name(self, name: str) -> bool:
|
359
|
-
"""Check if a case name is available."""
|
360
|
-
query = CheckCaseNameQuery(self.http_client, name)
|
361
|
-
return query.execute()
|
362
|
-
|
363
|
-
# COMMANDS (Write operations)
|
364
|
-
def create(self, case_data: CreateCaseRequest) -> Case:
|
365
|
-
"""Create a new case."""
|
366
|
-
command = CreateCaseCommand(self.http_client, case_data)
|
367
|
-
return command.execute()
|
368
|
-
|
369
|
-
def update(self, case_id: str, update_data: UpdateCaseRequest) -> Case:
|
370
|
-
"""Update an existing case."""
|
371
|
-
command = UpdateCaseCommand(self.http_client, case_id, update_data)
|
372
|
-
return command.execute()
|
373
|
-
|
374
|
-
def close(self, case_id: str) -> Case:
|
375
|
-
"""Close a case."""
|
376
|
-
command = CloseCaseCommand(self.http_client, case_id)
|
377
|
-
return command.execute()
|
378
|
-
|
379
|
-
def open(self, case_id: str) -> Case:
|
380
|
-
"""Open a case."""
|
381
|
-
command = OpenCaseCommand(self.http_client, case_id)
|
382
|
-
return command.execute()
|
383
|
-
|
384
|
-
def archive(self, case_id: str) -> Case:
|
385
|
-
"""Archive a case."""
|
386
|
-
command = ArchiveCaseCommand(self.http_client, case_id)
|
387
|
-
return command.execute()
|
388
|
-
|
389
|
-
def change_owner(self, case_id: str, new_owner_id: str) -> Case:
|
390
|
-
"""Change case owner."""
|
391
|
-
command = ChangeCaseOwnerCommand(self.http_client, case_id, new_owner_id)
|
392
|
-
return command.execute()
|
393
|
-
|
394
|
-
def remove_endpoints(self, case_id: str, filter_params: AssetFilter) -> Dict[str, Any]:
|
395
|
-
"""Remove endpoints from a case."""
|
396
|
-
command = RemoveEndpointsFromCaseCommand(self.http_client, case_id, filter_params)
|
397
|
-
return command.execute()
|
398
|
-
|
399
|
-
def remove_task_assignment(self, case_id: str, task_assignment_id: str) -> Dict[str, Any]:
|
400
|
-
"""Remove task assignment from a case."""
|
401
|
-
command = RemoveTaskAssignmentFromCaseCommand(self.http_client, case_id, task_assignment_id)
|
402
|
-
return command.execute()
|
403
|
-
|
404
|
-
def import_task_assignments(self, case_id: str, task_assignment_ids: List[str]) -> Dict[str, Any]:
|
405
|
-
"""Import task assignments to a case."""
|
406
|
-
command = ImportTaskAssignmentsToCaseCommand(self.http_client, case_id, task_assignment_ids)
|
407
|
-
return command.execute()
|
408
|
-
|
409
|
-
def add_note(self, case_id: str, note_value: str) -> CaseNote:
|
410
|
-
"""Add a note to a case."""
|
411
|
-
command = AddNoteToCaseCommand(self.http_client, case_id, note_value)
|
412
|
-
return command.execute()
|
413
|
-
|
414
|
-
def update_note(self, case_id: str, note_id: str, note_value: str) -> CaseNote:
|
415
|
-
"""Update a note in a case."""
|
416
|
-
command = UpdateNoteToCaseCommand(self.http_client, case_id, note_id, note_value)
|
417
|
-
return command.execute()
|
418
|
-
|
419
|
-
def delete_note(self, case_id: str, note_id: str) -> Dict[str, Any]:
|
420
|
-
"""Delete a note from a case."""
|
421
|
-
command = DeleteNoteToCaseCommand(self.http_client, case_id, note_id)
|
422
|
-
return command.execute()
|
423
|
-
|
424
|
-
def export_notes(self, case_id: str) -> Dict[str, Any]:
|
425
|
-
"""Export case notes as a file download (ZIP/CSV format)."""
|
426
|
-
command = ExportCaseNotesCommand(self.http_client, case_id)
|
427
|
-
return command.execute()
|
428
|
-
|
429
|
-
def export_cases(self, filter_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
430
|
-
"""Export cases as a CSV file download."""
|
431
|
-
command = ExportCasesCommand(self.http_client, filter_params)
|
432
|
-
return command.execute()
|
433
|
-
|
434
|
-
def export_endpoints(self, case_id: str, filter_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
435
|
-
"""Export case endpoints as a CSV file download with optional filtering."""
|
436
|
-
command = ExportCaseEndpointsCommand(self.http_client, case_id, filter_params)
|
437
|
-
return command.execute()
|
438
|
-
|
439
|
-
def export_activities(self, case_id: str, filter_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
440
|
-
"""Export case activities as a CSV file download with optional filtering and pagination."""
|
441
|
-
command = ExportCaseActivitiesCommand(self.http_client, case_id, filter_params)
|
442
|
-
return command.execute()
|
443
|
-
|
444
|
-
|
445
|
-
class TasksAPI:
|
446
|
-
"""Tasks API with CQRS pattern - separated queries and commands."""
|
447
|
-
|
448
|
-
def __init__(self, http_client: HTTPClient):
|
449
|
-
self.http_client = http_client
|
450
|
-
|
451
|
-
# QUERIES (Read operations)
|
452
|
-
def list(self, filter_params: Optional[TaskFilter] = None, organization_ids: Optional[List[int]] = None) -> List[Task]:
|
453
|
-
"""List tasks with optional filtering."""
|
454
|
-
query = ListTasksQuery(self.http_client, filter_params, organization_ids)
|
455
|
-
return query.execute()
|
456
|
-
|
457
|
-
def get(self, task_id: str) -> Task:
|
458
|
-
"""Get a specific task by ID."""
|
459
|
-
query = GetTaskQuery(self.http_client, task_id)
|
460
|
-
return query.execute()
|
461
|
-
|
462
|
-
def get_assignments(self, task_id: str) -> List[TaskAssignment]:
|
463
|
-
"""Get task assignments for a specific task."""
|
464
|
-
query = GetTaskAssignmentsQuery(self.http_client, task_id)
|
465
|
-
return query.execute()
|
466
|
-
|
467
|
-
# COMMANDS (Write operations)
|
468
|
-
def cancel(self, task_id: str) -> Dict[str, Any]:
|
469
|
-
"""Cancel a task."""
|
470
|
-
command = CancelTaskCommand(self.http_client, task_id)
|
471
|
-
return command.execute()
|
472
|
-
|
473
|
-
def cancel_assignment(self, assignment_id: str) -> Dict[str, Any]:
|
474
|
-
"""Cancel a task assignment."""
|
475
|
-
command = CancelTaskAssignmentCommand(self.http_client, assignment_id)
|
476
|
-
return command.execute()
|
477
|
-
|
478
|
-
def delete_assignment(self, assignment_id: str) -> Dict[str, Any]:
|
479
|
-
"""Delete a task assignment."""
|
480
|
-
command = DeleteTaskAssignmentCommand(self.http_client, assignment_id)
|
481
|
-
return command.execute()
|
482
|
-
|
483
|
-
def delete(self, task_id: str) -> Dict[str, Any]:
|
484
|
-
"""Delete a task."""
|
485
|
-
command = DeleteTaskCommand(self.http_client, task_id)
|
486
|
-
return command.execute()
|
487
|
-
|
488
|
-
|
489
|
-
class AcquisitionsAPI:
|
490
|
-
"""Acquisitions API with CQRS pattern - separated queries and commands."""
|
491
|
-
|
492
|
-
def __init__(self, http_client: HTTPClient):
|
493
|
-
self.http_client = http_client
|
494
|
-
|
495
|
-
# QUERIES (Read operations)
|
496
|
-
def list_profiles(
|
497
|
-
self,
|
498
|
-
filter_params: Optional[AcquisitionFilter] = None,
|
499
|
-
organization_ids: Optional[List[int]] = None,
|
500
|
-
all_organizations: bool = False
|
501
|
-
) -> List[AcquisitionProfile]:
|
502
|
-
"""List acquisition profiles with optional filtering."""
|
503
|
-
query = ListAcquisitionProfilesQuery(self.http_client, filter_params, organization_ids, all_organizations)
|
504
|
-
return query.execute()
|
505
|
-
|
506
|
-
def get_profile(self, profile_id: str) -> AcquisitionProfileDetails:
|
507
|
-
"""Get a specific acquisition profile by ID."""
|
508
|
-
query = GetAcquisitionProfileQuery(self.http_client, profile_id)
|
509
|
-
return query.execute()
|
510
|
-
|
511
|
-
# COMMANDS (Write operations)
|
512
|
-
def acquire(self, request) -> Dict[str, Any]:
|
513
|
-
"""Assign evidence acquisition task by filter."""
|
514
|
-
command = CreateAcquisitionCommand(self.http_client, request)
|
515
|
-
return command.execute()
|
516
|
-
|
517
|
-
def acquire_image(self, request) -> Dict[str, Any]:
|
518
|
-
"""Assign image acquisition task by filter."""
|
519
|
-
command = CreateImageAcquisitionCommand(self.http_client, request)
|
520
|
-
return command.execute()
|
521
|
-
|
522
|
-
def create_profile(self, request: CreateAcquisitionProfileRequest) -> Dict[str, Any]:
|
523
|
-
"""Create acquisition profile."""
|
524
|
-
command = CreateAcquisitionProfileCommand(self.http_client, request)
|
525
|
-
return command.execute()
|
526
|
-
|
527
|
-
# Legacy method aliases for backwards compatibility
|
528
|
-
def assign_task(self, request: AcquisitionTaskRequest) -> List[Dict[str, Any]]:
|
529
|
-
"""Legacy alias for acquire method."""
|
530
|
-
command = AssignAcquisitionTaskCommand(self.http_client, request)
|
531
|
-
return command.execute()
|
532
|
-
|
533
|
-
def assign_image_task(self, request: ImageAcquisitionTaskRequest) -> List[Dict[str, Any]]:
|
534
|
-
"""Legacy alias for acquire_image method."""
|
535
|
-
command = AssignImageAcquisitionTaskCommand(self.http_client, request)
|
536
|
-
return command.execute()
|
537
|
-
|
538
|
-
|
539
|
-
class PoliciesAPI:
|
540
|
-
"""Policies API with CQRS pattern - separated queries and commands."""
|
541
|
-
|
542
|
-
def __init__(self, http_client: HTTPClient):
|
543
|
-
self.http_client = http_client
|
544
|
-
|
545
|
-
# QUERIES (Read operations)
|
546
|
-
def list(self, filter_params: Optional[PolicyFilter] = None, organization_ids: Optional[List[int]] = None) -> List[Policy]:
|
547
|
-
"""List policies with optional filtering."""
|
548
|
-
from .queries.policies import ListPoliciesQuery
|
549
|
-
query = ListPoliciesQuery(self.http_client, filter_params, organization_ids)
|
550
|
-
result = query.execute()
|
551
|
-
# Extract the policies list from the paginated response
|
552
|
-
if hasattr(result, 'entities'):
|
553
|
-
return result.entities
|
554
|
-
elif isinstance(result, list):
|
555
|
-
return result
|
556
|
-
else:
|
557
|
-
return []
|
558
|
-
|
559
|
-
def get(self, policy_id: str) -> Policy:
|
560
|
-
"""Get a specific policy by ID."""
|
561
|
-
from .queries.policies import GetPolicyQuery
|
562
|
-
query = GetPolicyQuery(self.http_client, policy_id)
|
563
|
-
return query.execute()
|
564
|
-
|
565
|
-
def get_assignments(self, policy_id: str) -> List[PolicyAssignment]:
|
566
|
-
"""Get policy assignments."""
|
567
|
-
from .queries.policies import GetPolicyAssignmentsQuery
|
568
|
-
query = GetPolicyAssignmentsQuery(self.http_client, policy_id)
|
569
|
-
return query.execute()
|
570
|
-
|
571
|
-
def get_executions(self, policy_id: str) -> List[PolicyExecution]:
|
572
|
-
"""Get policy executions."""
|
573
|
-
from .queries.policies import GetPolicyExecutionsQuery
|
574
|
-
query = GetPolicyExecutionsQuery(self.http_client, policy_id)
|
575
|
-
return query.execute()
|
576
|
-
|
577
|
-
def get_match_stats(self, filter_params: Optional[Dict[str, Any]] = None, organization_ids: Optional[List[int]] = None) -> Dict[str, Any]:
|
578
|
-
"""Get policy match statistics with filtering.
|
579
|
-
|
580
|
-
Args:
|
581
|
-
filter_params: Optional filter parameters (name, platform, tags, etc.)
|
582
|
-
organization_ids: List of organization IDs (defaults to [0])
|
583
|
-
|
584
|
-
Returns:
|
585
|
-
Dictionary containing policy match statistics
|
586
|
-
"""
|
587
|
-
try:
|
588
|
-
# Fix API-001: Ensure organizationIds are provided to prevent errors
|
589
|
-
if organization_ids is None or len(organization_ids) == 0:
|
590
|
-
organization_ids = [0] # Default to organization 0
|
591
|
-
|
592
|
-
# Build payload with default filter structure
|
593
|
-
payload = {
|
594
|
-
"name": "",
|
595
|
-
"searchTerm": "",
|
596
|
-
"ipAddress": "",
|
597
|
-
"groupId": "",
|
598
|
-
"groupFullPath": "",
|
599
|
-
"managedStatus": [],
|
600
|
-
"isolationStatus": [],
|
601
|
-
"platform": [],
|
602
|
-
"issue": "",
|
603
|
-
"onlineStatus": [],
|
604
|
-
"tags": [],
|
605
|
-
"version": "",
|
606
|
-
"policy": "",
|
607
|
-
"includedEndpointIds": [],
|
608
|
-
"excludedEndpointIds": [],
|
609
|
-
"organizationIds": organization_ids
|
610
|
-
}
|
611
|
-
|
612
|
-
# Apply custom filter parameters if provided
|
613
|
-
if filter_params:
|
614
|
-
for key, value in filter_params.items():
|
615
|
-
if key in payload:
|
616
|
-
payload[key] = value
|
617
|
-
|
618
|
-
# Use correct API endpoint: POST policies/match-stats (not GET policies/stats)
|
619
|
-
response = self.http_client.post("policies/match-stats", json_data=payload)
|
620
|
-
return response
|
621
|
-
|
622
|
-
except Exception as e:
|
623
|
-
# Return a simulated response for testing
|
624
|
-
return {
|
625
|
-
"success": False,
|
626
|
-
"error": str(e),
|
627
|
-
"result": []
|
628
|
-
}
|
629
|
-
|
630
|
-
# COMMANDS (Write operations)
|
631
|
-
def create(self, policy_data: Union[CreatePolicyRequest, Dict[str, Any]]) -> Policy:
|
632
|
-
"""Create a new policy."""
|
633
|
-
from .commands.policies import CreatePolicyCommand
|
634
|
-
command = CreatePolicyCommand(self.http_client, policy_data)
|
635
|
-
return command.execute()
|
636
|
-
|
637
|
-
def update(self, policy_id: str, update_data: Union[UpdatePolicyRequest, Dict[str, Any]]) -> Policy:
|
638
|
-
"""Update an existing policy."""
|
639
|
-
from .commands.policies import UpdatePolicyCommand
|
640
|
-
command = UpdatePolicyCommand(self.http_client, policy_id, update_data)
|
641
|
-
return command.execute()
|
642
|
-
|
643
|
-
def delete(self, policy_id: str) -> Dict[str, Any]:
|
644
|
-
"""Delete a policy."""
|
645
|
-
from .commands.policies import DeletePolicyCommand
|
646
|
-
command = DeletePolicyCommand(self.http_client, policy_id)
|
647
|
-
return command.execute()
|
648
|
-
|
649
|
-
def activate(self, policy_id: str) -> Policy:
|
650
|
-
"""Activate a policy."""
|
651
|
-
from .commands.policies import ActivatePolicyCommand
|
652
|
-
command = ActivatePolicyCommand(self.http_client, policy_id)
|
653
|
-
return command.execute()
|
654
|
-
|
655
|
-
def deactivate(self, policy_id: str) -> Policy:
|
656
|
-
"""Deactivate a policy."""
|
657
|
-
from .commands.policies import DeactivatePolicyCommand
|
658
|
-
command = DeactivatePolicyCommand(self.http_client, policy_id)
|
659
|
-
return command.execute()
|
660
|
-
|
661
|
-
def assign(self, assignment_data: Union[AssignPolicyRequest, Dict[str, Any]]) -> Dict[str, Any]:
|
662
|
-
"""Assign policy to endpoints."""
|
663
|
-
from .commands.policies import AssignPolicyCommand
|
664
|
-
command = AssignPolicyCommand(self.http_client, assignment_data)
|
665
|
-
return command.execute()
|
666
|
-
|
667
|
-
def unassign(self, policy_id: str, endpoint_ids: List[str]) -> Dict[str, Any]:
|
668
|
-
"""Unassign policy from endpoints."""
|
669
|
-
from .commands.policies import UnassignPolicyCommand
|
670
|
-
command = UnassignPolicyCommand(self.http_client, policy_id, endpoint_ids)
|
671
|
-
return command.execute()
|
672
|
-
|
673
|
-
def execute(self, policy_id: str, endpoint_ids: Optional[List[str]] = None) -> Dict[str, Any]:
|
674
|
-
"""Execute a policy on assigned endpoints."""
|
675
|
-
from .commands.policies import ExecutePolicyCommand
|
676
|
-
command = ExecutePolicyCommand(self.http_client, policy_id, endpoint_ids)
|
677
|
-
return command.execute()
|
678
|
-
|
679
|
-
def update_priorities(self, policy_ids: List[str], organization_ids: Optional[List[int]] = None) -> Dict[str, Any]:
|
680
|
-
"""Update policy priorities.
|
681
|
-
|
682
|
-
Args:
|
683
|
-
policy_ids: List of policy IDs in priority order (System policy must be first)
|
684
|
-
organization_ids: List of organization IDs (defaults to [0])
|
685
|
-
|
686
|
-
Returns:
|
687
|
-
Response dictionary with success status
|
688
|
-
"""
|
689
|
-
try:
|
690
|
-
# Fix API-001: Ensure organizationIds are provided to prevent issues
|
691
|
-
if organization_ids is None or len(organization_ids) == 0:
|
692
|
-
organization_ids = [0] # Default to organization 0
|
693
|
-
|
694
|
-
# Use correct API parameter names according to specification
|
695
|
-
payload = {
|
696
|
-
"ids": policy_ids, # API expects 'ids', not 'policyIds'
|
697
|
-
"organizationIds": organization_ids # Required parameter
|
698
|
-
}
|
699
|
-
|
700
|
-
response = self.http_client.put("policies/priorities", json_data=payload)
|
701
|
-
return response
|
702
|
-
except Exception as e:
|
703
|
-
# Return a simulated response for testing
|
704
|
-
return {
|
705
|
-
"success": False,
|
706
|
-
"error": str(e),
|
707
|
-
"updated_policies": []
|
708
|
-
}
|
709
|
-
|
710
|
-
|
711
|
-
class OrganizationsAPI:
|
712
|
-
"""Organizations API with CQRS pattern - separated queries and commands."""
|
713
|
-
|
714
|
-
def __init__(self, http_client: HTTPClient):
|
715
|
-
self.http_client = http_client
|
716
|
-
|
717
|
-
# QUERIES (Read operations)
|
718
|
-
def list(self, page: int = 1, page_size: int = 10,
|
719
|
-
sort_by: str = "name", order: str = "asc") -> OrganizationsPaginatedResponse:
|
720
|
-
"""List organizations with pagination and sorting."""
|
721
|
-
from .queries.organizations import ListOrganizationsQuery
|
722
|
-
query = ListOrganizationsQuery(self.http_client, page, page_size, sort_by, order, None)
|
723
|
-
return query.execute()
|
724
|
-
|
725
|
-
def get(self, organization_id: str) -> Organization:
|
726
|
-
"""Get organization by ID."""
|
727
|
-
from .queries.organizations import GetOrganizationQuery
|
728
|
-
query = GetOrganizationQuery(self.http_client, organization_id)
|
729
|
-
return query.execute()
|
730
|
-
|
731
|
-
def get_users(self, organization_id: str, page: int = 1, page_size: int = 10) -> OrganizationUsersPaginatedResponse:
|
732
|
-
"""Get users in organization."""
|
733
|
-
from .queries.organizations import GetOrganizationUsersQuery
|
734
|
-
query = GetOrganizationUsersQuery(self.http_client, organization_id, page, page_size)
|
735
|
-
return query.execute()
|
736
|
-
|
737
|
-
def check_name(self, name: str) -> bool:
|
738
|
-
"""Check if organization name exists."""
|
739
|
-
try:
|
740
|
-
params = {"name": name}
|
741
|
-
response = self.http_client.get("organizations/check", params=params)
|
742
|
-
return response.get("result", False)
|
743
|
-
except Exception:
|
744
|
-
return False
|
745
|
-
|
746
|
-
def get_shareable_deployment_info(self, deployment_token: str) -> Dict[str, Any]:
|
747
|
-
"""Get shareable deployment information by token."""
|
748
|
-
try:
|
749
|
-
response = self.http_client.get(f"organizations/shareable-deployment-info/{deployment_token}")
|
750
|
-
|
751
|
-
if response.get("success"):
|
752
|
-
return response.get("result", {})
|
753
|
-
else:
|
754
|
-
# Return error information
|
755
|
-
return {
|
756
|
-
"error": True,
|
757
|
-
"errors": response.get("errors", []),
|
758
|
-
"statusCode": response.get("statusCode", 500)
|
759
|
-
}
|
760
|
-
except Exception as e:
|
761
|
-
return {
|
762
|
-
"error": True,
|
763
|
-
"errors": [str(e)],
|
764
|
-
"statusCode": 500
|
765
|
-
}
|
766
|
-
|
767
|
-
# COMMANDS (Write operations)
|
768
|
-
def create(self, request: CreateOrganizationRequest) -> Organization:
|
769
|
-
"""Create organization."""
|
770
|
-
from .commands.organizations import CreateOrganizationCommand
|
771
|
-
command = CreateOrganizationCommand(self.http_client, request)
|
772
|
-
return command.execute()
|
773
|
-
|
774
|
-
def update(self, organization_id: str, request: UpdateOrganizationRequest) -> Organization:
|
775
|
-
"""Update organization."""
|
776
|
-
from .commands.organizations import UpdateOrganizationCommand
|
777
|
-
command = UpdateOrganizationCommand(self.http_client, organization_id, request)
|
778
|
-
return command.execute()
|
779
|
-
|
780
|
-
def add_user(self, organization_id: str, request: AddUserToOrganizationRequest) -> OrganizationUser:
|
781
|
-
"""Add user to organization."""
|
782
|
-
from .commands.organizations import AddUserToOrganizationCommand
|
783
|
-
command = AddUserToOrganizationCommand(self.http_client, organization_id, request)
|
784
|
-
return command.execute()
|
785
|
-
|
786
|
-
def assign_users(self, organization_id: str, user_ids: List[str]) -> bool:
|
787
|
-
"""Assign users to organization using the /assign-users endpoint."""
|
788
|
-
from .commands.organizations import AssignUsersToOrganizationCommand
|
789
|
-
from .models.organizations import AssignUsersToOrganizationRequest
|
790
|
-
|
791
|
-
# Create the proper request object with correct field name
|
792
|
-
request = AssignUsersToOrganizationRequest(userIds=user_ids)
|
793
|
-
command = AssignUsersToOrganizationCommand(self.http_client, organization_id, request)
|
794
|
-
return command.execute()
|
795
|
-
|
796
|
-
def remove_user(self, organization_id: str, user_id: str) -> Dict[str, Any]:
|
797
|
-
"""Remove user from organization using the /remove-user endpoint."""
|
798
|
-
from .commands.organizations import RemoveUserFromOrganizationCommand
|
799
|
-
command = RemoveUserFromOrganizationCommand(self.http_client, organization_id, user_id)
|
800
|
-
return command.execute()
|
801
|
-
|
802
|
-
def update_settings(self, organization_id: str, settings: Dict[str, Any]) -> OrganizationSettings:
|
803
|
-
"""Update organization settings."""
|
804
|
-
from .commands.organizations import UpdateOrganizationSettingsCommand
|
805
|
-
command = UpdateOrganizationSettingsCommand(self.http_client, organization_id, settings)
|
806
|
-
return command.execute()
|
807
|
-
|
808
|
-
def update_shareable_deployment_settings(self, organization_id: int, status: bool) -> Dict[str, Any]:
|
809
|
-
"""Update organization shareable deployment settings."""
|
810
|
-
try:
|
811
|
-
# Prepare the payload according to API specification
|
812
|
-
payload = {"status": status}
|
813
|
-
|
814
|
-
# Make the API call
|
815
|
-
response = self.http_client.post(f"organizations/{organization_id}/shareable-deployment", json_data=payload)
|
816
|
-
return response
|
817
|
-
|
818
|
-
except Exception as e:
|
819
|
-
# Check if it's a 409 conflict (expected behavior when setting to same state)
|
820
|
-
error_msg = str(e)
|
821
|
-
if "409" in error_msg or "already" in error_msg.lower():
|
822
|
-
# Return success for 409 conflicts (expected behavior)
|
823
|
-
return {
|
824
|
-
"success": True,
|
825
|
-
"result": None,
|
826
|
-
"statusCode": 409,
|
827
|
-
"message": "Shareable deployment setting already in desired state"
|
828
|
-
}
|
829
|
-
|
830
|
-
# Return error response format matching API
|
831
|
-
return {
|
832
|
-
"success": False,
|
833
|
-
"result": None,
|
834
|
-
"statusCode": 500,
|
835
|
-
"errors": [str(e)]
|
836
|
-
}
|
837
|
-
|
838
|
-
def update_deployment_token(self, organization_id: int, deployment_token: str) -> Dict[str, Any]:
|
839
|
-
"""Update organization deployment token."""
|
840
|
-
try:
|
841
|
-
# Prepare the payload according to API specification
|
842
|
-
payload = {"deploymentToken": deployment_token}
|
843
|
-
|
844
|
-
# Make the API call
|
845
|
-
response = self.http_client.post(f"organizations/{organization_id}/deployment-token", json_data=payload)
|
846
|
-
return response
|
847
|
-
|
848
|
-
except Exception as e:
|
849
|
-
# Check if it's a 409 conflict (expected behavior when setting to same token)
|
850
|
-
error_msg = str(e)
|
851
|
-
if "409" in error_msg or "same token" in error_msg.lower() or "cannot be updated with same" in error_msg.lower():
|
852
|
-
# Return success for 409 conflicts (expected behavior)
|
853
|
-
return {
|
854
|
-
"success": True,
|
855
|
-
"result": None,
|
856
|
-
"statusCode": 409,
|
857
|
-
"message": "Deployment token already set to this value"
|
858
|
-
}
|
859
|
-
|
860
|
-
# Return error response format matching API
|
861
|
-
return {
|
862
|
-
"success": False,
|
863
|
-
"result": None,
|
864
|
-
"statusCode": 500,
|
865
|
-
"errors": [str(e)]
|
866
|
-
}
|
867
|
-
|
868
|
-
def delete(self, organization_id: int) -> Dict[str, Any]:
|
869
|
-
"""Delete organization by ID."""
|
870
|
-
try:
|
871
|
-
# Make the API call
|
872
|
-
response = self.http_client.delete(f"organizations/{organization_id}")
|
873
|
-
return response
|
874
|
-
|
875
|
-
except Exception as e:
|
876
|
-
# Return error response format matching API
|
877
|
-
return {
|
878
|
-
"success": False,
|
879
|
-
"result": None,
|
880
|
-
"statusCode": 500,
|
881
|
-
"errors": [str(e)]
|
882
|
-
}
|
883
|
-
|
884
|
-
def add_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
885
|
-
"""Add tags to organization."""
|
886
|
-
try:
|
887
|
-
# Prepare the payload according to API specification
|
888
|
-
payload = {"tags": tags}
|
889
|
-
|
890
|
-
# Make the API call using PATCH method
|
891
|
-
response = self.http_client.patch(f"organizations/{organization_id}/tags", json_data=payload)
|
892
|
-
return response
|
893
|
-
|
894
|
-
except Exception as e:
|
895
|
-
# Return error response format matching API
|
896
|
-
return {
|
897
|
-
"success": False,
|
898
|
-
"result": None,
|
899
|
-
"statusCode": 500,
|
900
|
-
"errors": [str(e)]
|
901
|
-
}
|
902
|
-
|
903
|
-
def delete_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
904
|
-
"""Delete tags from organization."""
|
905
|
-
try:
|
906
|
-
# Prepare the payload according to API specification
|
907
|
-
payload = {"tags": tags}
|
908
|
-
|
909
|
-
# Make the API call using DELETE method
|
910
|
-
response = self.http_client.delete(f"organizations/{organization_id}/tags", json_data=payload)
|
911
|
-
return response
|
912
|
-
|
913
|
-
except Exception as e:
|
914
|
-
# Return error response format matching API
|
915
|
-
return {
|
916
|
-
"success": False,
|
917
|
-
"result": None,
|
918
|
-
"statusCode": 500,
|
919
|
-
"errors": [str(e)]
|
920
|
-
}
|
921
|
-
|
922
|
-
def remove_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
923
|
-
"""Remove tags from organization (alias for delete_tags)."""
|
924
|
-
return self.delete_tags(organization_id, tags)
|
925
|
-
|
926
|
-
|
927
|
-
class TriageAPI:
|
928
|
-
"""Triage API with CQRS pattern - separated queries and commands."""
|
929
|
-
|
930
|
-
def __init__(self, http_client: HTTPClient):
|
931
|
-
self.http_client = http_client
|
932
|
-
|
933
|
-
# QUERIES (Read operations)
|
934
|
-
def list_rules(self, filter_params: Optional[TriageFilter] = None, organization_ids: Optional[List[int]] = None) -> List[TriageRule]:
|
935
|
-
"""List triage rules with optional filtering."""
|
936
|
-
from .queries.triage import ListTriageRulesQuery
|
937
|
-
query = ListTriageRulesQuery(self.http_client, filter_params, organization_ids)
|
938
|
-
return query.execute()
|
939
|
-
|
940
|
-
def get_rule(self, rule_id: str) -> TriageRule:
|
941
|
-
"""Get a specific triage rule by ID."""
|
942
|
-
from .queries.triage import GetTriageRuleQuery
|
943
|
-
query = GetTriageRuleQuery(self.http_client, rule_id)
|
944
|
-
return query.execute()
|
945
|
-
|
946
|
-
def get_rule_by_id(self, rule_id: str) -> TriageRule:
|
947
|
-
"""Get a specific triage rule by ID - alias for get_rule."""
|
948
|
-
return self.get_rule(rule_id)
|
949
|
-
|
950
|
-
def list_tags(self, organization_id: Optional[int] = None) -> List[TriageTag]:
|
951
|
-
"""List triage tags."""
|
952
|
-
from .queries.triage import ListTriageTagsQuery
|
953
|
-
query = ListTriageTagsQuery(self.http_client, organization_id)
|
954
|
-
return query.execute()
|
955
|
-
|
956
|
-
def validate_rule(self, rule_content: str, engine: str = "yara") -> Dict[str, Any]:
|
957
|
-
"""Validate triage rule syntax."""
|
958
|
-
try:
|
959
|
-
# Prepare validation data
|
960
|
-
validation_data = {
|
961
|
-
"rule": rule_content,
|
962
|
-
"engine": engine
|
963
|
-
}
|
964
|
-
|
965
|
-
# Call the API validation endpoint
|
966
|
-
response = self.http_client.post("triages/rules/validate", json_data=validation_data)
|
967
|
-
return response
|
968
|
-
|
969
|
-
except Exception as e:
|
970
|
-
# Return error response format matching API
|
971
|
-
return {
|
972
|
-
"success": False,
|
973
|
-
"result": None,
|
974
|
-
"statusCode": 500,
|
975
|
-
"errors": [str(e)]
|
976
|
-
}
|
977
|
-
|
978
|
-
# COMMANDS (Write operations)
|
979
|
-
def create_rule(self, request: Union[CreateTriageRuleRequest, Dict[str, Any]]) -> TriageRule:
|
980
|
-
"""Create a new triage rule."""
|
981
|
-
from .commands.triage import CreateTriageRuleCommand
|
982
|
-
command = CreateTriageRuleCommand(self.http_client, request)
|
983
|
-
return command.execute()
|
984
|
-
|
985
|
-
def update_rule(self, rule_id_or_data: Union[str, Dict[str, Any]], request: Optional[Union[UpdateTriageRuleRequest, Dict[str, Any]]] = None) -> TriageRule:
|
986
|
-
"""Update an existing triage rule."""
|
987
|
-
from .commands.triage import UpdateTriageRuleCommand
|
988
|
-
|
989
|
-
# Handle both signatures: update_rule(rule_id, request) and update_rule(data_dict)
|
990
|
-
if isinstance(rule_id_or_data, str) and request is not None:
|
991
|
-
# Traditional signature: update_rule(rule_id, request)
|
992
|
-
command = UpdateTriageRuleCommand(self.http_client, rule_id_or_data, request)
|
993
|
-
elif isinstance(rule_id_or_data, dict):
|
994
|
-
# Dict signature: update_rule(data_dict) where data_dict contains 'id'
|
995
|
-
rule_id = rule_id_or_data.get('id')
|
996
|
-
if not rule_id:
|
997
|
-
raise ValueError("Rule ID must be provided in data dict or as separate parameter")
|
998
|
-
command = UpdateTriageRuleCommand(self.http_client, rule_id, rule_id_or_data)
|
999
|
-
else:
|
1000
|
-
raise ValueError("Invalid arguments for update_rule")
|
1001
|
-
|
1002
|
-
return command.execute()
|
1003
|
-
|
1004
|
-
def delete_rule(self, rule_id: str) -> Dict[str, Any]:
|
1005
|
-
"""Delete a triage rule."""
|
1006
|
-
from .commands.triage import DeleteTriageRuleCommand
|
1007
|
-
command = DeleteTriageRuleCommand(self.http_client, rule_id)
|
1008
|
-
return command.execute()
|
1009
|
-
|
1010
|
-
def create_tag(self, request: Union[CreateTriageTagRequest, Dict[str, Any]]) -> TriageTag:
|
1011
|
-
"""Create a new triage tag."""
|
1012
|
-
from .commands.triage import CreateTriageTagCommand
|
1013
|
-
command = CreateTriageTagCommand(self.http_client, request)
|
1014
|
-
return command.execute()
|
1015
|
-
|
1016
|
-
def delete_tag(self, tag_id: str) -> Dict[str, Any]:
|
1017
|
-
"""Delete a triage tag."""
|
1018
|
-
from .commands.triage import DeleteTriageTagCommand
|
1019
|
-
command = DeleteTriageTagCommand(self.http_client, tag_id)
|
1020
|
-
return command.execute()
|
1021
|
-
|
1022
|
-
def assign_task(self, task_data: Dict[str, Any]) -> Dict[str, Any]:
|
1023
|
-
"""Assign a triage task to endpoints."""
|
1024
|
-
try:
|
1025
|
-
# Call the correct API endpoint for triage task assignment
|
1026
|
-
response = self.http_client.post("triages/triage", json_data=task_data)
|
1027
|
-
return response
|
1028
|
-
except Exception as e:
|
1029
|
-
# Import specific exception types
|
1030
|
-
from .exceptions import AuthorizationError, AuthenticationError, ValidationError, AIRAPIError
|
1031
|
-
|
1032
|
-
# Handle specific API errors and preserve status codes
|
1033
|
-
if isinstance(e, (AuthorizationError, AuthenticationError, ValidationError, AIRAPIError)):
|
1034
|
-
# Return the actual API error response if available
|
1035
|
-
if hasattr(e, 'response_data') and e.response_data:
|
1036
|
-
return e.response_data
|
1037
|
-
else:
|
1038
|
-
# Create response matching API format with actual status code
|
1039
|
-
return {
|
1040
|
-
"success": False,
|
1041
|
-
"result": None,
|
1042
|
-
"statusCode": getattr(e, 'status_code', 500),
|
1043
|
-
"errors": [str(e)]
|
1044
|
-
}
|
1045
|
-
else:
|
1046
|
-
# For unexpected errors, use 500
|
1047
|
-
return {
|
1048
|
-
"success": False,
|
1049
|
-
"result": None,
|
1050
|
-
"statusCode": 500,
|
1051
|
-
"errors": [str(e)]
|
1052
|
-
}
|
1053
|
-
|
1054
|
-
|
1055
|
-
class AuditAPI:
|
1056
|
-
"""Audit logs API with enhanced filtering capabilities."""
|
1057
|
-
|
1058
|
-
def __init__(self, http_client: HTTPClient):
|
1059
|
-
self.http_client = http_client
|
1060
|
-
|
1061
|
-
# QUERIES (Read operations)
|
1062
|
-
def list_logs(self, filter_params: Optional[AuditLogsFilter] = None, organization_ids: Optional[int] = None) -> List[AuditLog]:
|
1063
|
-
"""List audit logs with enhanced filtering - UPDATED for new POST-based API."""
|
1064
|
-
from .queries.audit import ListAuditLogsQuery
|
1065
|
-
query = ListAuditLogsQuery(self.http_client, filter_params, organization_ids)
|
1066
|
-
return query.execute()
|
1067
|
-
|
1068
|
-
def get_log(self, log_id: str) -> AuditLog:
|
1069
|
-
"""Get audit log by ID."""
|
1070
|
-
from .queries.audit import GetAuditLogQuery
|
1071
|
-
query = GetAuditLogQuery(self.http_client, log_id)
|
1072
|
-
return query.execute()
|
1073
|
-
|
1074
|
-
def get_summary(self, organization_id: int, start_date: datetime, end_date: datetime) -> AuditSummary:
|
1075
|
-
"""Get audit summary for a date range."""
|
1076
|
-
from .queries.audit import GetAuditSummaryQuery
|
1077
|
-
query = GetAuditSummaryQuery(self.http_client, organization_id, start_date, end_date)
|
1078
|
-
return query.execute()
|
1079
|
-
|
1080
|
-
def get_user_activity(self, organization_id: int, start_date: datetime, end_date: datetime, user_id: Optional[str] = None) -> List[AuditUserActivity]:
|
1081
|
-
"""Get user activity audit logs."""
|
1082
|
-
from .queries.audit import GetUserActivityQuery
|
1083
|
-
query = GetUserActivityQuery(self.http_client, organization_id, start_date, end_date, user_id)
|
1084
|
-
return query.execute()
|
1085
|
-
|
1086
|
-
def get_system_events(self, organization_id: int, start_date: datetime, end_date: datetime, severity: Optional[AuditLevel] = None) -> List[AuditSystemEvent]:
|
1087
|
-
"""Get system events audit logs."""
|
1088
|
-
from .queries.audit import GetSystemEventsQuery
|
1089
|
-
query = GetSystemEventsQuery(self.http_client, organization_id, start_date, end_date, severity)
|
1090
|
-
return query.execute()
|
1091
|
-
|
1092
|
-
def get_retention_policy(self, organization_id: int) -> AuditRetentionPolicy:
|
1093
|
-
"""Get audit retention policy."""
|
1094
|
-
from .queries.audit import GetAuditRetentionPolicyQuery
|
1095
|
-
query = GetAuditRetentionPolicyQuery(self.http_client, organization_id)
|
1096
|
-
return query.execute()
|
1097
|
-
|
1098
|
-
def export_logs(self, filter_params: Optional[AuditLogsFilter] = None, format: str = "json", organization_ids: Optional[int] = None) -> Dict[str, Any]:
|
1099
|
-
"""Export audit logs with enhanced filtering - UPDATED for new API."""
|
1100
|
-
from .queries.audit import ExportAuditLogsQuery
|
1101
|
-
query = ExportAuditLogsQuery(self.http_client, filter_params, format, organization_ids)
|
1102
|
-
return query.execute()
|
1103
|
-
|
1104
|
-
|
1105
|
-
class BaselineAPI:
|
1106
|
-
"""Baseline API with CQRS pattern - separated queries and commands."""
|
1107
|
-
|
1108
|
-
def __init__(self, http_client: HTTPClient):
|
1109
|
-
self.http_client = http_client
|
1110
|
-
|
1111
|
-
# QUERIES (Read operations)
|
1112
|
-
def list(self, filter_params: Optional[BaselineFilter] = None, organization_ids: Optional[List[int]] = None) -> List[Baseline]:
|
1113
|
-
"""List baselines with optional filtering."""
|
1114
|
-
from .queries.baseline import ListBaselinesQuery
|
1115
|
-
query = ListBaselinesQuery(self.http_client, filter_params, organization_ids)
|
1116
|
-
return query.execute()
|
1117
|
-
|
1118
|
-
def get(self, baseline_id: str) -> Baseline:
|
1119
|
-
"""Get a specific baseline by ID."""
|
1120
|
-
from .queries.baseline import GetBaselineQuery
|
1121
|
-
query = GetBaselineQuery(self.http_client, baseline_id)
|
1122
|
-
return query.execute()
|
1123
|
-
|
1124
|
-
def get_comparisons(self, baseline_id: str) -> List[BaselineComparison]:
|
1125
|
-
"""Get baseline comparisons."""
|
1126
|
-
from .queries.baseline import GetBaselineComparisonsQuery
|
1127
|
-
query = GetBaselineComparisonsQuery(self.http_client, baseline_id)
|
1128
|
-
return query.execute()
|
1129
|
-
|
1130
|
-
def get_comparison(self, comparison_id: str) -> BaselineComparison:
|
1131
|
-
"""Get a specific baseline comparison by ID."""
|
1132
|
-
from .queries.baseline import GetBaselineComparisonQuery
|
1133
|
-
query = GetBaselineComparisonQuery(self.http_client, comparison_id)
|
1134
|
-
return query.execute()
|
1135
|
-
|
1136
|
-
def list_profiles(self, organization_ids: Optional[List[int]] = None) -> List[BaselineProfile]:
|
1137
|
-
"""List baseline profiles."""
|
1138
|
-
from .queries.baseline import ListBaselineProfilesQuery
|
1139
|
-
query = ListBaselineProfilesQuery(self.http_client, organization_ids)
|
1140
|
-
return query.execute()
|
1141
|
-
|
1142
|
-
def get_profile(self, profile_id: str) -> BaselineProfile:
|
1143
|
-
"""Get a specific baseline profile by ID."""
|
1144
|
-
from .queries.baseline import GetBaselineProfileQuery
|
1145
|
-
query = GetBaselineProfileQuery(self.http_client, profile_id)
|
1146
|
-
return query.execute()
|
1147
|
-
|
1148
|
-
def get_schedules(self, baseline_id: Optional[str] = None, organization_ids: Optional[List[int]] = None) -> List[BaselineSchedule]:
|
1149
|
-
"""Get baseline schedules."""
|
1150
|
-
from .queries.baseline import GetBaselineSchedulesQuery
|
1151
|
-
query = GetBaselineSchedulesQuery(self.http_client, baseline_id, organization_ids)
|
1152
|
-
return query.execute()
|
1153
|
-
|
1154
|
-
# COMMANDS (Write operations)
|
1155
|
-
def create(self, request: CreateBaselineRequest) -> Baseline:
|
1156
|
-
"""Create a new baseline."""
|
1157
|
-
from .commands.baseline import CreateBaselineCommand
|
1158
|
-
command = CreateBaselineCommand(self.http_client, request)
|
1159
|
-
return command.execute()
|
1160
|
-
|
1161
|
-
def update(self, baseline_id: str, request: UpdateBaselineRequest) -> Baseline:
|
1162
|
-
"""Update an existing baseline."""
|
1163
|
-
from .commands.baseline import UpdateBaselineCommand
|
1164
|
-
command = UpdateBaselineCommand(self.http_client, baseline_id, request)
|
1165
|
-
return command.execute()
|
1166
|
-
|
1167
|
-
def delete(self, baseline_id: str) -> Dict[str, Any]:
|
1168
|
-
"""Delete a baseline."""
|
1169
|
-
from .commands.baseline import DeleteBaselineCommand
|
1170
|
-
command = DeleteBaselineCommand(self.http_client, baseline_id)
|
1171
|
-
return command.execute()
|
1172
|
-
|
1173
|
-
def compare(self, request: CompareBaselineRequest) -> BaselineComparison:
|
1174
|
-
"""Run a baseline comparison."""
|
1175
|
-
from .commands.baseline import CompareBaselineCommand
|
1176
|
-
command = CompareBaselineCommand(self.http_client, request)
|
1177
|
-
return command.execute()
|
1178
|
-
|
1179
|
-
def refresh(self, baseline_id: str) -> Baseline:
|
1180
|
-
"""Refresh/rebuild a baseline."""
|
1181
|
-
from .commands.baseline import RefreshBaselineCommand
|
1182
|
-
command = RefreshBaselineCommand(self.http_client, baseline_id)
|
1183
|
-
return command.execute()
|
1184
|
-
|
1185
|
-
def create_profile(self, request: CreateBaselineProfileRequest) -> BaselineProfile:
|
1186
|
-
"""Create a new baseline profile."""
|
1187
|
-
from .commands.baseline import CreateBaselineProfileCommand
|
1188
|
-
command = CreateBaselineProfileCommand(self.http_client, request)
|
1189
|
-
return command.execute()
|
1190
|
-
|
1191
|
-
def update_profile(self, profile_id: str, request: CreateBaselineProfileRequest) -> BaselineProfile:
|
1192
|
-
"""Update an existing baseline profile."""
|
1193
|
-
from .commands.baseline import UpdateBaselineProfileCommand
|
1194
|
-
command = UpdateBaselineProfileCommand(self.http_client, profile_id, request)
|
1195
|
-
return command.execute()
|
1196
|
-
|
1197
|
-
def delete_profile(self, profile_id: str) -> Dict[str, Any]:
|
1198
|
-
"""Delete a baseline profile."""
|
1199
|
-
from .commands.baseline import DeleteBaselineProfileCommand
|
1200
|
-
command = DeleteBaselineProfileCommand(self.http_client, profile_id)
|
1201
|
-
return command.execute()
|
1202
|
-
|
1203
|
-
def create_schedule(self, baseline_id: str, schedule_data: Dict[str, Any]) -> BaselineSchedule:
|
1204
|
-
"""Create a baseline schedule."""
|
1205
|
-
from .commands.baseline import CreateBaselineScheduleCommand
|
1206
|
-
command = CreateBaselineScheduleCommand(self.http_client, baseline_id, schedule_data)
|
1207
|
-
return command.execute()
|
1208
|
-
|
1209
|
-
def delete_schedule(self, schedule_id: str) -> Dict[str, Any]:
|
1210
|
-
"""Delete a baseline schedule."""
|
1211
|
-
from .commands.baseline import DeleteBaselineScheduleCommand
|
1212
|
-
command = DeleteBaselineScheduleCommand(self.http_client, schedule_id)
|
1213
|
-
return command.execute()
|
1214
|
-
|
1215
|
-
def get_comparison_report(self, baseline_id: str, task_id: str) -> BaselineComparison:
|
1216
|
-
"""Get comparison report - alias for get_comparison method (maintaining backward compatibility)."""
|
1217
|
-
# Use the comparison_id as the primary lookup since that's what the query expects
|
1218
|
-
return self.get_comparison(task_id)
|
1219
|
-
|
1220
|
-
def acquire(self, baseline_data: Dict[str, Any]) -> Baseline:
|
1221
|
-
"""Acquire baseline - wrapper for create method (maintaining backward compatibility)."""
|
1222
|
-
# Convert dict to CreateBaselineRequest if needed
|
1223
|
-
from .models.baseline import CreateBaselineRequest
|
1224
|
-
if isinstance(baseline_data, dict):
|
1225
|
-
request = CreateBaselineRequest(**baseline_data)
|
1226
|
-
else:
|
1227
|
-
request = baseline_data
|
1228
|
-
return self.create(request)
|
1229
|
-
|
1230
|
-
def acquire_by_filter(self, filter_data: Dict[str, Any], case_id: Optional[str] = None) -> Dict[str, Any]:
|
1231
|
-
"""Acquire baselines by asset filter criteria."""
|
1232
|
-
from .commands.baseline import AcquireBaselineByFilterCommand
|
1233
|
-
|
1234
|
-
payload = {
|
1235
|
-
"filter": filter_data,
|
1236
|
-
"caseId": case_id
|
1237
|
-
}
|
1238
|
-
|
1239
|
-
command = AcquireBaselineByFilterCommand(self.http_client, payload)
|
1240
|
-
return command.execute()
|
1241
|
-
|
1242
|
-
def compare_by_endpoint(self, endpoint_id: str, baseline_task_ids: List[str]) -> Dict[str, Any]:
|
1243
|
-
"""Compare baseline acquisition tasks by endpoint ID."""
|
1244
|
-
from .commands.baseline import CompareBaselineByEndpointCommand
|
1245
|
-
|
1246
|
-
payload = {
|
1247
|
-
"endpointId": endpoint_id,
|
1248
|
-
"taskIds": baseline_task_ids
|
1249
|
-
}
|
1250
|
-
|
1251
|
-
command = CompareBaselineByEndpointCommand(self.http_client, payload)
|
1252
|
-
return command.execute()
|
1253
|
-
|
1254
|
-
|
1255
|
-
class AIRClient:
|
1256
|
-
"""Main client for the Binalyze AIR API using CQRS architecture."""
|
1257
|
-
|
1258
|
-
def __init__(
|
1259
|
-
self,
|
1260
|
-
host: Optional[str] = None,
|
1261
|
-
api_token: Optional[str] = None,
|
1262
|
-
organization_id: Optional[int] = None,
|
1263
|
-
config_file: Optional[str] = None,
|
1264
|
-
config: Optional[AIRConfig] = None,
|
1265
|
-
**kwargs
|
1266
|
-
):
|
1267
|
-
"""
|
1268
|
-
Initialize the AIR client.
|
1269
|
-
|
1270
|
-
Args:
|
1271
|
-
host: AIR instance host URL
|
1272
|
-
api_token: API token for authentication
|
1273
|
-
organization_id: Default organization ID
|
1274
|
-
config_file: Path to configuration file
|
1275
|
-
config: Pre-configured AIRConfig instance
|
1276
|
-
**kwargs: Additional configuration options
|
1277
|
-
"""
|
1278
|
-
if config:
|
1279
|
-
self.config = config
|
1280
|
-
else:
|
1281
|
-
self.config = AIRConfig.create(
|
1282
|
-
host=host,
|
1283
|
-
api_token=api_token,
|
1284
|
-
organization_id=organization_id,
|
1285
|
-
config_file=config_file,
|
1286
|
-
**kwargs
|
1287
|
-
)
|
1288
|
-
|
1289
|
-
self.http_client = HTTPClient(self.config)
|
1290
|
-
|
1291
|
-
# Initialize API sections using CQRS pattern
|
1292
|
-
self.assets = AssetsAPI(self.http_client)
|
1293
|
-
self.cases = CasesAPI(self.http_client)
|
1294
|
-
self.tasks = TasksAPI(self.http_client)
|
1295
|
-
self.acquisitions = AcquisitionsAPI(self.http_client)
|
1296
|
-
self.policies = PoliciesAPI(self.http_client)
|
1297
|
-
self.organizations = OrganizationsAPI(self.http_client)
|
1298
|
-
self.triage = TriageAPI(self.http_client)
|
1299
|
-
self.audit = AuditAPI(self.http_client)
|
1300
|
-
self.baseline = BaselineAPI(self.http_client)
|
1301
|
-
|
1302
|
-
# NEW API sections
|
1303
|
-
self.authentication = AuthenticationAPI(self.http_client)
|
1304
|
-
self.user_management = UsersAPI(self.http_client)
|
1305
|
-
self.evidence = EvidenceAPI(self.http_client)
|
1306
|
-
self.auto_asset_tags = AutoAssetTagsAPI(self.http_client)
|
1307
|
-
self.evidences = EvidencesAPI(self.http_client)
|
1308
|
-
|
1309
|
-
# NEWEST API sections - 5 missing categories
|
1310
|
-
self.event_subscription = EventSubscriptionAPI(self.http_client)
|
1311
|
-
self.interact = InteractAPI(self.http_client)
|
1312
|
-
self.params = ParamsAPI(self.http_client)
|
1313
|
-
self.settings = SettingsAPI(self.http_client)
|
1314
|
-
self.endpoints = EndpointsAPI(self.http_client)
|
1315
|
-
|
1316
|
-
# Webhook API for triggering webhook endpoints
|
1317
|
-
self.webhooks = WebhookAPI(self.http_client)
|
1318
|
-
|
1319
|
-
def test_connection(self) -> bool:
|
1320
|
-
"""Test the connection to AIR API."""
|
1321
|
-
try:
|
1322
|
-
# Try to check authentication as a simple test
|
1323
|
-
self.authentication.check_status()
|
1324
|
-
return True
|
1325
|
-
except Exception:
|
1326
|
-
return False
|
1327
|
-
|
1328
|
-
@classmethod
|
1329
|
-
def from_environment(cls) -> "AIRClient":
|
1330
|
-
"""Create client from environment variables."""
|
1331
|
-
config = AIRConfig.from_environment()
|
1332
|
-
return cls(config=config)
|
1333
|
-
|
1334
|
-
@classmethod
|
1335
|
-
def from_config_file(cls, config_path: str = ".air_config.json") -> "AIRClient":
|
1336
|
-
"""Create client from configuration file."""
|
1337
|
-
config = AIRConfig.from_file(config_path)
|
1
|
+
"""
|
2
|
+
Main client for the Binalyze AIR SDK using CQRS architecture.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from typing import List, Optional, Union, Dict, Any
|
7
|
+
from datetime import datetime
|
8
|
+
|
9
|
+
from .config import AIRConfig
|
10
|
+
from .http_client import HTTPClient
|
11
|
+
|
12
|
+
# Import models
|
13
|
+
from .models.assets import Asset, AssetDetail, AssetTask, AssetFilter, AssetTaskFilter
|
14
|
+
from .models.cases import (
|
15
|
+
Case, CaseActivity, CaseEndpoint, CaseTask, User, CaseFilter, CaseActivityFilter,
|
16
|
+
CreateCaseRequest, UpdateCaseRequest, CaseNote, CaseEndpointFilter, CaseTaskFilter, CaseUserFilter
|
17
|
+
)
|
18
|
+
from .models.tasks import Task, TaskFilter, TaskAssignment
|
19
|
+
from .models.acquisitions import (
|
20
|
+
AcquisitionProfile, AcquisitionProfileDetails, AcquisitionFilter,
|
21
|
+
AcquisitionTaskRequest, ImageAcquisitionTaskRequest, CreateAcquisitionProfileRequest
|
22
|
+
)
|
23
|
+
from .models.policies import (
|
24
|
+
Policy, PolicyAssignment, PolicyExecution, PolicyFilter,
|
25
|
+
CreatePolicyRequest, UpdatePolicyRequest, AssignPolicyRequest
|
26
|
+
)
|
27
|
+
from .models.organizations import (
|
28
|
+
Organization, OrganizationUser, OrganizationRole, OrganizationLicense,
|
29
|
+
OrganizationSettings, OrganizationFilter, CreateOrganizationRequest,
|
30
|
+
UpdateOrganizationRequest, AddUserToOrganizationRequest, OrganizationsPaginatedResponse,
|
31
|
+
OrganizationUsersPaginatedResponse
|
32
|
+
)
|
33
|
+
from .models.triage import (
|
34
|
+
TriageRule, TriageTag,
|
35
|
+
TriageFilter, CreateTriageRuleRequest, UpdateTriageRuleRequest,
|
36
|
+
CreateTriageTagRequest, CreateTriageProfileRequest
|
37
|
+
)
|
38
|
+
from .models.audit import (
|
39
|
+
AuditLog, AuditSummary, AuditUserActivity, AuditSystemEvent,
|
40
|
+
AuditRetentionPolicy, AuditFilter, AuditLogsFilter, AuditLevel
|
41
|
+
)
|
42
|
+
from .models.baseline import (
|
43
|
+
Baseline, BaselineProfile, BaselineComparison, BaselineSchedule,
|
44
|
+
BaselineFilter, CreateBaselineRequest, UpdateBaselineRequest,
|
45
|
+
CreateBaselineProfileRequest, CompareBaselineRequest
|
46
|
+
)
|
47
|
+
from .models.auth import (
|
48
|
+
AuthStatus, LoginRequest, LoginResponse
|
49
|
+
)
|
50
|
+
from .models.user_management import (
|
51
|
+
UserManagementUser, CreateUserRequest, UpdateUserRequest,
|
52
|
+
AIUser, CreateAIUserRequest, APIUser, CreateAPIUserRequest, UserFilter
|
53
|
+
)
|
54
|
+
from .models.evidence import (
|
55
|
+
EvidencePPC, EvidenceReportFileInfo, EvidenceReport
|
56
|
+
)
|
57
|
+
from .models.auto_asset_tags import (
|
58
|
+
AutoAssetTag, CreateAutoAssetTagRequest, UpdateAutoAssetTagRequest,
|
59
|
+
StartTaggingRequest, TaggingResult, AutoAssetTagFilter
|
60
|
+
)
|
61
|
+
from .models.evidences import (
|
62
|
+
EvidenceRepository, AmazonS3Repository, AzureStorageRepository,
|
63
|
+
FTPSRepository, SFTPRepository, SMBRepository, RepositoryFilter,
|
64
|
+
CreateAmazonS3RepositoryRequest, UpdateAmazonS3RepositoryRequest,
|
65
|
+
CreateAzureStorageRepositoryRequest, UpdateAzureStorageRepositoryRequest,
|
66
|
+
CreateFTPSRepositoryRequest, UpdateFTPSRepositoryRequest,
|
67
|
+
CreateSFTPRepositoryRequest, UpdateSFTPRepositoryRequest,
|
68
|
+
CreateSMBRepositoryRequest, UpdateSMBRepositoryRequest,
|
69
|
+
ValidateRepositoryRequest, ValidationResult
|
70
|
+
)
|
71
|
+
from .models.event_subscription import (
|
72
|
+
EventSubscription, EventSubscriptionFilter, CreateEventSubscriptionRequest,
|
73
|
+
UpdateEventSubscriptionRequest
|
74
|
+
)
|
75
|
+
from .models.interact import (
|
76
|
+
ShellInteraction, AssignShellTaskRequest, ShellTaskResponse
|
77
|
+
)
|
78
|
+
from .models.params import (
|
79
|
+
AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer
|
80
|
+
)
|
81
|
+
from .models.settings import (
|
82
|
+
BannerSettings, UpdateBannerSettingsRequest
|
83
|
+
)
|
84
|
+
from .models.license import (
|
85
|
+
License, LicenseUpdateRequest
|
86
|
+
)
|
87
|
+
from .models.logger import (
|
88
|
+
LogDownloadRequest, LogDownloadResponse
|
89
|
+
)
|
90
|
+
from .models.multipart_upload import (
|
91
|
+
UploadInitializeRequest, UploadInitializeResponse, UploadPartRequest, UploadPartResponse,
|
92
|
+
UploadStatusRequest, UploadStatusResponse, UploadFinalizeRequest, UploadFinalizeResponse,
|
93
|
+
MultipartUploadSession, FileChunker
|
94
|
+
)
|
95
|
+
|
96
|
+
# Import ALL API classes from their separate files
|
97
|
+
from .apis.assets import AssetsAPI
|
98
|
+
from .apis.cases import CasesAPI
|
99
|
+
from .apis.tasks import TasksAPI
|
100
|
+
from .apis.acquisitions import AcquisitionsAPI
|
101
|
+
from .apis.policies import PoliciesAPI
|
102
|
+
from .apis.organizations import OrganizationsAPI
|
103
|
+
from .apis.triage import TriageAPI
|
104
|
+
from .apis.audit_logs import AuditAPI
|
105
|
+
from .apis.baseline import BaselineAPI
|
106
|
+
from .apis.auth import AuthAPI
|
107
|
+
from .apis.evidence import EvidenceAPI
|
108
|
+
from .apis.auto_asset_tags import AutoAssetTagsAPI
|
109
|
+
from .apis.event_subscription import EventSubscriptionAPI
|
110
|
+
from .apis.interact import InteractAPI
|
111
|
+
from .apis.params import ParamsAPI
|
112
|
+
from .apis.settings import SettingsAPI
|
113
|
+
from .apis.webhooks import WebhookAPI
|
114
|
+
from .apis.api_tokens import APITokensAPI
|
115
|
+
from .apis.investigation_hub import InvestigationHubAPI
|
116
|
+
from .apis.cloud_forensics import CloudForensicsAPI
|
117
|
+
from .apis.backup import BackupAPI
|
118
|
+
from .apis.license import LicenseAPI
|
119
|
+
from .apis.logger import LoggerAPI
|
120
|
+
from .apis.multipart_upload import MultipartUploadAPI
|
121
|
+
from .apis.notifications import NotificationsAPI
|
122
|
+
from .apis.preset_filters import PresetFiltersAPI
|
123
|
+
from .apis.recent_activities import RecentActivitiesAPI
|
124
|
+
from .apis.relay_server import RelayServerAPI
|
125
|
+
from .apis.webhook_executions import WebhookExecutionsAPI
|
126
|
+
from .apis.user_management import UserManagementAPI
|
127
|
+
|
128
|
+
|
129
|
+
class AIRClient:
|
130
|
+
"""Main client for the Binalyze AIR API using CQRS architecture."""
|
131
|
+
|
132
|
+
def __init__(
|
133
|
+
self,
|
134
|
+
host: Optional[str] = None,
|
135
|
+
api_token: Optional[str] = None,
|
136
|
+
organization_id: Optional[int] = None,
|
137
|
+
config_file: Optional[str] = None,
|
138
|
+
config: Optional[AIRConfig] = None,
|
139
|
+
**kwargs
|
140
|
+
):
|
141
|
+
"""
|
142
|
+
Initialize the AIR client.
|
143
|
+
|
144
|
+
Args:
|
145
|
+
host: AIR instance host URL
|
146
|
+
api_token: API token for authentication
|
147
|
+
organization_id: Default organization ID
|
148
|
+
config_file: Path to configuration file
|
149
|
+
config: Pre-configured AIRConfig instance
|
150
|
+
**kwargs: Additional configuration options
|
151
|
+
"""
|
152
|
+
if config:
|
153
|
+
self.config = config
|
154
|
+
else:
|
155
|
+
self.config = AIRConfig.create(
|
156
|
+
host=host,
|
157
|
+
api_token=api_token,
|
158
|
+
organization_id=organization_id,
|
159
|
+
config_file=config_file,
|
160
|
+
**kwargs
|
161
|
+
)
|
162
|
+
|
163
|
+
self.http_client = HTTPClient(self.config)
|
164
|
+
|
165
|
+
# Initialize API sections using CQRS pattern - ALL FROM SEPARATE FILES
|
166
|
+
self.assets = AssetsAPI(self.http_client)
|
167
|
+
self.cases = CasesAPI(self.http_client)
|
168
|
+
self.tasks = TasksAPI(self.http_client)
|
169
|
+
self.acquisitions = AcquisitionsAPI(self.http_client)
|
170
|
+
self.policies = PoliciesAPI(self.http_client)
|
171
|
+
self.organizations = OrganizationsAPI(self.http_client)
|
172
|
+
self.triage = TriageAPI(self.http_client)
|
173
|
+
self.audit = AuditAPI(self.http_client)
|
174
|
+
self.baseline = BaselineAPI(self.http_client)
|
175
|
+
|
176
|
+
# Additional API sections
|
177
|
+
self.auth = AuthAPI(self.http_client)
|
178
|
+
self.user_management = UserManagementAPI(self.http_client)
|
179
|
+
self.evidence = EvidenceAPI(self.http_client)
|
180
|
+
self.auto_asset_tags = AutoAssetTagsAPI(self.http_client)
|
181
|
+
self.event_subscription = EventSubscriptionAPI(self.http_client)
|
182
|
+
self.interact = InteractAPI(self.http_client)
|
183
|
+
self.params = ParamsAPI(self.http_client)
|
184
|
+
self.settings = SettingsAPI(self.http_client)
|
185
|
+
self.webhooks = WebhookAPI(self.http_client)
|
186
|
+
self.api_tokens = APITokensAPI(self.http_client)
|
187
|
+
self.investigation_hub = InvestigationHubAPI(self.http_client)
|
188
|
+
self.cloud_forensics = CloudForensicsAPI(self.http_client)
|
189
|
+
self.backup = BackupAPI(self.http_client)
|
190
|
+
self.license = LicenseAPI(self.http_client)
|
191
|
+
self.logger = LoggerAPI(self.http_client)
|
192
|
+
self.multipart_upload = MultipartUploadAPI(self.http_client)
|
193
|
+
self.notifications = NotificationsAPI(self.http_client)
|
194
|
+
self.preset_filters = PresetFiltersAPI(self.http_client)
|
195
|
+
self.recent_activities = RecentActivitiesAPI(self.http_client)
|
196
|
+
self.relay_server = RelayServerAPI(self.http_client)
|
197
|
+
self.webhook_executions = WebhookExecutionsAPI(self.http_client)
|
198
|
+
|
199
|
+
def test_connection(self) -> bool:
|
200
|
+
"""Test the connection to AIR API."""
|
201
|
+
try:
|
202
|
+
# Try to check authentication as a simple test
|
203
|
+
self.auth.check_status()
|
204
|
+
return True
|
205
|
+
except Exception:
|
206
|
+
return False
|
207
|
+
|
208
|
+
@classmethod
|
209
|
+
def from_environment(cls) -> "AIRClient":
|
210
|
+
"""Create client from environment variables."""
|
211
|
+
config = AIRConfig.from_environment()
|
212
|
+
return cls(config=config)
|
213
|
+
|
214
|
+
@classmethod
|
215
|
+
def from_config_file(cls, config_path: str = ".air_config.json") -> "AIRClient":
|
216
|
+
"""Create client from configuration file."""
|
217
|
+
config = AIRConfig.from_file(config_path)
|
1338
218
|
return cls(config=config)
|