binalyze-air-sdk 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. binalyze_air/__init__.py +77 -77
  2. binalyze_air/apis/__init__.py +67 -27
  3. binalyze_air/apis/acquisitions.py +107 -0
  4. binalyze_air/apis/api_tokens.py +49 -0
  5. binalyze_air/apis/assets.py +161 -0
  6. binalyze_air/apis/audit_logs.py +26 -0
  7. binalyze_air/apis/{authentication.py → auth.py} +29 -27
  8. binalyze_air/apis/auto_asset_tags.py +79 -75
  9. binalyze_air/apis/backup.py +177 -0
  10. binalyze_air/apis/baseline.py +46 -0
  11. binalyze_air/apis/cases.py +225 -0
  12. binalyze_air/apis/cloud_forensics.py +116 -0
  13. binalyze_air/apis/event_subscription.py +96 -96
  14. binalyze_air/apis/evidence.py +249 -53
  15. binalyze_air/apis/interact.py +153 -36
  16. binalyze_air/apis/investigation_hub.py +234 -0
  17. binalyze_air/apis/license.py +104 -0
  18. binalyze_air/apis/logger.py +83 -0
  19. binalyze_air/apis/multipart_upload.py +201 -0
  20. binalyze_air/apis/notifications.py +115 -0
  21. binalyze_air/apis/organizations.py +267 -0
  22. binalyze_air/apis/params.py +44 -39
  23. binalyze_air/apis/policies.py +186 -0
  24. binalyze_air/apis/preset_filters.py +79 -0
  25. binalyze_air/apis/recent_activities.py +71 -0
  26. binalyze_air/apis/relay_server.py +104 -0
  27. binalyze_air/apis/settings.py +395 -27
  28. binalyze_air/apis/tasks.py +80 -0
  29. binalyze_air/apis/triage.py +197 -0
  30. binalyze_air/apis/user_management.py +183 -74
  31. binalyze_air/apis/webhook_executions.py +50 -0
  32. binalyze_air/apis/webhooks.py +322 -230
  33. binalyze_air/base.py +207 -133
  34. binalyze_air/client.py +217 -1337
  35. binalyze_air/commands/__init__.py +175 -145
  36. binalyze_air/commands/acquisitions.py +661 -387
  37. binalyze_air/commands/api_tokens.py +55 -0
  38. binalyze_air/commands/assets.py +324 -362
  39. binalyze_air/commands/{authentication.py → auth.py} +36 -36
  40. binalyze_air/commands/auto_asset_tags.py +230 -230
  41. binalyze_air/commands/backup.py +47 -0
  42. binalyze_air/commands/baseline.py +32 -396
  43. binalyze_air/commands/cases.py +609 -602
  44. binalyze_air/commands/cloud_forensics.py +88 -0
  45. binalyze_air/commands/event_subscription.py +101 -101
  46. binalyze_air/commands/evidences.py +918 -988
  47. binalyze_air/commands/interact.py +172 -58
  48. binalyze_air/commands/investigation_hub.py +315 -0
  49. binalyze_air/commands/license.py +183 -0
  50. binalyze_air/commands/logger.py +126 -0
  51. binalyze_air/commands/multipart_upload.py +363 -0
  52. binalyze_air/commands/notifications.py +45 -0
  53. binalyze_air/commands/organizations.py +200 -221
  54. binalyze_air/commands/policies.py +175 -203
  55. binalyze_air/commands/preset_filters.py +55 -0
  56. binalyze_air/commands/recent_activities.py +32 -0
  57. binalyze_air/commands/relay_server.py +144 -0
  58. binalyze_air/commands/settings.py +431 -29
  59. binalyze_air/commands/tasks.py +95 -56
  60. binalyze_air/commands/triage.py +224 -360
  61. binalyze_air/commands/user_management.py +351 -126
  62. binalyze_air/commands/webhook_executions.py +77 -0
  63. binalyze_air/config.py +244 -244
  64. binalyze_air/exceptions.py +49 -49
  65. binalyze_air/http_client.py +426 -305
  66. binalyze_air/models/__init__.py +287 -285
  67. binalyze_air/models/acquisitions.py +365 -250
  68. binalyze_air/models/api_tokens.py +73 -0
  69. binalyze_air/models/assets.py +438 -438
  70. binalyze_air/models/audit.py +247 -272
  71. binalyze_air/models/audit_logs.py +14 -0
  72. binalyze_air/models/{authentication.py → auth.py} +69 -69
  73. binalyze_air/models/auto_asset_tags.py +227 -116
  74. binalyze_air/models/backup.py +138 -0
  75. binalyze_air/models/baseline.py +231 -231
  76. binalyze_air/models/cases.py +275 -275
  77. binalyze_air/models/cloud_forensics.py +145 -0
  78. binalyze_air/models/event_subscription.py +170 -171
  79. binalyze_air/models/evidence.py +65 -65
  80. binalyze_air/models/evidences.py +367 -348
  81. binalyze_air/models/interact.py +266 -135
  82. binalyze_air/models/investigation_hub.py +265 -0
  83. binalyze_air/models/license.py +150 -0
  84. binalyze_air/models/logger.py +83 -0
  85. binalyze_air/models/multipart_upload.py +352 -0
  86. binalyze_air/models/notifications.py +138 -0
  87. binalyze_air/models/organizations.py +293 -293
  88. binalyze_air/models/params.py +153 -127
  89. binalyze_air/models/policies.py +260 -249
  90. binalyze_air/models/preset_filters.py +79 -0
  91. binalyze_air/models/recent_activities.py +70 -0
  92. binalyze_air/models/relay_server.py +121 -0
  93. binalyze_air/models/settings.py +538 -84
  94. binalyze_air/models/tasks.py +215 -149
  95. binalyze_air/models/triage.py +141 -142
  96. binalyze_air/models/user_management.py +200 -97
  97. binalyze_air/models/webhook_executions.py +33 -0
  98. binalyze_air/queries/__init__.py +121 -133
  99. binalyze_air/queries/acquisitions.py +155 -155
  100. binalyze_air/queries/api_tokens.py +46 -0
  101. binalyze_air/queries/assets.py +186 -105
  102. binalyze_air/queries/audit.py +400 -416
  103. binalyze_air/queries/{authentication.py → auth.py} +55 -55
  104. binalyze_air/queries/auto_asset_tags.py +59 -59
  105. binalyze_air/queries/backup.py +66 -0
  106. binalyze_air/queries/baseline.py +21 -185
  107. binalyze_air/queries/cases.py +292 -292
  108. binalyze_air/queries/cloud_forensics.py +137 -0
  109. binalyze_air/queries/event_subscription.py +54 -54
  110. binalyze_air/queries/evidence.py +139 -139
  111. binalyze_air/queries/evidences.py +279 -279
  112. binalyze_air/queries/interact.py +140 -28
  113. binalyze_air/queries/investigation_hub.py +329 -0
  114. binalyze_air/queries/license.py +85 -0
  115. binalyze_air/queries/logger.py +58 -0
  116. binalyze_air/queries/multipart_upload.py +180 -0
  117. binalyze_air/queries/notifications.py +71 -0
  118. binalyze_air/queries/organizations.py +222 -222
  119. binalyze_air/queries/params.py +154 -115
  120. binalyze_air/queries/policies.py +149 -149
  121. binalyze_air/queries/preset_filters.py +60 -0
  122. binalyze_air/queries/recent_activities.py +44 -0
  123. binalyze_air/queries/relay_server.py +42 -0
  124. binalyze_air/queries/settings.py +533 -20
  125. binalyze_air/queries/tasks.py +125 -81
  126. binalyze_air/queries/triage.py +230 -230
  127. binalyze_air/queries/user_management.py +193 -83
  128. binalyze_air/queries/webhook_executions.py +39 -0
  129. binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
  130. binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
  131. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
  132. binalyze_air/apis/endpoints.py +0 -22
  133. binalyze_air/apis/evidences.py +0 -216
  134. binalyze_air/apis/users.py +0 -68
  135. binalyze_air/commands/users.py +0 -101
  136. binalyze_air/models/endpoints.py +0 -76
  137. binalyze_air/models/users.py +0 -82
  138. binalyze_air/queries/endpoints.py +0 -25
  139. binalyze_air/queries/users.py +0 -69
  140. binalyze_air_sdk-1.0.1.dist-info/METADATA +0 -635
  141. binalyze_air_sdk-1.0.1.dist-info/RECORD +0 -82
  142. {binalyze_air_sdk-1.0.1.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
@@ -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)