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/queries/cases.py
CHANGED
@@ -1,293 +1,293 @@
|
|
1
|
-
"""
|
2
|
-
Case-related queries for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List, Optional
|
6
|
-
|
7
|
-
from ..base import Query
|
8
|
-
from ..models.cases import (
|
9
|
-
Case, CaseActivity, CaseEndpoint, CaseTask, User, CaseFilter, CaseActivityFilter,
|
10
|
-
CaseEndpointFilter, CaseTaskFilter, CaseUserFilter
|
11
|
-
)
|
12
|
-
from ..http_client import HTTPClient
|
13
|
-
|
14
|
-
|
15
|
-
class ListCasesQuery(Query[List[Case]]):
|
16
|
-
"""Query to list cases with optional filtering."""
|
17
|
-
|
18
|
-
def __init__(self, http_client: HTTPClient, filter_params: Optional[CaseFilter] = None, organization_ids: Optional[List[int]] = None):
|
19
|
-
self.http_client = http_client
|
20
|
-
self.filter_params = filter_params or CaseFilter()
|
21
|
-
self.organization_ids = organization_ids or [0]
|
22
|
-
|
23
|
-
def execute(self) -> List[Case]:
|
24
|
-
"""Execute the query to list cases."""
|
25
|
-
params = self.filter_params.to_params()
|
26
|
-
|
27
|
-
# Add organization IDs
|
28
|
-
params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
|
29
|
-
|
30
|
-
# Ensure consistent sorting to match API defaults
|
31
|
-
if "sortBy" not in params:
|
32
|
-
params["sortBy"] = "createdAt"
|
33
|
-
if "sortType" not in params:
|
34
|
-
params["sortType"] = "ASC"
|
35
|
-
|
36
|
-
response = self.http_client.get("cases", params=params)
|
37
|
-
|
38
|
-
entities = response.get("result", {}).get("entities", [])
|
39
|
-
|
40
|
-
# Use Pydantic parsing with proper field aliasing
|
41
|
-
return [Case.model_validate(entity_data) for entity_data in entities]
|
42
|
-
|
43
|
-
|
44
|
-
class GetCaseQuery(Query[Case]):
|
45
|
-
"""Query to get a specific case by ID."""
|
46
|
-
|
47
|
-
def __init__(self, http_client: HTTPClient, case_id: str):
|
48
|
-
self.http_client = http_client
|
49
|
-
self.case_id = case_id
|
50
|
-
|
51
|
-
def execute(self) -> Case:
|
52
|
-
"""Execute the query to get case details."""
|
53
|
-
response = self.http_client.get(f"cases/{self.case_id}")
|
54
|
-
|
55
|
-
entity_data = response.get("result", {})
|
56
|
-
|
57
|
-
# Use Pydantic parsing with proper field aliasing
|
58
|
-
return Case.model_validate(entity_data)
|
59
|
-
|
60
|
-
|
61
|
-
class GetCaseActivitiesQuery(Query[List[CaseActivity]]):
|
62
|
-
"""Query to get activities for a specific case."""
|
63
|
-
|
64
|
-
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseActivityFilter] = None):
|
65
|
-
self.http_client = http_client
|
66
|
-
self.case_id = case_id
|
67
|
-
self.filter_params = filter_params or CaseActivityFilter()
|
68
|
-
|
69
|
-
def execute(self) -> List[CaseActivity]:
|
70
|
-
"""Execute the query to get case activities."""
|
71
|
-
params = {}
|
72
|
-
|
73
|
-
# Add pagination parameters
|
74
|
-
if self.filter_params.page_number is not None:
|
75
|
-
params["pageNumber"] = self.filter_params.page_number
|
76
|
-
if self.filter_params.page_size is not None:
|
77
|
-
params["pageSize"] = self.filter_params.page_size
|
78
|
-
|
79
|
-
# Add sorting parameters
|
80
|
-
if self.filter_params.sort_by is not None:
|
81
|
-
params["sortBy"] = self.filter_params.sort_by
|
82
|
-
if self.filter_params.sort_type is not None:
|
83
|
-
params["sortType"] = self.filter_params.sort_type
|
84
|
-
|
85
|
-
# Add filter parameters
|
86
|
-
if self.filter_params.performed_by is not None:
|
87
|
-
params["filter[performedBy]"] = ",".join(self.filter_params.performed_by)
|
88
|
-
if self.filter_params.types is not None:
|
89
|
-
params["filter[types]"] = ",".join(self.filter_params.types)
|
90
|
-
if self.filter_params.search_term is not None:
|
91
|
-
params["filter[searchTerm]"] = self.filter_params.search_term
|
92
|
-
if self.filter_params.occurred_at is not None:
|
93
|
-
params["filter[occurredAt]"] = self.filter_params.occurred_at
|
94
|
-
|
95
|
-
response = self.http_client.get(f"cases/{self.case_id}/activities", params=params)
|
96
|
-
|
97
|
-
entities = response.get("result", {}).get("entities", [])
|
98
|
-
|
99
|
-
# Use Pydantic parsing with proper field aliasing
|
100
|
-
return [CaseActivity.model_validate(entity_data) for entity_data in entities]
|
101
|
-
|
102
|
-
|
103
|
-
class GetCaseEndpointsQuery(Query[List[CaseEndpoint]]):
|
104
|
-
"""Query to get endpoints for a specific case."""
|
105
|
-
|
106
|
-
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseEndpointFilter] = None):
|
107
|
-
self.http_client = http_client
|
108
|
-
self.case_id = case_id
|
109
|
-
self.filter_params = filter_params or CaseEndpointFilter()
|
110
|
-
|
111
|
-
def execute(self) -> List[CaseEndpoint]:
|
112
|
-
"""Execute the query to get case endpoints."""
|
113
|
-
params = {}
|
114
|
-
|
115
|
-
# Add pagination parameters
|
116
|
-
if self.filter_params.page_number is not None:
|
117
|
-
params["pageNumber"] = self.filter_params.page_number
|
118
|
-
if self.filter_params.page_size is not None:
|
119
|
-
params["pageSize"] = self.filter_params.page_size
|
120
|
-
|
121
|
-
# Add sorting parameters
|
122
|
-
if self.filter_params.sort_by is not None:
|
123
|
-
params["sortBy"] = self.filter_params.sort_by
|
124
|
-
if self.filter_params.sort_type is not None:
|
125
|
-
params["sortType"] = self.filter_params.sort_type
|
126
|
-
|
127
|
-
# Add filter parameters
|
128
|
-
if self.filter_params.organization_ids is not None:
|
129
|
-
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
130
|
-
else:
|
131
|
-
# Default to organization 0 if not specified
|
132
|
-
params["filter[organizationIds]"] = "0"
|
133
|
-
|
134
|
-
if self.filter_params.search_term is not None:
|
135
|
-
params["filter[searchTerm]"] = self.filter_params.search_term
|
136
|
-
if self.filter_params.name is not None:
|
137
|
-
params["filter[name]"] = self.filter_params.name
|
138
|
-
if self.filter_params.ip_address is not None:
|
139
|
-
params["filter[ipAddress]"] = self.filter_params.ip_address
|
140
|
-
if self.filter_params.group_id is not None:
|
141
|
-
params["filter[groupId]"] = self.filter_params.group_id
|
142
|
-
if self.filter_params.group_full_path is not None:
|
143
|
-
params["filter[groupFullPath]"] = self.filter_params.group_full_path
|
144
|
-
if self.filter_params.label is not None:
|
145
|
-
params["filter[label]"] = self.filter_params.label
|
146
|
-
if self.filter_params.last_seen is not None:
|
147
|
-
params["filter[lastSeen]"] = self.filter_params.last_seen
|
148
|
-
if self.filter_params.managed_status is not None:
|
149
|
-
params["filter[managedStatus]"] = ",".join(self.filter_params.managed_status)
|
150
|
-
if self.filter_params.isolation_status is not None:
|
151
|
-
params["filter[isolationStatus]"] = ",".join(self.filter_params.isolation_status)
|
152
|
-
if self.filter_params.platform is not None:
|
153
|
-
params["filter[platform]"] = ",".join(self.filter_params.platform)
|
154
|
-
if self.filter_params.issue is not None:
|
155
|
-
params["filter[issue]"] = ",".join(self.filter_params.issue)
|
156
|
-
if self.filter_params.online_status is not None:
|
157
|
-
params["filter[onlineStatus]"] = ",".join(self.filter_params.online_status)
|
158
|
-
if self.filter_params.tags is not None:
|
159
|
-
params["filter[tags]"] = ",".join(self.filter_params.tags)
|
160
|
-
if self.filter_params.version is not None:
|
161
|
-
params["filter[version]"] = self.filter_params.version
|
162
|
-
if self.filter_params.policy is not None:
|
163
|
-
params["filter[policy]"] = self.filter_params.policy
|
164
|
-
if self.filter_params.included_endpoint_ids is not None:
|
165
|
-
params["filter[includedEndpointIds]"] = ",".join(self.filter_params.included_endpoint_ids)
|
166
|
-
if self.filter_params.excluded_endpoint_ids is not None:
|
167
|
-
params["filter[excludedEndpointIds]"] = ",".join(self.filter_params.excluded_endpoint_ids)
|
168
|
-
if self.filter_params.aws_regions is not None:
|
169
|
-
params["filter[awsRegions]"] = ",".join(self.filter_params.aws_regions)
|
170
|
-
if self.filter_params.azure_regions is not None:
|
171
|
-
params["filter[azureRegions]"] = ",".join(self.filter_params.azure_regions)
|
172
|
-
|
173
|
-
response = self.http_client.get(f"cases/{self.case_id}/endpoints", params=params)
|
174
|
-
|
175
|
-
entities = response.get("result", {}).get("entities", [])
|
176
|
-
|
177
|
-
# Use Pydantic parsing with proper field aliasing
|
178
|
-
return [CaseEndpoint.model_validate(entity_data) for entity_data in entities]
|
179
|
-
|
180
|
-
|
181
|
-
class GetCaseTasksQuery(Query[List[CaseTask]]):
|
182
|
-
"""Query to get tasks for a specific case."""
|
183
|
-
|
184
|
-
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseTaskFilter] = None):
|
185
|
-
self.http_client = http_client
|
186
|
-
self.case_id = case_id
|
187
|
-
self.filter_params = filter_params or CaseTaskFilter()
|
188
|
-
|
189
|
-
def execute(self) -> List[CaseTask]:
|
190
|
-
"""Execute the query to get case tasks."""
|
191
|
-
params = {}
|
192
|
-
|
193
|
-
# Add pagination parameters
|
194
|
-
if self.filter_params.page_number is not None:
|
195
|
-
params["pageNumber"] = self.filter_params.page_number
|
196
|
-
if self.filter_params.page_size is not None:
|
197
|
-
params["pageSize"] = self.filter_params.page_size
|
198
|
-
|
199
|
-
# Add sorting parameters
|
200
|
-
if self.filter_params.sort_by is not None:
|
201
|
-
params["sortBy"] = self.filter_params.sort_by
|
202
|
-
if self.filter_params.sort_type is not None:
|
203
|
-
params["sortType"] = self.filter_params.sort_type
|
204
|
-
|
205
|
-
# Add filter parameters
|
206
|
-
if self.filter_params.organization_ids is not None:
|
207
|
-
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
208
|
-
else:
|
209
|
-
# Default to organization 0 if not specified
|
210
|
-
params["filter[organizationIds]"] = "0"
|
211
|
-
|
212
|
-
if self.filter_params.search_term is not None:
|
213
|
-
params["filter[searchTerm]"] = self.filter_params.search_term
|
214
|
-
if self.filter_params.name is not None:
|
215
|
-
params["filter[name]"] = self.filter_params.name
|
216
|
-
if self.filter_params.endpoint_ids is not None:
|
217
|
-
params["filter[endpointIds]"] = ",".join(self.filter_params.endpoint_ids)
|
218
|
-
if self.filter_params.execution_type is not None:
|
219
|
-
params["filter[executionType]"] = self.filter_params.execution_type
|
220
|
-
if self.filter_params.status is not None:
|
221
|
-
params["filter[status]"] = self.filter_params.status
|
222
|
-
if self.filter_params.type is not None:
|
223
|
-
params["filter[type]"] = self.filter_params.type
|
224
|
-
if self.filter_params.asset_names is not None:
|
225
|
-
params["filter[assetNames]"] = self.filter_params.asset_names
|
226
|
-
if self.filter_params.started_by is not None:
|
227
|
-
params["filter[startedBy]"] = self.filter_params.started_by
|
228
|
-
|
229
|
-
response = self.http_client.get(f"cases/{self.case_id}/tasks", params=params)
|
230
|
-
|
231
|
-
entities = response.get("result", {}).get("entities", [])
|
232
|
-
|
233
|
-
# Use Pydantic parsing with proper field aliasing
|
234
|
-
return [CaseTask.model_validate(entity_data) for entity_data in entities]
|
235
|
-
|
236
|
-
|
237
|
-
class GetCaseUsersQuery(Query[List[User]]):
|
238
|
-
"""Query to get users for a specific case."""
|
239
|
-
|
240
|
-
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseUserFilter] = None):
|
241
|
-
self.http_client = http_client
|
242
|
-
self.case_id = case_id
|
243
|
-
self.filter_params = filter_params or CaseUserFilter()
|
244
|
-
|
245
|
-
def execute(self) -> List[User]:
|
246
|
-
"""Execute the query to get case users."""
|
247
|
-
params = {}
|
248
|
-
|
249
|
-
# Add pagination parameters
|
250
|
-
if self.filter_params.page_number is not None:
|
251
|
-
params["pageNumber"] = self.filter_params.page_number
|
252
|
-
if self.filter_params.page_size is not None:
|
253
|
-
params["pageSize"] = self.filter_params.page_size
|
254
|
-
|
255
|
-
# Add sorting parameters
|
256
|
-
if self.filter_params.sort_by is not None:
|
257
|
-
params["sortBy"] = self.filter_params.sort_by
|
258
|
-
if self.filter_params.sort_type is not None:
|
259
|
-
params["sortType"] = self.filter_params.sort_type
|
260
|
-
|
261
|
-
# Add filter parameters
|
262
|
-
if self.filter_params.organization_ids is not None:
|
263
|
-
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
264
|
-
else:
|
265
|
-
# Default to organization 0 if not specified
|
266
|
-
params["filter[organizationIds]"] = "0"
|
267
|
-
|
268
|
-
if self.filter_params.search_term is not None:
|
269
|
-
params["filter[searchTerm]"] = self.filter_params.search_term
|
270
|
-
|
271
|
-
response = self.http_client.get(f"cases/{self.case_id}/users", params=params)
|
272
|
-
|
273
|
-
entities = response.get("result", {}).get("entities", [])
|
274
|
-
|
275
|
-
# Use Pydantic parsing with proper field aliasing
|
276
|
-
return [User.model_validate(entity_data) for entity_data in entities]
|
277
|
-
|
278
|
-
|
279
|
-
class CheckCaseNameQuery(Query[bool]):
|
280
|
-
"""Query to check if a case name is available."""
|
281
|
-
|
282
|
-
def __init__(self, http_client: HTTPClient, name: str):
|
283
|
-
self.http_client = http_client
|
284
|
-
self.name = name
|
285
|
-
|
286
|
-
def execute(self) -> bool:
|
287
|
-
"""Execute the query to check case name availability."""
|
288
|
-
params = {"name": self.name}
|
289
|
-
|
290
|
-
response = self.http_client.get("cases/check", params=params)
|
291
|
-
|
292
|
-
# Return the actual result (whether name is taken/available)
|
1
|
+
"""
|
2
|
+
Case-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.cases import (
|
9
|
+
Case, CaseActivity, CaseEndpoint, CaseTask, User, CaseFilter, CaseActivityFilter,
|
10
|
+
CaseEndpointFilter, CaseTaskFilter, CaseUserFilter
|
11
|
+
)
|
12
|
+
from ..http_client import HTTPClient
|
13
|
+
|
14
|
+
|
15
|
+
class ListCasesQuery(Query[List[Case]]):
|
16
|
+
"""Query to list cases with optional filtering."""
|
17
|
+
|
18
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[CaseFilter] = None, organization_ids: Optional[List[int]] = None):
|
19
|
+
self.http_client = http_client
|
20
|
+
self.filter_params = filter_params or CaseFilter()
|
21
|
+
self.organization_ids = organization_ids or [0]
|
22
|
+
|
23
|
+
def execute(self) -> List[Case]:
|
24
|
+
"""Execute the query to list cases."""
|
25
|
+
params = self.filter_params.to_params()
|
26
|
+
|
27
|
+
# Add organization IDs
|
28
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.organization_ids))
|
29
|
+
|
30
|
+
# Ensure consistent sorting to match API defaults
|
31
|
+
if "sortBy" not in params:
|
32
|
+
params["sortBy"] = "createdAt"
|
33
|
+
if "sortType" not in params:
|
34
|
+
params["sortType"] = "ASC"
|
35
|
+
|
36
|
+
response = self.http_client.get("cases", params=params)
|
37
|
+
|
38
|
+
entities = response.get("result", {}).get("entities", [])
|
39
|
+
|
40
|
+
# Use Pydantic parsing with proper field aliasing
|
41
|
+
return [Case.model_validate(entity_data) for entity_data in entities]
|
42
|
+
|
43
|
+
|
44
|
+
class GetCaseQuery(Query[Case]):
|
45
|
+
"""Query to get a specific case by ID."""
|
46
|
+
|
47
|
+
def __init__(self, http_client: HTTPClient, case_id: str):
|
48
|
+
self.http_client = http_client
|
49
|
+
self.case_id = case_id
|
50
|
+
|
51
|
+
def execute(self) -> Case:
|
52
|
+
"""Execute the query to get case details."""
|
53
|
+
response = self.http_client.get(f"cases/{self.case_id}")
|
54
|
+
|
55
|
+
entity_data = response.get("result", {})
|
56
|
+
|
57
|
+
# Use Pydantic parsing with proper field aliasing
|
58
|
+
return Case.model_validate(entity_data)
|
59
|
+
|
60
|
+
|
61
|
+
class GetCaseActivitiesQuery(Query[List[CaseActivity]]):
|
62
|
+
"""Query to get activities for a specific case."""
|
63
|
+
|
64
|
+
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseActivityFilter] = None):
|
65
|
+
self.http_client = http_client
|
66
|
+
self.case_id = case_id
|
67
|
+
self.filter_params = filter_params or CaseActivityFilter()
|
68
|
+
|
69
|
+
def execute(self) -> List[CaseActivity]:
|
70
|
+
"""Execute the query to get case activities."""
|
71
|
+
params = {}
|
72
|
+
|
73
|
+
# Add pagination parameters
|
74
|
+
if self.filter_params.page_number is not None:
|
75
|
+
params["pageNumber"] = self.filter_params.page_number
|
76
|
+
if self.filter_params.page_size is not None:
|
77
|
+
params["pageSize"] = self.filter_params.page_size
|
78
|
+
|
79
|
+
# Add sorting parameters
|
80
|
+
if self.filter_params.sort_by is not None:
|
81
|
+
params["sortBy"] = self.filter_params.sort_by
|
82
|
+
if self.filter_params.sort_type is not None:
|
83
|
+
params["sortType"] = self.filter_params.sort_type
|
84
|
+
|
85
|
+
# Add filter parameters
|
86
|
+
if self.filter_params.performed_by is not None:
|
87
|
+
params["filter[performedBy]"] = ",".join(self.filter_params.performed_by)
|
88
|
+
if self.filter_params.types is not None:
|
89
|
+
params["filter[types]"] = ",".join(self.filter_params.types)
|
90
|
+
if self.filter_params.search_term is not None:
|
91
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
92
|
+
if self.filter_params.occurred_at is not None:
|
93
|
+
params["filter[occurredAt]"] = self.filter_params.occurred_at
|
94
|
+
|
95
|
+
response = self.http_client.get(f"cases/{self.case_id}/activities", params=params)
|
96
|
+
|
97
|
+
entities = response.get("result", {}).get("entities", [])
|
98
|
+
|
99
|
+
# Use Pydantic parsing with proper field aliasing
|
100
|
+
return [CaseActivity.model_validate(entity_data) for entity_data in entities]
|
101
|
+
|
102
|
+
|
103
|
+
class GetCaseEndpointsQuery(Query[List[CaseEndpoint]]):
|
104
|
+
"""Query to get endpoints for a specific case."""
|
105
|
+
|
106
|
+
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseEndpointFilter] = None):
|
107
|
+
self.http_client = http_client
|
108
|
+
self.case_id = case_id
|
109
|
+
self.filter_params = filter_params or CaseEndpointFilter()
|
110
|
+
|
111
|
+
def execute(self) -> List[CaseEndpoint]:
|
112
|
+
"""Execute the query to get case endpoints."""
|
113
|
+
params = {}
|
114
|
+
|
115
|
+
# Add pagination parameters
|
116
|
+
if self.filter_params.page_number is not None:
|
117
|
+
params["pageNumber"] = self.filter_params.page_number
|
118
|
+
if self.filter_params.page_size is not None:
|
119
|
+
params["pageSize"] = self.filter_params.page_size
|
120
|
+
|
121
|
+
# Add sorting parameters
|
122
|
+
if self.filter_params.sort_by is not None:
|
123
|
+
params["sortBy"] = self.filter_params.sort_by
|
124
|
+
if self.filter_params.sort_type is not None:
|
125
|
+
params["sortType"] = self.filter_params.sort_type
|
126
|
+
|
127
|
+
# Add filter parameters
|
128
|
+
if self.filter_params.organization_ids is not None:
|
129
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
130
|
+
else:
|
131
|
+
# Default to organization 0 if not specified
|
132
|
+
params["filter[organizationIds]"] = "0"
|
133
|
+
|
134
|
+
if self.filter_params.search_term is not None:
|
135
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
136
|
+
if self.filter_params.name is not None:
|
137
|
+
params["filter[name]"] = self.filter_params.name
|
138
|
+
if self.filter_params.ip_address is not None:
|
139
|
+
params["filter[ipAddress]"] = self.filter_params.ip_address
|
140
|
+
if self.filter_params.group_id is not None:
|
141
|
+
params["filter[groupId]"] = self.filter_params.group_id
|
142
|
+
if self.filter_params.group_full_path is not None:
|
143
|
+
params["filter[groupFullPath]"] = self.filter_params.group_full_path
|
144
|
+
if self.filter_params.label is not None:
|
145
|
+
params["filter[label]"] = self.filter_params.label
|
146
|
+
if self.filter_params.last_seen is not None:
|
147
|
+
params["filter[lastSeen]"] = self.filter_params.last_seen
|
148
|
+
if self.filter_params.managed_status is not None:
|
149
|
+
params["filter[managedStatus]"] = ",".join(self.filter_params.managed_status)
|
150
|
+
if self.filter_params.isolation_status is not None:
|
151
|
+
params["filter[isolationStatus]"] = ",".join(self.filter_params.isolation_status)
|
152
|
+
if self.filter_params.platform is not None:
|
153
|
+
params["filter[platform]"] = ",".join(self.filter_params.platform)
|
154
|
+
if self.filter_params.issue is not None:
|
155
|
+
params["filter[issue]"] = ",".join(self.filter_params.issue)
|
156
|
+
if self.filter_params.online_status is not None:
|
157
|
+
params["filter[onlineStatus]"] = ",".join(self.filter_params.online_status)
|
158
|
+
if self.filter_params.tags is not None:
|
159
|
+
params["filter[tags]"] = ",".join(self.filter_params.tags)
|
160
|
+
if self.filter_params.version is not None:
|
161
|
+
params["filter[version]"] = self.filter_params.version
|
162
|
+
if self.filter_params.policy is not None:
|
163
|
+
params["filter[policy]"] = self.filter_params.policy
|
164
|
+
if self.filter_params.included_endpoint_ids is not None:
|
165
|
+
params["filter[includedEndpointIds]"] = ",".join(self.filter_params.included_endpoint_ids)
|
166
|
+
if self.filter_params.excluded_endpoint_ids is not None:
|
167
|
+
params["filter[excludedEndpointIds]"] = ",".join(self.filter_params.excluded_endpoint_ids)
|
168
|
+
if self.filter_params.aws_regions is not None:
|
169
|
+
params["filter[awsRegions]"] = ",".join(self.filter_params.aws_regions)
|
170
|
+
if self.filter_params.azure_regions is not None:
|
171
|
+
params["filter[azureRegions]"] = ",".join(self.filter_params.azure_regions)
|
172
|
+
|
173
|
+
response = self.http_client.get(f"cases/{self.case_id}/endpoints", params=params)
|
174
|
+
|
175
|
+
entities = response.get("result", {}).get("entities", [])
|
176
|
+
|
177
|
+
# Use Pydantic parsing with proper field aliasing
|
178
|
+
return [CaseEndpoint.model_validate(entity_data) for entity_data in entities]
|
179
|
+
|
180
|
+
|
181
|
+
class GetCaseTasksQuery(Query[List[CaseTask]]):
|
182
|
+
"""Query to get tasks for a specific case."""
|
183
|
+
|
184
|
+
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseTaskFilter] = None):
|
185
|
+
self.http_client = http_client
|
186
|
+
self.case_id = case_id
|
187
|
+
self.filter_params = filter_params or CaseTaskFilter()
|
188
|
+
|
189
|
+
def execute(self) -> List[CaseTask]:
|
190
|
+
"""Execute the query to get case tasks."""
|
191
|
+
params = {}
|
192
|
+
|
193
|
+
# Add pagination parameters
|
194
|
+
if self.filter_params.page_number is not None:
|
195
|
+
params["pageNumber"] = self.filter_params.page_number
|
196
|
+
if self.filter_params.page_size is not None:
|
197
|
+
params["pageSize"] = self.filter_params.page_size
|
198
|
+
|
199
|
+
# Add sorting parameters
|
200
|
+
if self.filter_params.sort_by is not None:
|
201
|
+
params["sortBy"] = self.filter_params.sort_by
|
202
|
+
if self.filter_params.sort_type is not None:
|
203
|
+
params["sortType"] = self.filter_params.sort_type
|
204
|
+
|
205
|
+
# Add filter parameters
|
206
|
+
if self.filter_params.organization_ids is not None:
|
207
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
208
|
+
else:
|
209
|
+
# Default to organization 0 if not specified
|
210
|
+
params["filter[organizationIds]"] = "0"
|
211
|
+
|
212
|
+
if self.filter_params.search_term is not None:
|
213
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
214
|
+
if self.filter_params.name is not None:
|
215
|
+
params["filter[name]"] = self.filter_params.name
|
216
|
+
if self.filter_params.endpoint_ids is not None:
|
217
|
+
params["filter[endpointIds]"] = ",".join(self.filter_params.endpoint_ids)
|
218
|
+
if self.filter_params.execution_type is not None:
|
219
|
+
params["filter[executionType]"] = self.filter_params.execution_type
|
220
|
+
if self.filter_params.status is not None:
|
221
|
+
params["filter[status]"] = self.filter_params.status
|
222
|
+
if self.filter_params.type is not None:
|
223
|
+
params["filter[type]"] = self.filter_params.type
|
224
|
+
if self.filter_params.asset_names is not None:
|
225
|
+
params["filter[assetNames]"] = self.filter_params.asset_names
|
226
|
+
if self.filter_params.started_by is not None:
|
227
|
+
params["filter[startedBy]"] = self.filter_params.started_by
|
228
|
+
|
229
|
+
response = self.http_client.get(f"cases/{self.case_id}/tasks", params=params)
|
230
|
+
|
231
|
+
entities = response.get("result", {}).get("entities", [])
|
232
|
+
|
233
|
+
# Use Pydantic parsing with proper field aliasing
|
234
|
+
return [CaseTask.model_validate(entity_data) for entity_data in entities]
|
235
|
+
|
236
|
+
|
237
|
+
class GetCaseUsersQuery(Query[List[User]]):
|
238
|
+
"""Query to get users for a specific case."""
|
239
|
+
|
240
|
+
def __init__(self, http_client: HTTPClient, case_id: str, filter_params: Optional[CaseUserFilter] = None):
|
241
|
+
self.http_client = http_client
|
242
|
+
self.case_id = case_id
|
243
|
+
self.filter_params = filter_params or CaseUserFilter()
|
244
|
+
|
245
|
+
def execute(self) -> List[User]:
|
246
|
+
"""Execute the query to get case users."""
|
247
|
+
params = {}
|
248
|
+
|
249
|
+
# Add pagination parameters
|
250
|
+
if self.filter_params.page_number is not None:
|
251
|
+
params["pageNumber"] = self.filter_params.page_number
|
252
|
+
if self.filter_params.page_size is not None:
|
253
|
+
params["pageSize"] = self.filter_params.page_size
|
254
|
+
|
255
|
+
# Add sorting parameters
|
256
|
+
if self.filter_params.sort_by is not None:
|
257
|
+
params["sortBy"] = self.filter_params.sort_by
|
258
|
+
if self.filter_params.sort_type is not None:
|
259
|
+
params["sortType"] = self.filter_params.sort_type
|
260
|
+
|
261
|
+
# Add filter parameters
|
262
|
+
if self.filter_params.organization_ids is not None:
|
263
|
+
params["filter[organizationIds]"] = ",".join(map(str, self.filter_params.organization_ids))
|
264
|
+
else:
|
265
|
+
# Default to organization 0 if not specified
|
266
|
+
params["filter[organizationIds]"] = "0"
|
267
|
+
|
268
|
+
if self.filter_params.search_term is not None:
|
269
|
+
params["filter[searchTerm]"] = self.filter_params.search_term
|
270
|
+
|
271
|
+
response = self.http_client.get(f"cases/{self.case_id}/users", params=params)
|
272
|
+
|
273
|
+
entities = response.get("result", {}).get("entities", [])
|
274
|
+
|
275
|
+
# Use Pydantic parsing with proper field aliasing
|
276
|
+
return [User.model_validate(entity_data) for entity_data in entities]
|
277
|
+
|
278
|
+
|
279
|
+
class CheckCaseNameQuery(Query[bool]):
|
280
|
+
"""Query to check if a case name is available."""
|
281
|
+
|
282
|
+
def __init__(self, http_client: HTTPClient, name: str):
|
283
|
+
self.http_client = http_client
|
284
|
+
self.name = name
|
285
|
+
|
286
|
+
def execute(self) -> bool:
|
287
|
+
"""Execute the query to check case name availability."""
|
288
|
+
params = {"name": self.name}
|
289
|
+
|
290
|
+
response = self.http_client.get("cases/check", params=params)
|
291
|
+
|
292
|
+
# Return the actual result (whether name is taken/available)
|
293
293
|
return response.get("result", False)
|