binalyze-air-sdk 1.0.1__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 -0
- binalyze_air/apis/__init__.py +27 -0
- binalyze_air/apis/authentication.py +27 -0
- binalyze_air/apis/auto_asset_tags.py +75 -0
- binalyze_air/apis/endpoints.py +22 -0
- binalyze_air/apis/event_subscription.py +97 -0
- binalyze_air/apis/evidence.py +53 -0
- binalyze_air/apis/evidences.py +216 -0
- binalyze_air/apis/interact.py +36 -0
- binalyze_air/apis/params.py +40 -0
- binalyze_air/apis/settings.py +27 -0
- binalyze_air/apis/user_management.py +74 -0
- binalyze_air/apis/users.py +68 -0
- binalyze_air/apis/webhooks.py +231 -0
- binalyze_air/base.py +133 -0
- binalyze_air/client.py +1338 -0
- binalyze_air/commands/__init__.py +146 -0
- binalyze_air/commands/acquisitions.py +387 -0
- binalyze_air/commands/assets.py +363 -0
- binalyze_air/commands/authentication.py +37 -0
- binalyze_air/commands/auto_asset_tags.py +231 -0
- binalyze_air/commands/baseline.py +396 -0
- binalyze_air/commands/cases.py +603 -0
- binalyze_air/commands/event_subscription.py +102 -0
- binalyze_air/commands/evidences.py +988 -0
- binalyze_air/commands/interact.py +58 -0
- binalyze_air/commands/organizations.py +221 -0
- binalyze_air/commands/policies.py +203 -0
- binalyze_air/commands/settings.py +29 -0
- binalyze_air/commands/tasks.py +56 -0
- binalyze_air/commands/triage.py +360 -0
- binalyze_air/commands/user_management.py +126 -0
- binalyze_air/commands/users.py +101 -0
- binalyze_air/config.py +245 -0
- binalyze_air/exceptions.py +50 -0
- binalyze_air/http_client.py +306 -0
- binalyze_air/models/__init__.py +285 -0
- binalyze_air/models/acquisitions.py +251 -0
- binalyze_air/models/assets.py +439 -0
- binalyze_air/models/audit.py +273 -0
- binalyze_air/models/authentication.py +70 -0
- binalyze_air/models/auto_asset_tags.py +117 -0
- binalyze_air/models/baseline.py +232 -0
- binalyze_air/models/cases.py +276 -0
- binalyze_air/models/endpoints.py +76 -0
- binalyze_air/models/event_subscription.py +172 -0
- binalyze_air/models/evidence.py +66 -0
- binalyze_air/models/evidences.py +349 -0
- binalyze_air/models/interact.py +136 -0
- binalyze_air/models/organizations.py +294 -0
- binalyze_air/models/params.py +128 -0
- binalyze_air/models/policies.py +250 -0
- binalyze_air/models/settings.py +84 -0
- binalyze_air/models/tasks.py +149 -0
- binalyze_air/models/triage.py +143 -0
- binalyze_air/models/user_management.py +97 -0
- binalyze_air/models/users.py +82 -0
- binalyze_air/queries/__init__.py +134 -0
- binalyze_air/queries/acquisitions.py +156 -0
- binalyze_air/queries/assets.py +105 -0
- binalyze_air/queries/audit.py +417 -0
- binalyze_air/queries/authentication.py +56 -0
- binalyze_air/queries/auto_asset_tags.py +60 -0
- binalyze_air/queries/baseline.py +185 -0
- binalyze_air/queries/cases.py +293 -0
- binalyze_air/queries/endpoints.py +25 -0
- binalyze_air/queries/event_subscription.py +55 -0
- binalyze_air/queries/evidence.py +140 -0
- binalyze_air/queries/evidences.py +280 -0
- binalyze_air/queries/interact.py +28 -0
- binalyze_air/queries/organizations.py +223 -0
- binalyze_air/queries/params.py +115 -0
- binalyze_air/queries/policies.py +150 -0
- binalyze_air/queries/settings.py +20 -0
- binalyze_air/queries/tasks.py +82 -0
- binalyze_air/queries/triage.py +231 -0
- binalyze_air/queries/user_management.py +83 -0
- binalyze_air/queries/users.py +69 -0
- binalyze_air_sdk-1.0.1.dist-info/METADATA +635 -0
- binalyze_air_sdk-1.0.1.dist-info/RECORD +82 -0
- binalyze_air_sdk-1.0.1.dist-info/WHEEL +5 -0
- binalyze_air_sdk-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
"""
|
2
|
+
Authentication-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from ..base import Query
|
6
|
+
from ..models.authentication import AuthStatus, User, UserProfile, UserRole
|
7
|
+
from ..http_client import HTTPClient
|
8
|
+
|
9
|
+
|
10
|
+
class CheckAuthStatusQuery(Query[AuthStatus]):
|
11
|
+
"""Query to check authentication status."""
|
12
|
+
|
13
|
+
def __init__(self, http_client: HTTPClient):
|
14
|
+
self.http_client = http_client
|
15
|
+
|
16
|
+
def execute(self) -> AuthStatus:
|
17
|
+
"""Execute the auth status check query."""
|
18
|
+
# Try different endpoint patterns since auth endpoints may vary
|
19
|
+
endpoints_to_try = [
|
20
|
+
"auth/check", # Original pattern
|
21
|
+
# Note: /api/public/auth/check may not exist in all API versions
|
22
|
+
# The HTTP client will add the api_prefix automatically
|
23
|
+
]
|
24
|
+
|
25
|
+
last_exception = None
|
26
|
+
|
27
|
+
for endpoint in endpoints_to_try:
|
28
|
+
try:
|
29
|
+
response = self.http_client.get(endpoint)
|
30
|
+
|
31
|
+
if response.get("success"):
|
32
|
+
result = response.get("result", {})
|
33
|
+
|
34
|
+
# Use Pydantic to parse the User data with proper field aliasing
|
35
|
+
user = User.model_validate(result)
|
36
|
+
return AuthStatus(authenticated=True, user=user)
|
37
|
+
else:
|
38
|
+
# Return unauthenticated status for failed responses
|
39
|
+
return AuthStatus(authenticated=False)
|
40
|
+
|
41
|
+
except Exception as e:
|
42
|
+
last_exception = e
|
43
|
+
continue # Try next endpoint
|
44
|
+
|
45
|
+
# If all endpoints failed, check if it's because auth features are disabled
|
46
|
+
if last_exception and "auth-management-via-api" in str(last_exception):
|
47
|
+
# Auth features are disabled - this is a configuration issue, not authentication failure
|
48
|
+
# We can't determine auth status, so return a special state
|
49
|
+
return AuthStatus(authenticated=False)
|
50
|
+
|
51
|
+
# If we get here, all endpoints failed
|
52
|
+
if last_exception:
|
53
|
+
raise last_exception
|
54
|
+
|
55
|
+
# Return unauthenticated status as fallback
|
56
|
+
return AuthStatus(authenticated=False)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""
|
2
|
+
Auto Asset Tags-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.auto_asset_tags import AutoAssetTag, AutoAssetTagFilter
|
9
|
+
from ..http_client import HTTPClient
|
10
|
+
|
11
|
+
|
12
|
+
class ListAutoAssetTagsQuery(Query[List[AutoAssetTag]]):
|
13
|
+
"""Query to list auto asset tags."""
|
14
|
+
|
15
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[AutoAssetTagFilter] = None):
|
16
|
+
self.http_client = http_client
|
17
|
+
self.filter_params = filter_params
|
18
|
+
|
19
|
+
def execute(self) -> List[AutoAssetTag]:
|
20
|
+
"""Execute the list auto asset tags query."""
|
21
|
+
params = {}
|
22
|
+
|
23
|
+
# Add default organization filtering if no filter is provided
|
24
|
+
if self.filter_params:
|
25
|
+
params = self.filter_params.to_params()
|
26
|
+
else:
|
27
|
+
# Default organization filtering - critical for the API to work
|
28
|
+
params["filter[organizationIds]"] = "0"
|
29
|
+
|
30
|
+
# Ensure organization filtering is always present
|
31
|
+
if "filter[organizationIds]" not in params and "filter[organizationId]" not in params:
|
32
|
+
params["filter[organizationIds]"] = "0"
|
33
|
+
|
34
|
+
response = self.http_client.get("auto-asset-tag", params=params)
|
35
|
+
|
36
|
+
if response.get("success"):
|
37
|
+
tags_data = response.get("result", {}).get("entities", [])
|
38
|
+
# Use Pydantic parsing with proper field aliasing
|
39
|
+
return [AutoAssetTag.model_validate(tag_data) for tag_data in tags_data]
|
40
|
+
|
41
|
+
return []
|
42
|
+
|
43
|
+
|
44
|
+
class GetAutoAssetTagQuery(Query[AutoAssetTag]):
|
45
|
+
"""Query to get auto asset tag by ID."""
|
46
|
+
|
47
|
+
def __init__(self, http_client: HTTPClient, tag_id: str):
|
48
|
+
self.http_client = http_client
|
49
|
+
self.tag_id = tag_id
|
50
|
+
|
51
|
+
def execute(self) -> AutoAssetTag:
|
52
|
+
"""Execute the get auto asset tag query."""
|
53
|
+
response = self.http_client.get(f"auto-asset-tag/{self.tag_id}")
|
54
|
+
|
55
|
+
if response.get("success"):
|
56
|
+
tag_data = response.get("result", {})
|
57
|
+
# Use Pydantic parsing with proper field aliasing
|
58
|
+
return AutoAssetTag.model_validate(tag_data)
|
59
|
+
|
60
|
+
raise Exception(f"Auto asset tag not found: {self.tag_id}")
|
@@ -0,0 +1,185 @@
|
|
1
|
+
"""
|
2
|
+
Baseline-related queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.baseline import (
|
9
|
+
Baseline, BaselineProfile, BaselineComparison, BaselineChange,
|
10
|
+
BaselineSchedule, BaselineFilter, BaselineStatus, ComparisonStatus
|
11
|
+
)
|
12
|
+
from ..http_client import HTTPClient
|
13
|
+
|
14
|
+
|
15
|
+
class ListBaselinesQuery(Query[List[Baseline]]):
|
16
|
+
"""Query to list baselines with optional filtering."""
|
17
|
+
|
18
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[BaselineFilter] = None, organization_ids: Optional[List[int]] = None):
|
19
|
+
self.http_client = http_client
|
20
|
+
self.filter_params = filter_params or BaselineFilter()
|
21
|
+
self.organization_ids = organization_ids or [0]
|
22
|
+
|
23
|
+
def execute(self) -> List[Baseline]:
|
24
|
+
"""Execute the query to list baselines."""
|
25
|
+
params = {}
|
26
|
+
if self.filter_params:
|
27
|
+
params = self.filter_params.model_dump(exclude_none=True)
|
28
|
+
|
29
|
+
response = self.http_client.get("baselines", params=params)
|
30
|
+
|
31
|
+
if response.get("success"):
|
32
|
+
baselines_data = response.get("result", {}).get("entities", [])
|
33
|
+
# Use Pydantic parsing with proper field aliasing
|
34
|
+
return [Baseline.model_validate(baseline_data) for baseline_data in baselines_data]
|
35
|
+
|
36
|
+
return []
|
37
|
+
|
38
|
+
|
39
|
+
class GetBaselineQuery(Query[Baseline]):
|
40
|
+
"""Query to get a specific baseline by ID."""
|
41
|
+
|
42
|
+
def __init__(self, http_client: HTTPClient, baseline_id: str):
|
43
|
+
self.http_client = http_client
|
44
|
+
self.baseline_id = baseline_id
|
45
|
+
|
46
|
+
def execute(self) -> Baseline:
|
47
|
+
"""Execute the query to get baseline details."""
|
48
|
+
response = self.http_client.get(f"baselines/{self.baseline_id}")
|
49
|
+
|
50
|
+
if response.get("success"):
|
51
|
+
baseline_data = response.get("result", {})
|
52
|
+
# Use Pydantic parsing with proper field aliasing
|
53
|
+
return Baseline.model_validate(baseline_data)
|
54
|
+
|
55
|
+
raise Exception(f"Baseline not found: {self.baseline_id}")
|
56
|
+
|
57
|
+
|
58
|
+
class GetBaselineComparisonsQuery(Query[List[BaselineComparison]]):
|
59
|
+
"""Query to get baseline comparisons."""
|
60
|
+
|
61
|
+
def __init__(self, http_client: HTTPClient, baseline_id: str):
|
62
|
+
self.http_client = http_client
|
63
|
+
self.baseline_id = baseline_id
|
64
|
+
|
65
|
+
def execute(self) -> List[BaselineComparison]:
|
66
|
+
"""Execute the query to get baseline comparisons."""
|
67
|
+
response = self.http_client.get(f"baselines/{self.baseline_id}/comparisons")
|
68
|
+
|
69
|
+
if response.get("success"):
|
70
|
+
comparisons_data = response.get("result", {}).get("entities", [])
|
71
|
+
comparisons = []
|
72
|
+
for comparison_data in comparisons_data:
|
73
|
+
# Parse changes if present
|
74
|
+
changes = []
|
75
|
+
for change_data in comparison_data.get("changes", []):
|
76
|
+
# Use Pydantic parsing for changes
|
77
|
+
changes.append(BaselineChange.model_validate(change_data))
|
78
|
+
|
79
|
+
# Add parsed changes to comparison data
|
80
|
+
comparison_data["changes"] = changes
|
81
|
+
|
82
|
+
# Use Pydantic parsing with proper field aliasing
|
83
|
+
comparisons.append(BaselineComparison.model_validate(comparison_data))
|
84
|
+
|
85
|
+
return comparisons
|
86
|
+
|
87
|
+
return []
|
88
|
+
|
89
|
+
|
90
|
+
class GetBaselineComparisonQuery(Query[BaselineComparison]):
|
91
|
+
"""Query to get a specific baseline comparison by ID."""
|
92
|
+
|
93
|
+
def __init__(self, http_client: HTTPClient, comparison_id: str):
|
94
|
+
self.http_client = http_client
|
95
|
+
self.comparison_id = comparison_id
|
96
|
+
|
97
|
+
def execute(self) -> BaselineComparison:
|
98
|
+
"""Execute the query to get baseline comparison details."""
|
99
|
+
response = self.http_client.get(f"baselines/comparisons/{self.comparison_id}")
|
100
|
+
|
101
|
+
if response.get("success"):
|
102
|
+
comparison_data = response.get("result", {})
|
103
|
+
|
104
|
+
# Parse changes if present
|
105
|
+
changes = []
|
106
|
+
for change_data in comparison_data.get("changes", []):
|
107
|
+
# Use Pydantic parsing for changes
|
108
|
+
changes.append(BaselineChange.model_validate(change_data))
|
109
|
+
|
110
|
+
# Add parsed changes to comparison data
|
111
|
+
comparison_data["changes"] = changes
|
112
|
+
|
113
|
+
# Use Pydantic parsing with proper field aliasing
|
114
|
+
return BaselineComparison.model_validate(comparison_data)
|
115
|
+
|
116
|
+
raise Exception(f"Baseline comparison not found: {self.comparison_id}")
|
117
|
+
|
118
|
+
|
119
|
+
class ListBaselineProfilesQuery(Query[List[BaselineProfile]]):
|
120
|
+
"""Query to list baseline profiles."""
|
121
|
+
|
122
|
+
def __init__(self, http_client: HTTPClient, organization_ids: Optional[List[int]] = None):
|
123
|
+
self.http_client = http_client
|
124
|
+
self.organization_ids = organization_ids or [0]
|
125
|
+
|
126
|
+
def execute(self) -> List[BaselineProfile]:
|
127
|
+
"""Execute the query to list baseline profiles."""
|
128
|
+
params = {
|
129
|
+
"filter[organizationIds]": ",".join(map(str, self.organization_ids))
|
130
|
+
}
|
131
|
+
|
132
|
+
response = self.http_client.get("baseline-profiles", params=params)
|
133
|
+
|
134
|
+
if response.get("success"):
|
135
|
+
profiles_data = response.get("result", {}).get("entities", [])
|
136
|
+
# Use Pydantic parsing with proper field aliasing
|
137
|
+
return [BaselineProfile.model_validate(profile_data) for profile_data in profiles_data]
|
138
|
+
|
139
|
+
return []
|
140
|
+
|
141
|
+
|
142
|
+
class GetBaselineProfileQuery(Query[BaselineProfile]):
|
143
|
+
"""Query to get a specific baseline profile by ID."""
|
144
|
+
|
145
|
+
def __init__(self, http_client: HTTPClient, profile_id: str):
|
146
|
+
self.http_client = http_client
|
147
|
+
self.profile_id = profile_id
|
148
|
+
|
149
|
+
def execute(self) -> BaselineProfile:
|
150
|
+
"""Execute the query to get baseline profile details."""
|
151
|
+
response = self.http_client.get(f"baseline-profiles/{self.profile_id}")
|
152
|
+
|
153
|
+
if response.get("success"):
|
154
|
+
profile_data = response.get("result", {})
|
155
|
+
# Use Pydantic parsing with proper field aliasing
|
156
|
+
return BaselineProfile.model_validate(profile_data)
|
157
|
+
|
158
|
+
raise Exception(f"Baseline profile not found: {self.profile_id}")
|
159
|
+
|
160
|
+
|
161
|
+
class GetBaselineSchedulesQuery(Query[List[BaselineSchedule]]):
|
162
|
+
"""Query to get baseline schedules."""
|
163
|
+
|
164
|
+
def __init__(self, http_client: HTTPClient, baseline_id: Optional[str] = None, organization_ids: Optional[List[int]] = None):
|
165
|
+
self.http_client = http_client
|
166
|
+
self.baseline_id = baseline_id
|
167
|
+
self.organization_ids = organization_ids or [0]
|
168
|
+
|
169
|
+
def execute(self) -> List[BaselineSchedule]:
|
170
|
+
"""Execute the query to get baseline schedules."""
|
171
|
+
params = {
|
172
|
+
"filter[organizationIds]": ",".join(map(str, self.organization_ids))
|
173
|
+
}
|
174
|
+
|
175
|
+
if self.baseline_id:
|
176
|
+
params["baselineId"] = self.baseline_id
|
177
|
+
|
178
|
+
response = self.http_client.get("baseline-schedules", params=params)
|
179
|
+
|
180
|
+
if response.get("success"):
|
181
|
+
schedules_data = response.get("result", {}).get("entities", [])
|
182
|
+
# Use Pydantic parsing with proper field aliasing
|
183
|
+
return [BaselineSchedule.model_validate(schedule_data) for schedule_data in schedules_data]
|
184
|
+
|
185
|
+
return []
|
@@ -0,0 +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)
|
293
|
+
return response.get("result", False)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
"""
|
2
|
+
Endpoints queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.endpoints import EndpointTag, EndpointTagFilter
|
9
|
+
from ..http_client import HTTPClient
|
10
|
+
|
11
|
+
|
12
|
+
class GetEndpointTagsQuery(Query[List[EndpointTag]]):
|
13
|
+
"""Query to get endpoint tags."""
|
14
|
+
|
15
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[EndpointTagFilter] = None):
|
16
|
+
self.http_client = http_client
|
17
|
+
self.filter_params = filter_params or EndpointTagFilter()
|
18
|
+
|
19
|
+
def execute(self) -> List[EndpointTag]:
|
20
|
+
"""Execute the query to get endpoint tags."""
|
21
|
+
params = self.filter_params.to_params()
|
22
|
+
response = self.http_client.get("endpoints/tags", params=params)
|
23
|
+
|
24
|
+
entities = response.get("result", [])
|
25
|
+
return [EndpointTag(**item) for item in entities]
|
@@ -0,0 +1,55 @@
|
|
1
|
+
"""
|
2
|
+
Event Subscription queries for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from ..base import Query
|
8
|
+
from ..models.event_subscription import EventSubscription, EventSubscriptionFilter
|
9
|
+
from ..http_client import HTTPClient
|
10
|
+
|
11
|
+
|
12
|
+
class ListEventSubscriptionsQuery(Query[List[EventSubscription]]):
|
13
|
+
"""Query to list event subscriptions."""
|
14
|
+
|
15
|
+
def __init__(self, http_client: HTTPClient, filter_params: Optional[EventSubscriptionFilter] = None):
|
16
|
+
self.http_client = http_client
|
17
|
+
self.filter_params = filter_params or EventSubscriptionFilter()
|
18
|
+
|
19
|
+
def execute(self) -> List[EventSubscription]:
|
20
|
+
"""Execute the query to get event subscriptions."""
|
21
|
+
params = self.filter_params.to_params()
|
22
|
+
response = self.http_client.get("event-subscription", params=params)
|
23
|
+
|
24
|
+
entities = response.get("result", {}).get("entities", [])
|
25
|
+
|
26
|
+
# Use Pydantic parsing with proper field aliasing
|
27
|
+
subscriptions = []
|
28
|
+
for item in entities:
|
29
|
+
# Convert id to string as Pydantic expects
|
30
|
+
if "id" in item:
|
31
|
+
item["id"] = str(item["id"])
|
32
|
+
subscriptions.append(EventSubscription.model_validate(item))
|
33
|
+
|
34
|
+
return subscriptions
|
35
|
+
|
36
|
+
|
37
|
+
class GetEventSubscriptionQuery(Query[EventSubscription]):
|
38
|
+
"""Query to get a specific event subscription."""
|
39
|
+
|
40
|
+
def __init__(self, http_client: HTTPClient, subscription_id: str):
|
41
|
+
self.http_client = http_client
|
42
|
+
self.subscription_id = subscription_id
|
43
|
+
|
44
|
+
def execute(self) -> EventSubscription:
|
45
|
+
"""Execute the query to get a specific event subscription."""
|
46
|
+
response = self.http_client.get(f"event-subscription/{self.subscription_id}")
|
47
|
+
|
48
|
+
entity_data = response.get("result", {})
|
49
|
+
|
50
|
+
# Convert id to string as Pydantic expects
|
51
|
+
if "id" in entity_data:
|
52
|
+
entity_data["id"] = str(entity_data["id"])
|
53
|
+
|
54
|
+
# Use Pydantic parsing with proper field aliasing
|
55
|
+
return EventSubscription.model_validate(entity_data)
|