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,349 +1,368 @@
1
- """
2
- Evidences/Repositories-related data models for the Binalyze AIR SDK.
3
- """
4
-
5
- from typing import Optional, List, Dict, Any
6
- from datetime import datetime
7
- from pydantic import Field, field_validator
8
-
9
- from ..base import AIRBaseModel, Filter
10
-
11
-
12
- class EvidenceRepository(AIRBaseModel):
13
- """Base evidence repository model."""
14
-
15
- id: str = Field(alias="_id")
16
- name: str
17
- description: Optional[str] = None
18
- type: str # "amazon-s3", "azure-storage", "ftps", "sftp", "smb"
19
- path: Optional[str] = None
20
- username: Optional[str] = None
21
- password: Optional[str] = None
22
- host: Optional[str] = None
23
- port: Optional[int] = None
24
- organization_id: Optional[int] = Field(default=None, alias="organizationId")
25
- organization_ids: Optional[List[int]] = Field(default=None, alias="organizationIds")
26
- is_active: bool = Field(default=True, alias="isActive")
27
- is_default: bool = Field(default=False, alias="isDefault")
28
- created_at: Optional[datetime] = Field(default=None, alias="createdAt")
29
- updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
30
- created_by: Optional[str] = Field(default=None, alias="createdBy")
31
-
32
- @field_validator('organization_id', mode='after')
33
- @classmethod
34
- def derive_organization_id(cls, v, info):
35
- """Derive organization_id from organization_ids if not provided."""
36
- if v is None and 'organization_ids' in info.data:
37
- org_ids = info.data['organization_ids']
38
- if org_ids and len(org_ids) > 0:
39
- return org_ids[0]
40
- return 0 # Default to 0 if no organization IDs
41
- return v
42
-
43
-
44
- class AmazonS3Repository(AIRBaseModel):
45
- """Amazon S3 evidence repository model."""
46
-
47
- id: str
48
- name: str
49
- description: Optional[str] = None
50
- bucketName: str
51
- region: str
52
- accessKeyId: str
53
- secretAccessKey: str
54
- prefix: Optional[str] = None
55
- organizationId: int
56
- isActive: bool = True
57
- isDefault: bool = False
58
- createdAt: Optional[datetime] = None
59
- updatedAt: Optional[datetime] = None
60
-
61
-
62
- class CreateAmazonS3RepositoryRequest(AIRBaseModel):
63
- """Create Amazon S3 repository request model."""
64
-
65
- name: str
66
- description: Optional[str] = None
67
- bucketName: str
68
- region: str
69
- accessKeyId: str
70
- secretAccessKey: str
71
- prefix: Optional[str] = None
72
- organizationId: int
73
- isDefault: bool = False
74
-
75
-
76
- class UpdateAmazonS3RepositoryRequest(AIRBaseModel):
77
- """Update Amazon S3 repository request model."""
78
-
79
- name: Optional[str] = None
80
- description: Optional[str] = None
81
- bucketName: Optional[str] = None
82
- region: Optional[str] = None
83
- accessKeyId: Optional[str] = None
84
- secretAccessKey: Optional[str] = None
85
- prefix: Optional[str] = None
86
- isDefault: Optional[bool] = None
87
-
88
-
89
- class AzureStorageRepository(AIRBaseModel):
90
- """Azure Storage evidence repository model."""
91
-
92
- id: str
93
- name: str
94
- description: Optional[str] = None
95
- accountName: str
96
- accountKey: str
97
- containerName: str
98
- prefix: Optional[str] = None
99
- organizationId: int
100
- isActive: bool = True
101
- isDefault: bool = False
102
- createdAt: Optional[datetime] = None
103
- updatedAt: Optional[datetime] = None
104
-
105
-
106
- class CreateAzureStorageRepositoryRequest(AIRBaseModel):
107
- """Create Azure Storage repository request model."""
108
-
109
- name: str
110
- description: Optional[str] = None
111
- accountName: str
112
- accountKey: str
113
- containerName: str
114
- prefix: Optional[str] = None
115
- organizationId: int
116
- isDefault: bool = False
117
-
118
-
119
- class UpdateAzureStorageRepositoryRequest(AIRBaseModel):
120
- """Update Azure Storage repository request model."""
121
-
122
- name: Optional[str] = None
123
- description: Optional[str] = None
124
- accountName: Optional[str] = None
125
- accountKey: Optional[str] = None
126
- containerName: Optional[str] = None
127
- prefix: Optional[str] = None
128
- isDefault: Optional[bool] = None
129
-
130
-
131
- class FTPSRepository(AIRBaseModel):
132
- """FTPS evidence repository model."""
133
-
134
- id: str
135
- name: str
136
- description: Optional[str] = None
137
- host: str
138
- port: int = 21
139
- username: str
140
- password: str
141
- remotePath: Optional[str] = None
142
- passive: bool = True
143
- organizationId: int
144
- isActive: bool = True
145
- isDefault: bool = False
146
- createdAt: Optional[datetime] = None
147
- updatedAt: Optional[datetime] = None
148
-
149
-
150
- class CreateFTPSRepositoryRequest(AIRBaseModel):
151
- """Create FTPS repository request model."""
152
-
153
- name: str
154
- description: Optional[str] = None
155
- host: str
156
- port: int = 21
157
- username: str
158
- password: str
159
- remotePath: Optional[str] = None
160
- passive: bool = True
161
- organizationId: int
162
- isDefault: bool = False
163
-
164
-
165
- class UpdateFTPSRepositoryRequest(AIRBaseModel):
166
- """Update FTPS repository request model."""
167
-
168
- name: Optional[str] = None
169
- description: Optional[str] = None
170
- host: Optional[str] = None
171
- port: Optional[int] = None
172
- username: Optional[str] = None
173
- password: Optional[str] = None
174
- remotePath: Optional[str] = None
175
- passive: Optional[bool] = None
176
- isDefault: Optional[bool] = None
177
-
178
-
179
- class SFTPRepository(AIRBaseModel):
180
- """SFTP evidence repository model."""
181
-
182
- id: str
183
- name: str
184
- description: Optional[str] = None
185
- host: str
186
- port: int = 22
187
- username: str
188
- password: Optional[str] = None
189
- privateKey: Optional[str] = None
190
- remotePath: Optional[str] = None
191
- organizationId: int
192
- isActive: bool = True
193
- isDefault: bool = False
194
- createdAt: Optional[datetime] = None
195
- updatedAt: Optional[datetime] = None
196
-
197
-
198
- class CreateSFTPRepositoryRequest(AIRBaseModel):
199
- """Create SFTP repository request model."""
200
-
201
- name: str
202
- description: Optional[str] = None
203
- host: str
204
- port: int = 22
205
- username: str
206
- password: Optional[str] = None
207
- privateKey: Optional[str] = None
208
- remotePath: Optional[str] = None
209
- organizationId: int
210
- isDefault: bool = False
211
-
212
-
213
- class UpdateSFTPRepositoryRequest(AIRBaseModel):
214
- """Update SFTP repository request model."""
215
-
216
- name: Optional[str] = None
217
- description: Optional[str] = None
218
- host: Optional[str] = None
219
- port: Optional[int] = None
220
- username: Optional[str] = None
221
- password: Optional[str] = None
222
- privateKey: Optional[str] = None
223
- remotePath: Optional[str] = None
224
- isDefault: Optional[bool] = None
225
-
226
-
227
- class SMBRepository(AIRBaseModel):
228
- """SMB evidence repository model."""
229
-
230
- id: str
231
- name: str
232
- description: Optional[str] = None
233
- path: str
234
- username: str
235
- password: str
236
- domainName: Optional[str] = None
237
- organizationId: int
238
- organizationIds: Optional[List[int]] = None
239
- isActive: bool = True
240
- isDefault: bool = False
241
- createdAt: Optional[datetime] = None
242
- updatedAt: Optional[datetime] = None
243
-
244
-
245
- class CreateSMBRepositoryRequest(AIRBaseModel):
246
- """Create SMB repository request model."""
247
-
248
- name: str
249
- description: Optional[str] = None
250
- path: str
251
- username: str
252
- password: str
253
- domainName: Optional[str] = None
254
- organizationId: int
255
- organizationIds: Optional[List[int]] = None
256
- isDefault: bool = False
257
-
258
-
259
- class UpdateSMBRepositoryRequest(AIRBaseModel):
260
- """Update SMB repository request model."""
261
-
262
- name: Optional[str] = None
263
- description: Optional[str] = None
264
- path: Optional[str] = None
265
- username: Optional[str] = None
266
- password: Optional[str] = None
267
- domainName: Optional[str] = None
268
- isDefault: Optional[bool] = None
269
-
270
-
271
- class ValidateRepositoryRequest(AIRBaseModel):
272
- """Validate repository request model."""
273
-
274
- repositoryType: str # "amazon-s3", "azure-storage", "ftps"
275
- config: Dict[str, Any] # Repository-specific configuration
276
-
277
-
278
- class ValidationResult(AIRBaseModel):
279
- """Repository validation result model."""
280
-
281
- isValid: bool
282
- message: str
283
- errors: List[str] = []
284
- warnings: List[str] = []
285
-
286
-
287
- class RepositoryFilter(Filter):
288
- """Filter for repository queries."""
289
-
290
- name: Optional[str] = None
291
- type: Optional[str] = None
292
- organization_id: Optional[int] = None
293
- all_organizations: Optional[bool] = None
294
- path: Optional[str] = None
295
- username: Optional[str] = None
296
- host: Optional[str] = None
297
- is_active: Optional[bool] = None
298
- is_default: Optional[bool] = None
299
- created_by: Optional[str] = None
300
-
301
- def to_params(self) -> Dict[str, Any]:
302
- """Convert filter to API parameters with proper field mapping."""
303
- params = {}
304
-
305
- # Pagination parameters (not in filter namespace)
306
- if self.page_number is not None:
307
- params["pageNumber"] = self.page_number
308
- if self.page_size is not None:
309
- params["pageSize"] = self.page_size
310
- if self.sort_by is not None:
311
- params["sortBy"] = self.sort_by
312
- if self.sort_type is not None:
313
- params["sortType"] = self.sort_type
314
-
315
- # Filter parameters with proper field mapping
316
- field_mappings = {
317
- "search_term": "searchTerm",
318
- "organization_ids": "organizationIds",
319
- "organization_id": "organizationIds", # Map to organizationIds for API
320
- "all_organizations": "allOrganizations",
321
- "name": "name",
322
- "type": "type",
323
- "path": "path",
324
- "username": "username",
325
- "host": "host",
326
- "is_active": "isActive",
327
- "is_default": "isDefault",
328
- "created_by": "createdBy"
329
- }
330
-
331
- for field_name, field_value in self.model_dump(exclude_none=True).items():
332
- # Skip pagination/sorting fields as they're handled above
333
- if field_name in ["page_number", "page_size", "sort_by", "sort_type"]:
334
- continue
335
-
336
- if field_value is not None:
337
- api_field_name = field_mappings.get(field_name, field_name)
338
-
339
- # Special handling for organization_ids - use first ID for organizationIds filter
340
- if field_name == "organization_ids" and isinstance(field_value, list) and len(field_value) > 0:
341
- params[f"filter[{api_field_name}]"] = ",".join(map(str, field_value))
342
- elif field_name == "organization_id":
343
- params[f"filter[organizationIds]"] = str(field_value)
344
- elif isinstance(field_value, list):
345
- params[f"filter[{api_field_name}]"] = ",".join(map(str, field_value))
346
- else:
347
- params[f"filter[{api_field_name}]"] = str(field_value)
348
-
1
+ """
2
+ Evidences/Repositories-related data models for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, List, Dict, Any
6
+ from datetime import datetime
7
+ from pydantic import Field, field_validator
8
+
9
+ from ..base import AIRBaseModel, Filter
10
+
11
+
12
+ class EvidenceRepository(AIRBaseModel):
13
+ """Base evidence repository model."""
14
+
15
+ id: str = Field(alias="_id")
16
+ name: str
17
+ description: Optional[str] = None
18
+ type: str # "amazon-s3", "azure-storage", "ftps", "sftp", "smb"
19
+ path: Optional[str] = None
20
+ username: Optional[str] = None
21
+ password: Optional[str] = None
22
+ host: Optional[str] = None
23
+ port: Optional[int] = None
24
+ organization_id: Optional[int] = Field(default=None, alias="organizationId")
25
+ organization_ids: Optional[List[int]] = Field(default=None, alias="organizationIds")
26
+ is_active: bool = Field(default=True, alias="isActive")
27
+ is_default: bool = Field(default=False, alias="isDefault")
28
+ created_at: Optional[datetime] = Field(default=None, alias="createdAt")
29
+ updated_at: Optional[datetime] = Field(default=None, alias="updatedAt")
30
+ created_by: Optional[str] = Field(default=None, alias="createdBy")
31
+
32
+ @field_validator('organization_id', mode='after')
33
+ @classmethod
34
+ def derive_organization_id(cls, v, info):
35
+ """Derive organization_id from organization_ids if not provided."""
36
+ if v is None and 'organization_ids' in info.data:
37
+ org_ids = info.data['organization_ids']
38
+ if org_ids and len(org_ids) > 0:
39
+ return org_ids[0]
40
+ return 0 # Default to 0 if no organization IDs
41
+ return v
42
+
43
+
44
+ class AmazonS3Repository(AIRBaseModel):
45
+ """Amazon S3 evidence repository model."""
46
+
47
+ id: str
48
+ name: str
49
+ description: Optional[str] = None
50
+ bucketName: str
51
+ region: str
52
+ accessKeyId: str
53
+ secretAccessKey: str
54
+ prefix: Optional[str] = None
55
+ organizationId: int
56
+ isActive: bool = True
57
+ isDefault: bool = False
58
+ createdAt: Optional[datetime] = None
59
+ updatedAt: Optional[datetime] = None
60
+
61
+
62
+ class CreateAmazonS3RepositoryRequest(AIRBaseModel):
63
+ """Create Amazon S3 repository request model."""
64
+
65
+ name: str
66
+ description: Optional[str] = None
67
+ bucketName: str
68
+ region: str
69
+ accessKeyId: str
70
+ secretAccessKey: str
71
+ prefix: Optional[str] = None
72
+ organizationId: int
73
+ isDefault: bool = False
74
+
75
+
76
+ class UpdateAmazonS3RepositoryRequest(AIRBaseModel):
77
+ """Update Amazon S3 repository request model."""
78
+
79
+ name: Optional[str] = None
80
+ description: Optional[str] = None
81
+ bucketName: Optional[str] = None
82
+ region: Optional[str] = None
83
+ accessKeyId: Optional[str] = None
84
+ secretAccessKey: Optional[str] = None
85
+ prefix: Optional[str] = None
86
+ isDefault: Optional[bool] = None
87
+
88
+
89
+ class AzureStorageRepository(AIRBaseModel):
90
+ """Azure Storage evidence repository model."""
91
+
92
+ id: str
93
+ name: str
94
+ description: Optional[str] = None
95
+ accountName: str
96
+ accountKey: str
97
+ containerName: str
98
+ prefix: Optional[str] = None
99
+ organizationId: int
100
+ isActive: bool = True
101
+ isDefault: bool = False
102
+ createdAt: Optional[datetime] = None
103
+ updatedAt: Optional[datetime] = None
104
+
105
+
106
+ class CreateAzureStorageRepositoryRequest(AIRBaseModel):
107
+ """Create Azure Storage repository request model."""
108
+
109
+ name: str
110
+ description: Optional[str] = None
111
+ accountName: str
112
+ accountKey: str
113
+ containerName: str
114
+ prefix: Optional[str] = None
115
+ organizationId: int
116
+ isDefault: bool = False
117
+
118
+
119
+ class UpdateAzureStorageRepositoryRequest(AIRBaseModel):
120
+ """Update Azure Storage repository request model."""
121
+
122
+ name: Optional[str] = None
123
+ description: Optional[str] = None
124
+ accountName: Optional[str] = None
125
+ accountKey: Optional[str] = None
126
+ containerName: Optional[str] = None
127
+ prefix: Optional[str] = None
128
+ isDefault: Optional[bool] = None
129
+
130
+
131
+ class FTPSRepository(AIRBaseModel):
132
+ """FTPS evidence repository model."""
133
+
134
+ id: str
135
+ name: str
136
+ description: Optional[str] = None
137
+ host: str
138
+ port: int = 21
139
+ username: str
140
+ password: str
141
+ remotePath: Optional[str] = None
142
+ passive: bool = True
143
+ organizationId: int
144
+ isActive: bool = True
145
+ isDefault: bool = False
146
+ createdAt: Optional[datetime] = None
147
+ updatedAt: Optional[datetime] = None
148
+
149
+
150
+ class CreateFTPSRepositoryRequest(AIRBaseModel):
151
+ """Create FTPS repository request model."""
152
+
153
+ name: str
154
+ description: Optional[str] = None
155
+ host: str
156
+ port: int = 21
157
+ username: str
158
+ password: str
159
+ remotePath: Optional[str] = None
160
+ passive: bool = True
161
+ organizationId: int
162
+ isDefault: bool = False
163
+
164
+
165
+ class UpdateFTPSRepositoryRequest(AIRBaseModel):
166
+ """Update FTPS repository request model."""
167
+
168
+ name: Optional[str] = None
169
+ description: Optional[str] = None
170
+ host: Optional[str] = None
171
+ port: Optional[int] = None
172
+ username: Optional[str] = None
173
+ password: Optional[str] = None
174
+ remotePath: Optional[str] = None
175
+ passive: Optional[bool] = None
176
+ isDefault: Optional[bool] = None
177
+
178
+
179
+ class SFTPRepository(AIRBaseModel):
180
+ """SFTP evidence repository model."""
181
+
182
+ id: str
183
+ name: str
184
+ description: Optional[str] = None
185
+ host: str
186
+ port: int = 22
187
+ username: str
188
+ password: Optional[str] = None
189
+ privateKey: Optional[str] = None
190
+ remotePath: Optional[str] = None
191
+ organizationId: int
192
+ isActive: bool = True
193
+ isDefault: bool = False
194
+ createdAt: Optional[datetime] = None
195
+ updatedAt: Optional[datetime] = None
196
+
197
+
198
+ class CreateSFTPRepositoryRequest(AIRBaseModel):
199
+ """Create SFTP repository request model."""
200
+
201
+ name: str
202
+ description: Optional[str] = None
203
+ host: str
204
+ port: int = 22
205
+ username: str
206
+ password: Optional[str] = None
207
+ privateKey: Optional[str] = None
208
+ remotePath: Optional[str] = None
209
+ organizationId: int
210
+ isDefault: bool = False
211
+
212
+
213
+ class UpdateSFTPRepositoryRequest(AIRBaseModel):
214
+ """Update SFTP repository request model."""
215
+
216
+ name: Optional[str] = None
217
+ description: Optional[str] = None
218
+ host: Optional[str] = None
219
+ port: Optional[int] = None
220
+ username: Optional[str] = None
221
+ password: Optional[str] = None
222
+ privateKey: Optional[str] = None
223
+ remotePath: Optional[str] = None
224
+ isDefault: Optional[bool] = None
225
+
226
+
227
+ class SMBRepository(AIRBaseModel):
228
+ """SMB evidence repository model."""
229
+
230
+ id: str
231
+ name: str
232
+ description: Optional[str] = None
233
+ path: str
234
+ username: str
235
+ password: str
236
+ domainName: Optional[str] = None
237
+ organizationId: int
238
+ organizationIds: Optional[List[int]] = None
239
+ isActive: bool = True
240
+ isDefault: bool = False
241
+ createdAt: Optional[datetime] = None
242
+ updatedAt: Optional[datetime] = None
243
+
244
+
245
+ class CreateSMBRepositoryRequest(AIRBaseModel):
246
+ """Create SMB repository request model."""
247
+
248
+ name: str
249
+ description: Optional[str] = None
250
+ path: str
251
+ username: str
252
+ password: str
253
+ domainName: Optional[str] = None
254
+ organizationId: int
255
+ organizationIds: Optional[List[int]] = None
256
+ isDefault: bool = False
257
+
258
+
259
+ class UpdateSMBRepositoryRequest(AIRBaseModel):
260
+ """Update SMB repository request model."""
261
+
262
+ name: Optional[str] = None
263
+ description: Optional[str] = None
264
+ path: Optional[str] = None
265
+ username: Optional[str] = None
266
+ password: Optional[str] = None
267
+ domainName: Optional[str] = None
268
+ isDefault: Optional[bool] = None
269
+
270
+
271
+ class ValidateRepositoryRequest(AIRBaseModel):
272
+ """Validate repository request model."""
273
+
274
+ repositoryType: str # "amazon-s3", "azure-storage", "ftps"
275
+ config: Dict[str, Any] # Repository-specific configuration
276
+
277
+ def model_dump(self, **kwargs):
278
+ """Override to ensure path -> repositoryPath mapping in config."""
279
+ data = super().model_dump(**kwargs)
280
+ if 'config' in data and isinstance(data['config'], dict):
281
+ config = data['config']
282
+ if 'path' in config:
283
+ config['repositoryPath'] = config.pop('path')
284
+ return data
285
+
286
+
287
+ class ValidationResult(AIRBaseModel):
288
+ """Repository validation result model."""
289
+
290
+ isValid: bool
291
+ message: str
292
+ errors: List[str] = []
293
+ warnings: List[str] = []
294
+
295
+
296
+ class RepositoryFilter(Filter):
297
+ """Filter for repository queries."""
298
+
299
+ name: Optional[str] = None
300
+ type: Optional[str] = None
301
+ organization_id: Optional[int] = None
302
+ organization_ids: Optional[List[int]] = None
303
+ all_organizations: Optional[bool] = None
304
+ path: Optional[str] = None
305
+ username: Optional[str] = None
306
+ host: Optional[str] = None
307
+ is_active: Optional[bool] = None
308
+ is_default: Optional[bool] = None
309
+ created_by: Optional[str] = None
310
+
311
+ def to_params(self) -> Dict[str, Any]:
312
+ """Convert filter to API parameters with proper field mapping."""
313
+ params = {}
314
+
315
+ # Pagination parameters (not in filter namespace)
316
+ if self.page_number is not None:
317
+ params["pageNumber"] = self.page_number
318
+ if self.page_size is not None:
319
+ params["pageSize"] = self.page_size
320
+ if self.sort_by is not None:
321
+ params["sortBy"] = self.sort_by
322
+ if self.sort_type is not None:
323
+ params["sortType"] = self.sort_type
324
+
325
+ # Filter parameters with proper field mapping
326
+ field_mappings = {
327
+ "search_term": "searchTerm",
328
+ "organization_ids": "organizationIds",
329
+ "organization_id": "organizationIds", # Map to organizationIds for API
330
+ "all_organizations": "allOrganizations",
331
+ "name": "name",
332
+ "type": "type",
333
+ "path": "path",
334
+ "username": "username",
335
+ "host": "host",
336
+ "is_active": "isActive",
337
+ "is_default": "isDefault",
338
+ "created_by": "createdBy"
339
+ }
340
+
341
+ # Ensure organizationIds is always provided (required by API)
342
+ org_ids_provided = False
343
+
344
+ for field_name, field_value in self.model_dump(exclude_none=True).items():
345
+ # Skip pagination/sorting fields as they're handled above
346
+ if field_name in ["page_number", "page_size", "sort_by", "sort_type"]:
347
+ continue
348
+
349
+ if field_value is not None:
350
+ api_field_name = field_mappings.get(field_name, field_name)
351
+
352
+ # Special handling for organization_ids - use first ID for organizationIds filter
353
+ if field_name == "organization_ids" and isinstance(field_value, list) and len(field_value) > 0:
354
+ params[f"filter[{api_field_name}]"] = ",".join([str(x) for x in field_value])
355
+ org_ids_provided = True
356
+ elif field_name == "organization_id":
357
+ params[f"filter[organizationIds]"] = str(field_value)
358
+ org_ids_provided = True
359
+ elif isinstance(field_value, list):
360
+ params[f"filter[{api_field_name}]"] = ",".join([str(x) for x in field_value])
361
+ else:
362
+ params[f"filter[{api_field_name}]"] = str(field_value)
363
+
364
+ # API requires organizationIds - provide default if not set
365
+ if not org_ids_provided:
366
+ params["filter[organizationIds]"] = "0"
367
+
349
368
  return params