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
@@ -0,0 +1,201 @@
|
|
1
|
+
"""Multipart Upload API client for Binalyze AIR SDK."""
|
2
|
+
|
3
|
+
from typing import Optional, Dict, Any, Callable
|
4
|
+
from ..http_client import HTTPClient
|
5
|
+
from ..models.multipart_upload import (
|
6
|
+
UploadInitializeResponse, UploadPartResponse, UploadStatusResponse,
|
7
|
+
UploadFinalizeResponse, MultipartUploadSession
|
8
|
+
)
|
9
|
+
from ..queries.multipart_upload import (
|
10
|
+
InitializeUploadQuery, UploadPartQuery, CheckUploadStatusQuery, FinalizeUploadQuery
|
11
|
+
)
|
12
|
+
from ..commands.multipart_upload import (
|
13
|
+
InitializeUploadCommand, UploadPartCommand, CheckUploadStatusCommand,
|
14
|
+
FinalizeUploadCommand, CompleteFileUploadCommand, AbortUploadCommand
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
class MultipartUploadAPI:
|
19
|
+
"""Multipart Upload API with CQRS pattern - separated queries and commands.
|
20
|
+
|
21
|
+
This class provides methods for uploading large files using multipart upload,
|
22
|
+
which allows for resumable uploads and better handling of large files.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, http_client: HTTPClient):
|
26
|
+
"""Initialize Multipart Upload API client.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
http_client: HTTP client for making API requests
|
30
|
+
"""
|
31
|
+
self.http_client = http_client
|
32
|
+
|
33
|
+
# QUERIES (Read operations)
|
34
|
+
def check_upload_status(self, file_id: str) -> UploadStatusResponse:
|
35
|
+
"""Check if upload is ready to be finalized.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
file_id: Upload session identifier
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
UploadStatusResponse with ready status
|
42
|
+
|
43
|
+
Raises:
|
44
|
+
APIError: If the request fails
|
45
|
+
"""
|
46
|
+
command = CheckUploadStatusCommand(self, file_id)
|
47
|
+
return command.execute()
|
48
|
+
|
49
|
+
# COMMANDS (Write operations)
|
50
|
+
def initialize_upload(self) -> UploadInitializeResponse:
|
51
|
+
"""Initialize a new multipart upload session.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
UploadInitializeResponse with file ID for the session
|
55
|
+
|
56
|
+
Raises:
|
57
|
+
APIError: If the request fails
|
58
|
+
"""
|
59
|
+
command = InitializeUploadCommand(self)
|
60
|
+
return command.execute()
|
61
|
+
|
62
|
+
def upload_part(self, file_id: str, part_number: int, file_data: bytes, filename: Optional[str] = None) -> UploadPartResponse:
|
63
|
+
"""Upload a file part in a multipart upload.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
file_id: Upload session identifier
|
67
|
+
part_number: Sequential part number (starting from 1)
|
68
|
+
file_data: Binary file data for this part
|
69
|
+
filename: Optional filename for the part
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
UploadPartResponse indicating success/failure
|
73
|
+
|
74
|
+
Raises:
|
75
|
+
APIError: If the request fails
|
76
|
+
"""
|
77
|
+
command = UploadPartCommand(self, file_id, part_number, file_data, filename)
|
78
|
+
return command.execute()
|
79
|
+
|
80
|
+
def finalize_upload(self, file_id: str) -> UploadFinalizeResponse:
|
81
|
+
"""Finalize a multipart upload session.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
file_id: Upload session identifier to finalize
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
UploadFinalizeResponse indicating success/failure
|
88
|
+
|
89
|
+
Raises:
|
90
|
+
APIError: If the request fails
|
91
|
+
"""
|
92
|
+
command = FinalizeUploadCommand(self, file_id)
|
93
|
+
return command.execute()
|
94
|
+
|
95
|
+
def abort_upload(self, file_id: str) -> Dict[str, Any]:
|
96
|
+
"""Abort a multipart upload session and discard all uploaded parts.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
file_id: Upload session identifier to abort
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Dictionary with API response
|
103
|
+
"""
|
104
|
+
command = AbortUploadCommand(self, file_id)
|
105
|
+
return command.execute()
|
106
|
+
|
107
|
+
def upload_file(
|
108
|
+
self,
|
109
|
+
file_path: str,
|
110
|
+
chunk_size: int = 5 * 1024 * 1024,
|
111
|
+
progress_callback: Optional[Callable[[float, int, int], None]] = None
|
112
|
+
) -> Dict[str, Any]:
|
113
|
+
"""Upload a complete file using multipart upload.
|
114
|
+
|
115
|
+
This method handles the entire upload process automatically:
|
116
|
+
1. Initialize upload session
|
117
|
+
2. Upload all file parts
|
118
|
+
3. Check upload status
|
119
|
+
4. Finalize upload
|
120
|
+
|
121
|
+
Args:
|
122
|
+
file_path: Path to the file to upload
|
123
|
+
chunk_size: Size of each chunk in bytes (default 5MB)
|
124
|
+
progress_callback: Optional callback for progress updates (percentage, current_part, total_parts)
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
Dictionary with upload results and session information
|
128
|
+
|
129
|
+
Raises:
|
130
|
+
APIError: If any step of the upload fails
|
131
|
+
FileNotFoundError: If the file doesn't exist
|
132
|
+
"""
|
133
|
+
command = CompleteFileUploadCommand(self, file_path, chunk_size, progress_callback)
|
134
|
+
return command.execute()
|
135
|
+
|
136
|
+
# Low-level query methods (for internal use by commands)
|
137
|
+
def _initialize_upload_query(self, query: InitializeUploadQuery) -> Dict[str, Any]:
|
138
|
+
"""Execute initialize upload query (internal use).
|
139
|
+
|
140
|
+
Args:
|
141
|
+
query: InitializeUploadQuery instance
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
API response dictionary
|
145
|
+
"""
|
146
|
+
body = query.build_body()
|
147
|
+
return self.http_client.post('multipart-upload/initialize', json_data=body)
|
148
|
+
|
149
|
+
def _upload_part_query(self, query: UploadPartQuery) -> Dict[str, Any]:
|
150
|
+
"""Execute upload part query (internal use).
|
151
|
+
|
152
|
+
Args:
|
153
|
+
query: UploadPartQuery instance
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
API response dictionary
|
157
|
+
"""
|
158
|
+
form_data = query.build_form_data()
|
159
|
+
|
160
|
+
# Prepare files and data for multipart upload
|
161
|
+
# Use the filename from the request, or default to 'part.bin'
|
162
|
+
filename = getattr(query._request, 'filename', None) or 'part.bin'
|
163
|
+
files = {
|
164
|
+
'file': (filename, form_data['file'], 'application/octet-stream')
|
165
|
+
}
|
166
|
+
|
167
|
+
data = {
|
168
|
+
'fileId': form_data['fileId'],
|
169
|
+
'partNumber': form_data['partNumber']
|
170
|
+
}
|
171
|
+
|
172
|
+
return self.http_client.upload_multipart(
|
173
|
+
'multipart-upload/upload-part',
|
174
|
+
files=files,
|
175
|
+
data=data,
|
176
|
+
method='PUT'
|
177
|
+
)
|
178
|
+
|
179
|
+
def _check_upload_status_query(self, query: CheckUploadStatusQuery) -> Dict[str, Any]:
|
180
|
+
"""Execute check upload status query (internal use).
|
181
|
+
|
182
|
+
Args:
|
183
|
+
query: CheckUploadStatusQuery instance
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
API response dictionary
|
187
|
+
"""
|
188
|
+
params = query.build_params()
|
189
|
+
return self.http_client.get('multipart-upload/is-ready', params=params)
|
190
|
+
|
191
|
+
def _finalize_upload_query(self, query: FinalizeUploadQuery) -> Dict[str, Any]:
|
192
|
+
"""Execute finalize upload query (internal use).
|
193
|
+
|
194
|
+
Args:
|
195
|
+
query: FinalizeUploadQuery instance
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
API response dictionary
|
199
|
+
"""
|
200
|
+
body = query.build_body()
|
201
|
+
return self.http_client.post('multipart-upload/finalize', json_data=body)
|
@@ -0,0 +1,115 @@
|
|
1
|
+
"""
|
2
|
+
Notifications API for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Optional, Union, Dict, Any
|
6
|
+
|
7
|
+
from ..http_client import HTTPClient
|
8
|
+
from ..models.notifications import Notification, NotificationsList, NotificationsFilter, NotificationType, NotificationLevel
|
9
|
+
from ..queries.notifications import GetNotificationsQuery, GetNotificationByIdQuery
|
10
|
+
from ..commands.notifications import DeleteAllNotificationsCommand, MarkAllAsReadCommand, MarkAsReadByIdCommand
|
11
|
+
|
12
|
+
|
13
|
+
class NotificationsAPI:
|
14
|
+
"""Notifications API with CQRS pattern - separated queries and commands."""
|
15
|
+
|
16
|
+
def __init__(self, http_client: HTTPClient):
|
17
|
+
self.http_client = http_client
|
18
|
+
|
19
|
+
# QUERIES (Read operations)
|
20
|
+
def get_notifications(self, filter_params: Optional[NotificationsFilter] = None) -> NotificationsList:
|
21
|
+
"""Get notifications with optional filtering."""
|
22
|
+
query = GetNotificationsQuery(self.http_client, filter_params)
|
23
|
+
return query.execute()
|
24
|
+
|
25
|
+
def get_notification_by_id(self, notification_id: Union[int, str]) -> Optional[Notification]:
|
26
|
+
"""Get a specific notification by ID."""
|
27
|
+
query = GetNotificationByIdQuery(self.http_client, str(notification_id))
|
28
|
+
return query.execute()
|
29
|
+
|
30
|
+
# Convenience methods for common queries
|
31
|
+
def get_unread_notifications(self, organization_ids: Union[int, list] = 0) -> NotificationsList:
|
32
|
+
"""Get unread notifications."""
|
33
|
+
# Create filter with explicit field values
|
34
|
+
filter_params = NotificationsFilter()
|
35
|
+
filter_params.is_read = False
|
36
|
+
if isinstance(organization_ids, list):
|
37
|
+
filter_params.organization_ids = organization_ids
|
38
|
+
else:
|
39
|
+
filter_params.organization_ids = organization_ids
|
40
|
+
|
41
|
+
return self.get_notifications(filter_params)
|
42
|
+
|
43
|
+
def get_notifications_by_type(self, notification_type: Union[str, NotificationType], organization_ids: Union[int, list] = 0) -> NotificationsList:
|
44
|
+
"""Get notifications by type."""
|
45
|
+
# Create filter with explicit field values
|
46
|
+
filter_params = NotificationsFilter()
|
47
|
+
if isinstance(notification_type, str):
|
48
|
+
filter_params.type = NotificationType(notification_type)
|
49
|
+
else:
|
50
|
+
filter_params.type = notification_type
|
51
|
+
if isinstance(organization_ids, list):
|
52
|
+
filter_params.organization_ids = organization_ids
|
53
|
+
else:
|
54
|
+
filter_params.organization_ids = organization_ids
|
55
|
+
|
56
|
+
return self.get_notifications(filter_params)
|
57
|
+
|
58
|
+
def get_notifications_by_level(self, level: Union[str, NotificationLevel], organization_ids: Union[int, list] = 0) -> NotificationsList:
|
59
|
+
"""Get notifications by level."""
|
60
|
+
# Create filter with explicit field values
|
61
|
+
filter_params = NotificationsFilter()
|
62
|
+
if isinstance(level, str):
|
63
|
+
filter_params.level = NotificationLevel(level)
|
64
|
+
else:
|
65
|
+
filter_params.level = level
|
66
|
+
if isinstance(organization_ids, list):
|
67
|
+
filter_params.organization_ids = organization_ids
|
68
|
+
else:
|
69
|
+
filter_params.organization_ids = organization_ids
|
70
|
+
|
71
|
+
return self.get_notifications(filter_params)
|
72
|
+
|
73
|
+
# COMMANDS (Write operations)
|
74
|
+
def delete_all_notifications(self) -> Dict[str, Any]:
|
75
|
+
"""Delete all notifications for the current user."""
|
76
|
+
command = DeleteAllNotificationsCommand(self.http_client)
|
77
|
+
return command.execute()
|
78
|
+
|
79
|
+
def mark_all_as_read(self) -> Dict[str, Any]:
|
80
|
+
"""Mark all notifications as read."""
|
81
|
+
command = MarkAllAsReadCommand(self.http_client)
|
82
|
+
return command.execute()
|
83
|
+
|
84
|
+
def mark_as_read_by_id(self, notification_id: Union[int, str]) -> Dict[str, Any]:
|
85
|
+
"""Mark a specific notification as read by ID."""
|
86
|
+
command = MarkAsReadByIdCommand(self.http_client, notification_id)
|
87
|
+
return command.execute()
|
88
|
+
|
89
|
+
# Convenience methods
|
90
|
+
def get_notification_count(self, organization_ids: Union[int, list] = 0) -> int:
|
91
|
+
"""Get total notification count."""
|
92
|
+
# Create filter with explicit field values
|
93
|
+
filter_params = NotificationsFilter()
|
94
|
+
if isinstance(organization_ids, list):
|
95
|
+
filter_params.organization_ids = organization_ids
|
96
|
+
else:
|
97
|
+
filter_params.organization_ids = organization_ids
|
98
|
+
filter_params.page_size = 1 # We only need the count
|
99
|
+
|
100
|
+
result = self.get_notifications(filter_params)
|
101
|
+
return result.total_entity_count
|
102
|
+
|
103
|
+
def get_unread_count(self, organization_ids: Union[int, list] = 0) -> int:
|
104
|
+
"""Get unread notification count."""
|
105
|
+
# Create filter with explicit field values
|
106
|
+
filter_params = NotificationsFilter()
|
107
|
+
filter_params.is_read = False
|
108
|
+
if isinstance(organization_ids, list):
|
109
|
+
filter_params.organization_ids = organization_ids
|
110
|
+
else:
|
111
|
+
filter_params.organization_ids = organization_ids
|
112
|
+
filter_params.page_size = 1 # We only need the count
|
113
|
+
|
114
|
+
result = self.get_notifications(filter_params)
|
115
|
+
return result.total_entity_count
|
@@ -0,0 +1,267 @@
|
|
1
|
+
"""
|
2
|
+
Organizations API for the Binalyze AIR SDK using CQRS pattern.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List, Optional, Dict, Any
|
6
|
+
from ..http_client import HTTPClient
|
7
|
+
from ..models.organizations import (
|
8
|
+
Organization, OrganizationsPaginatedResponse, OrganizationUsersPaginatedResponse,
|
9
|
+
OrganizationUser, OrganizationSettings, CreateOrganizationRequest, UpdateOrganizationRequest,
|
10
|
+
AddUserToOrganizationRequest, AssignUsersToOrganizationRequest
|
11
|
+
)
|
12
|
+
from ..queries.organizations import (
|
13
|
+
ListOrganizationsQuery,
|
14
|
+
GetOrganizationQuery,
|
15
|
+
GetOrganizationUsersQuery,
|
16
|
+
)
|
17
|
+
from ..commands.organizations import (
|
18
|
+
CreateOrganizationCommand,
|
19
|
+
UpdateOrganizationCommand,
|
20
|
+
AssignUsersToOrganizationCommand,
|
21
|
+
RemoveUserFromOrganizationCommand,
|
22
|
+
UpdateOrganizationSettingsCommand,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
class OrganizationsAPI:
|
27
|
+
"""Organizations API with CQRS pattern - separated queries and commands."""
|
28
|
+
|
29
|
+
def __init__(self, http_client: HTTPClient):
|
30
|
+
self.http_client = http_client
|
31
|
+
|
32
|
+
# QUERIES (Read operations)
|
33
|
+
def list(self, page: int = 1, page_size: int = 10,
|
34
|
+
sort_by: str = "name", order: str = "asc") -> OrganizationsPaginatedResponse:
|
35
|
+
"""List organizations with pagination and sorting."""
|
36
|
+
query = ListOrganizationsQuery(self.http_client, page, page_size, sort_by, order, None)
|
37
|
+
return query.execute()
|
38
|
+
|
39
|
+
def get(self, organization_id: str) -> Organization:
|
40
|
+
"""Get organization by ID."""
|
41
|
+
query = GetOrganizationQuery(self.http_client, organization_id)
|
42
|
+
return query.execute()
|
43
|
+
|
44
|
+
def get_users(self, organization_id: str, page: int = 1, page_size: int = 10) -> OrganizationUsersPaginatedResponse:
|
45
|
+
"""Get users in organization."""
|
46
|
+
query = GetOrganizationUsersQuery(self.http_client, organization_id, page, page_size)
|
47
|
+
return query.execute()
|
48
|
+
|
49
|
+
def check_name(self, name: str) -> bool:
|
50
|
+
"""Check if organization name exists."""
|
51
|
+
try:
|
52
|
+
params = {"name": name}
|
53
|
+
response = self.http_client.get("organizations/check", params=params)
|
54
|
+
return response.get("result", False)
|
55
|
+
except Exception:
|
56
|
+
return False
|
57
|
+
|
58
|
+
def get_shareable_deployment_info(self, deployment_token: str) -> Dict[str, Any]:
|
59
|
+
"""Get shareable deployment information by token."""
|
60
|
+
try:
|
61
|
+
response = self.http_client.get(f"organizations/shareable-deployment-info/{deployment_token}")
|
62
|
+
|
63
|
+
if response.get("success"):
|
64
|
+
return response.get("result", {})
|
65
|
+
else:
|
66
|
+
# Return error information
|
67
|
+
return {
|
68
|
+
"error": True,
|
69
|
+
"errors": response.get("errors", []),
|
70
|
+
"statusCode": response.get("statusCode", 500)
|
71
|
+
}
|
72
|
+
except Exception as e:
|
73
|
+
return {
|
74
|
+
"error": True,
|
75
|
+
"errors": [str(e)],
|
76
|
+
"statusCode": 500
|
77
|
+
}
|
78
|
+
|
79
|
+
# COMMANDS (Write operations)
|
80
|
+
def create(self, request: CreateOrganizationRequest) -> Organization:
|
81
|
+
"""Create organization."""
|
82
|
+
command = CreateOrganizationCommand(self.http_client, request)
|
83
|
+
return command.execute()
|
84
|
+
|
85
|
+
def update(self, organization_id: str, request: UpdateOrganizationRequest) -> Organization:
|
86
|
+
"""Update organization."""
|
87
|
+
command = UpdateOrganizationCommand(self.http_client, organization_id, request)
|
88
|
+
return command.execute()
|
89
|
+
|
90
|
+
def add_user(self, organization_id: str, user_ids: List[str]) -> bool:
|
91
|
+
"""Add users to organization using the modern assign users endpoint."""
|
92
|
+
return self.assign_users(organization_id, user_ids)
|
93
|
+
|
94
|
+
def assign_users(self, organization_id: str, user_ids: List[str]) -> bool:
|
95
|
+
"""Assign users to organization using the /assign-users endpoint."""
|
96
|
+
# Create the proper request object with correct field name
|
97
|
+
request = AssignUsersToOrganizationRequest(userIds=user_ids)
|
98
|
+
command = AssignUsersToOrganizationCommand(self.http_client, organization_id, request)
|
99
|
+
return command.execute()
|
100
|
+
|
101
|
+
def remove_user(self, organization_id: str, user_id: str) -> Dict[str, Any]:
|
102
|
+
"""Remove user from organization using the /remove-user endpoint."""
|
103
|
+
command = RemoveUserFromOrganizationCommand(self.http_client, organization_id, user_id)
|
104
|
+
return command.execute()
|
105
|
+
|
106
|
+
def update_settings(self, organization_id: str, settings: Dict[str, Any]) -> OrganizationSettings:
|
107
|
+
"""Update organization settings."""
|
108
|
+
command = UpdateOrganizationSettingsCommand(self.http_client, organization_id, settings)
|
109
|
+
return command.execute()
|
110
|
+
|
111
|
+
def update_shareable_deployment_settings(self, organization_id: int, status: bool) -> Dict[str, Any]:
|
112
|
+
"""Update organization shareable deployment settings."""
|
113
|
+
try:
|
114
|
+
# Prepare the payload according to API specification
|
115
|
+
payload = {"status": status}
|
116
|
+
|
117
|
+
# Make the API call
|
118
|
+
response = self.http_client.post(f"organizations/{organization_id}/shareable-deployment", json_data=payload)
|
119
|
+
return response
|
120
|
+
|
121
|
+
except Exception as e:
|
122
|
+
# Check if it's a 409 conflict (expected behavior when setting to same state)
|
123
|
+
error_msg = str(e)
|
124
|
+
if "409" in error_msg or "already" in error_msg.lower():
|
125
|
+
# Return success for 409 conflicts (expected behavior)
|
126
|
+
return {
|
127
|
+
"success": True,
|
128
|
+
"result": None,
|
129
|
+
"statusCode": 409,
|
130
|
+
"message": "Shareable deployment setting already in desired state"
|
131
|
+
}
|
132
|
+
|
133
|
+
# Return error response format matching API
|
134
|
+
return {
|
135
|
+
"success": False,
|
136
|
+
"result": None,
|
137
|
+
"statusCode": 500,
|
138
|
+
"errors": [str(e)]
|
139
|
+
}
|
140
|
+
|
141
|
+
def update_deployment_token(self, organization_id: int, deployment_token: str) -> Dict[str, Any]:
|
142
|
+
"""Update organization deployment token."""
|
143
|
+
try:
|
144
|
+
# Prepare the payload according to API specification
|
145
|
+
payload = {"deploymentToken": deployment_token}
|
146
|
+
|
147
|
+
# Make the API call
|
148
|
+
response = self.http_client.post(f"organizations/{organization_id}/deployment-token", json_data=payload)
|
149
|
+
return response
|
150
|
+
|
151
|
+
except Exception as e:
|
152
|
+
# Check if it's a 409 conflict (expected behavior when setting to same token)
|
153
|
+
error_msg = str(e)
|
154
|
+
if "409" in error_msg or "same token" in error_msg.lower() or "cannot be updated with same" in error_msg.lower():
|
155
|
+
# Return success for 409 conflicts (expected behavior)
|
156
|
+
return {
|
157
|
+
"success": True,
|
158
|
+
"result": None,
|
159
|
+
"statusCode": 409,
|
160
|
+
"message": "Deployment token already set to this value"
|
161
|
+
}
|
162
|
+
|
163
|
+
# Return error response format matching API
|
164
|
+
return {
|
165
|
+
"success": False,
|
166
|
+
"result": None,
|
167
|
+
"statusCode": 500,
|
168
|
+
"errors": [str(e)]
|
169
|
+
}
|
170
|
+
|
171
|
+
def delete(self, organization_id: int) -> Dict[str, Any]:
|
172
|
+
"""Delete organization by ID."""
|
173
|
+
try:
|
174
|
+
# Make the API call
|
175
|
+
response = self.http_client.delete(f"organizations/{organization_id}")
|
176
|
+
return response
|
177
|
+
|
178
|
+
except Exception as e:
|
179
|
+
# Return error response format matching API
|
180
|
+
return {
|
181
|
+
"success": False,
|
182
|
+
"result": None,
|
183
|
+
"statusCode": 500,
|
184
|
+
"errors": [str(e)]
|
185
|
+
}
|
186
|
+
|
187
|
+
def add_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
188
|
+
"""Add tags to organization."""
|
189
|
+
try:
|
190
|
+
# Prepare the payload according to API specification
|
191
|
+
payload = {"tags": tags}
|
192
|
+
|
193
|
+
# Make the API call using PATCH method
|
194
|
+
response = self.http_client.patch(f"organizations/{organization_id}/tags", json_data=payload)
|
195
|
+
return response
|
196
|
+
|
197
|
+
except Exception as e:
|
198
|
+
# Return error response format matching API
|
199
|
+
return {
|
200
|
+
"success": False,
|
201
|
+
"result": None,
|
202
|
+
"statusCode": 500,
|
203
|
+
"errors": [str(e)]
|
204
|
+
}
|
205
|
+
|
206
|
+
def delete_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
207
|
+
"""Delete tags from organization."""
|
208
|
+
try:
|
209
|
+
# Prepare the payload according to API specification
|
210
|
+
payload = {"tags": tags}
|
211
|
+
|
212
|
+
# Make the API call using DELETE method
|
213
|
+
response = self.http_client.delete(f"organizations/{organization_id}/tags", json_data=payload)
|
214
|
+
return response
|
215
|
+
|
216
|
+
except Exception as e:
|
217
|
+
# Return error response format matching API
|
218
|
+
return {
|
219
|
+
"success": False,
|
220
|
+
"result": None,
|
221
|
+
"statusCode": 500,
|
222
|
+
"errors": [str(e)]
|
223
|
+
}
|
224
|
+
|
225
|
+
def remove_tags(self, organization_id: int, tags: List[str]) -> Dict[str, Any]:
|
226
|
+
"""Remove tags from organization (alias for delete_tags)."""
|
227
|
+
return self.delete_tags(organization_id, tags)
|
228
|
+
|
229
|
+
# ------------------------------------------------------------------
|
230
|
+
# New endpoints: user invitation & activation toggles
|
231
|
+
# ------------------------------------------------------------------
|
232
|
+
|
233
|
+
def resend_invitation(self, organization_id: int, user_id: str) -> Dict[str, Any]:
|
234
|
+
"""Resend organization invitation email to a user.
|
235
|
+
|
236
|
+
Mirrors POST /organizations/{orgId}/resend-invitation/{userId}
|
237
|
+
"""
|
238
|
+
try:
|
239
|
+
return self.http_client.post(
|
240
|
+
f"organizations/{organization_id}/resend-invitation/{user_id}",
|
241
|
+
json_data={},
|
242
|
+
)
|
243
|
+
except Exception as e:
|
244
|
+
return {
|
245
|
+
"success": False,
|
246
|
+
"result": None,
|
247
|
+
"statusCode": 500,
|
248
|
+
"errors": [str(e)],
|
249
|
+
}
|
250
|
+
|
251
|
+
def toggle_user_activation(self, organization_id: int, user_id: str) -> Dict[str, Any]:
|
252
|
+
"""Toggle active / inactive status for a user in the organization.
|
253
|
+
|
254
|
+
Endpoint: POST /organizations/{orgId}/toggle-user-activation/{userId}
|
255
|
+
"""
|
256
|
+
try:
|
257
|
+
return self.http_client.post(
|
258
|
+
f"organizations/{organization_id}/toggle-user-activation/{user_id}",
|
259
|
+
json_data={},
|
260
|
+
)
|
261
|
+
except Exception as e:
|
262
|
+
return {
|
263
|
+
"success": False,
|
264
|
+
"result": None,
|
265
|
+
"statusCode": 500,
|
266
|
+
"errors": [str(e)],
|
267
|
+
}
|
binalyze_air/apis/params.py
CHANGED
@@ -1,40 +1,45 @@
|
|
1
|
-
"""
|
2
|
-
Params API for the Binalyze AIR SDK.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List
|
6
|
-
|
7
|
-
from ..http_client import HTTPClient
|
8
|
-
from ..models.params import AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer
|
9
|
-
from ..queries.params import (
|
10
|
-
GetAcquisitionArtifactsQuery, GetEDiscoveryPatternsQuery,
|
11
|
-
GetAcquisitionEvidencesQuery, GetDroneAnalyzersQuery
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
class ParamsAPI:
|
16
|
-
"""Params API with CQRS pattern - read-only operations for parameters."""
|
17
|
-
|
18
|
-
def __init__(self, http_client: HTTPClient):
|
19
|
-
self.http_client = http_client
|
20
|
-
|
21
|
-
# QUERIES (Read operations only - params are read-only)
|
22
|
-
def get_acquisition_artifacts(self) -> List[AcquisitionArtifact]:
|
23
|
-
"""Get available acquisition artifacts."""
|
24
|
-
query = GetAcquisitionArtifactsQuery(self.http_client)
|
25
|
-
return query.execute()
|
26
|
-
|
27
|
-
def get_ediscovery_patterns(self) -> List[EDiscoveryPattern]:
|
28
|
-
"""Get available e-discovery patterns."""
|
29
|
-
query = GetEDiscoveryPatternsQuery(self.http_client)
|
30
|
-
return query.execute()
|
31
|
-
|
32
|
-
def get_acquisition_evidences(self) -> List[AcquisitionEvidence]:
|
33
|
-
"""Get available acquisition evidence types."""
|
34
|
-
query = GetAcquisitionEvidencesQuery(self.http_client)
|
35
|
-
return query.execute()
|
36
|
-
|
37
|
-
def get_drone_analyzers(self) -> List[DroneAnalyzer]:
|
38
|
-
"""Get available drone analyzers."""
|
39
|
-
query = GetDroneAnalyzersQuery(self.http_client)
|
1
|
+
"""
|
2
|
+
Params API for the Binalyze AIR SDK.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List
|
6
|
+
|
7
|
+
from ..http_client import HTTPClient
|
8
|
+
from ..models.params import AcquisitionArtifact, EDiscoveryPattern, AcquisitionEvidence, DroneAnalyzer, MitreAttackTactic
|
9
|
+
from ..queries.params import (
|
10
|
+
GetAcquisitionArtifactsQuery, GetEDiscoveryPatternsQuery,
|
11
|
+
GetAcquisitionEvidencesQuery, GetDroneAnalyzersQuery, GetMitreAttackTacticsQuery
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class ParamsAPI:
|
16
|
+
"""Params API with CQRS pattern - read-only operations for parameters."""
|
17
|
+
|
18
|
+
def __init__(self, http_client: HTTPClient):
|
19
|
+
self.http_client = http_client
|
20
|
+
|
21
|
+
# QUERIES (Read operations only - params are read-only)
|
22
|
+
def get_acquisition_artifacts(self) -> List[AcquisitionArtifact]:
|
23
|
+
"""Get available acquisition artifacts."""
|
24
|
+
query = GetAcquisitionArtifactsQuery(self.http_client)
|
25
|
+
return query.execute()
|
26
|
+
|
27
|
+
def get_ediscovery_patterns(self) -> List[EDiscoveryPattern]:
|
28
|
+
"""Get available e-discovery patterns."""
|
29
|
+
query = GetEDiscoveryPatternsQuery(self.http_client)
|
30
|
+
return query.execute()
|
31
|
+
|
32
|
+
def get_acquisition_evidences(self) -> List[AcquisitionEvidence]:
|
33
|
+
"""Get available acquisition evidence types."""
|
34
|
+
query = GetAcquisitionEvidencesQuery(self.http_client)
|
35
|
+
return query.execute()
|
36
|
+
|
37
|
+
def get_drone_analyzers(self) -> List[DroneAnalyzer]:
|
38
|
+
"""Get available drone analyzers."""
|
39
|
+
query = GetDroneAnalyzersQuery(self.http_client)
|
40
|
+
return query.execute()
|
41
|
+
|
42
|
+
def get_mitre_attack_tactics(self) -> List[MitreAttackTactic]:
|
43
|
+
"""Get available MITRE ATT&CK tactics."""
|
44
|
+
query = GetMitreAttackTacticsQuery(self.http_client)
|
40
45
|
return query.execute()
|