fortidlp 0.90__tar.gz

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.
fortidlp-0.90/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rafael Foster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include requirements.txt
2
+ include fortiedr/api_parameters.json
fortidlp-0.90/PKG-INFO ADDED
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: fortidlp
3
+ Version: 0.90
4
+ Summary: This FortiDLP module is an open-source Python library that simplifies interaction with the FortiDLP Cloud API.
5
+ Author: Rafael Foster
6
+ Author-email: rafaelgfoster@gmail.com
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Dist: Requests==2.31.0
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: classifier
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: summary
File without changes
@@ -0,0 +1 @@
1
+ from fortidlp.fortidlp import *
@@ -0,0 +1,40 @@
1
+ import requests
2
+
3
+ class AuthenticationHandler:
4
+
5
+ def test_authentication(self, headers, host):
6
+ data = None
7
+ status = False
8
+ response_headers = None
9
+ urls = ['api/v2/users/search', 'api/v2/dashboards']
10
+
11
+ for url in urls:
12
+
13
+ url = f'https://{host}/{url}'
14
+ try:
15
+ res = requests.get(url, headers=headers, verify=False)
16
+ res_code = res.status_code
17
+ status = False
18
+ if res_code == 401:
19
+ data = "Unauthorized"
20
+ elif res_code == 403:
21
+ data = "Forbidden"
22
+ elif res_code == 404:
23
+ data = "Not Found"
24
+ elif res_code == 500:
25
+ data = "Internal Server Error"
26
+ else:
27
+ data = res
28
+ status = True
29
+ response_headers = res.headers
30
+ return status, data, response_headers
31
+
32
+ except requests.exceptions.RequestException as err:
33
+ raise SystemExit(err) from err
34
+
35
+ return status, data, response_headers
36
+
37
+ def get_headers(self, fedr_host, auth_token):
38
+ headers = {"Authorization": f"Bearer {auth_token}"}
39
+ status, data, res_headers = self.test_authentication(headers, fedr_host)
40
+ return (headers, fedr_host) if status else (None, data)
@@ -0,0 +1,126 @@
1
+ import json
2
+ import logging
3
+ import requests
4
+ from datetime import datetime
5
+
6
+ # Globally disable SSL warnings
7
+ requests.packages.urllib3.disable_warnings()
8
+
9
+ class APIHandler:
10
+
11
+ def __init__(self):
12
+ self.host = None
13
+ self.headers = None
14
+ self.SSL_Verify = True
15
+ self.debug_enabled = False
16
+
17
+ def enable_debug(self):
18
+ import http.client as http_client
19
+ http_client.HTTPConnection.debuglevel = 1
20
+
21
+ logging.basicConfig()
22
+ logger = logging.getLogger().setLevel(logging.DEBUG)
23
+ requests_log = logging.getLogger("requests.packages.urllib3")
24
+ requests_log.setLevel(logging.DEBUG)
25
+ requests_log.propagate = True
26
+ self.debug_enabled = True
27
+
28
+ def conn(self, headers=None, host=None, enable_debug=False, enable_ssl=True, organization = None):
29
+ self.host = host
30
+ self.headers = headers
31
+ if enable_debug:
32
+ self.enable_debug()
33
+
34
+ self.SSL_Verify = enable_ssl
35
+
36
+ def get(self, url, params=None, request_type=None):
37
+ return self._exec("GET", url, params, request_type=request_type)
38
+
39
+ def send(self, url, params=None, request_type=None):
40
+ return self._exec("POST", url, params, request_type=request_type)
41
+
42
+ def insert(self, url, params=None, request_type=None):
43
+ return self._exec("PUT", url, params, request_type=request_type)
44
+
45
+ def update(self, url, params=None, request_type=None):
46
+ return self._exec("PATCH", url, params, request_type=request_type)
47
+
48
+ def delete(self, url, params=None, request_type=None):
49
+ return self._exec("DELETE", url, params, request_type=request_type)
50
+
51
+ def download(self, url, params=None, request_type=None, file_format = 'zip', download_folder = ''):
52
+ self.download_folder = download_folder
53
+ return self._exec("GET", url, params, request_type=request_type, download_file=True, file_format=file_format)
54
+
55
+ def upload(self, url, file, params=None, request_type=None ):
56
+ return self._exec("POST", url, params, request_type=request_type, upload_file=file)
57
+
58
+ def _exec(self, method, url, params=None, download_file=False, request_type=None, file_format=None, upload_file=None):
59
+ if method not in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']:
60
+ raise ValueError("Method not supported")
61
+
62
+ if not self.headers or not self.host:
63
+ return "NOT AUTHENTICATED. Run Auth() first."
64
+
65
+ params = {k: v for k, v in (params or {}).items() if v is not None}
66
+ url = f"https://{self.host}{url}"
67
+
68
+ if request_type:
69
+ self.headers['Content-Type'] = request_type
70
+
71
+ if self.debug_enabled:
72
+ print("URL = ", url)
73
+ print(json.dumps(self.headers, indent=4))
74
+ print(json.dumps(params, indent=4))
75
+
76
+ try:
77
+ response = requests.request(
78
+ method,
79
+ url,
80
+ headers=self.headers,
81
+ json=params if method in ['POST', 'PUT', 'PATCH'] else None,
82
+ params=params if method == 'GET' else None,
83
+ verify=self.SSL_Verify,
84
+ stream=download_file,
85
+ files=upload_file
86
+ )
87
+ except requests.exceptions.ConnectionError as e:
88
+ return {
89
+ 'status': False,
90
+ 'data': {'status_code': 500, 'error_message': f'Failed to connect to {url}. Error: {e}'}
91
+ }
92
+ except requests.exceptions.RequestException as e:
93
+ return {
94
+ 'status': False,
95
+ 'data': {'status_code': 500, 'error_message': e}
96
+ }
97
+
98
+ if not response.ok:
99
+ try:
100
+ error_message = response.json().get('errorMessage', response.text)
101
+ except ValueError:
102
+ error_message = response.text
103
+ return {
104
+ 'status': False,
105
+ 'data': {'status_code': response.status_code, 'error_message': error_message}
106
+ }
107
+
108
+ if download_file:
109
+ filename_function = url.split('/')[-1].replace('-','_')
110
+ filename_function = filename_function.split('?')[0]
111
+ if not self.download_folder:
112
+ self.download_folder = '.'
113
+ return self._handle_file_download(response, filename_function, file_format)
114
+
115
+ try:
116
+ return {'status': True, 'data': response.json()}
117
+ except ValueError: # If response is not JSON
118
+ return {'status': True, 'data': response.text}
119
+
120
+ def _handle_file_download(self, response, filename_prefix, file_format='zip'):
121
+ date_now = datetime.now().strftime("%Y%m%d_%H%M%S")
122
+ filename = f"{self.download_folder}/{filename_prefix}_{date_now}.{file_format}"
123
+ with open(filename, 'wb') as f:
124
+ for chunk in response.iter_content(chunk_size=1024):
125
+ f.write(chunk)
126
+ return {'status': True, 'data': filename}
@@ -0,0 +1,807 @@
1
+
2
+ import re
3
+ import os
4
+ import json
5
+ from typing import BinaryIO, Optional
6
+ from fortidlp.auth import AuthenticationHandler
7
+ from fortidlp.connector import APIHandler
8
+
9
+ version = '0.1'
10
+
11
+ fortidlp_connection = APIHandler()
12
+
13
+ class Audit:
14
+ '''
15
+ Class Audit
16
+ Description: Return a list of audit logs.
17
+ '''
18
+
19
+ def get_audit_logs(self, filter: list = None, start_time: str = None, end_time: str = None, operation_types: list[str] = None, results_per_page: int = 100, sort_order: str = 'desc') -> dict:
20
+ '''
21
+ Class Audit
22
+ Description: Return a list of audit logs.
23
+
24
+ Args:
25
+ start_time (str): Start time for the logs in ISO format.
26
+ end_time (str): End time for the logs in ISO format.
27
+ limit (int): Number of logs to return.
28
+
29
+ Returns:
30
+ bool: Status of the request (True or False).
31
+ None: This function does not return any data.
32
+ '''
33
+
34
+ parameters = {}
35
+
36
+ if filter:
37
+ parameters["filter"] = filter if isinstance(filter, list) else [filter]
38
+ if start_time or end_time:
39
+ parameters["time_range"] = {}
40
+ if start_time:
41
+ parameters["time_range"]["start_time"] = start_time
42
+ if end_time:
43
+ parameters["time_range"]["to"] = end_time
44
+
45
+ if operation_types:
46
+ parameters["types"] = operation_types if isinstance(operation_types, list) else [operation_types]
47
+
48
+ url = '/api/v1/audit/search'
49
+
50
+ url = f"{url}?results_per_page={results_per_page}&sort_order={sort_order}"
51
+
52
+ return fortidlp_connection.send(url, params=parameters)
53
+
54
+ class Cases:
55
+
56
+ def list_cases(self, content_event_uri: Optional[str] = None, content_operated_by: Optional[str] = None, created_by: Optional[str] = None) -> dict:
57
+ '''
58
+ Class Cases
59
+ Description: Return a list of cases.
60
+
61
+ Args:
62
+ content_event_uri: "string",
63
+ content_operated_by: "string",
64
+ created_by: "string"
65
+
66
+ Returns:
67
+ bool: Status of the request (True or False).
68
+ None: This function does not return any data.
69
+ '''
70
+
71
+ url = '/api/v1/cases'
72
+ parameters = {}
73
+
74
+ if content_event_uri:
75
+ parameters["content_event_uri"] = content_event_uri
76
+ if content_operated_by:
77
+ parameters["content_operated_by"] = content_operated_by
78
+ if created_by:
79
+ parameters["created_by"] = created_by
80
+
81
+ return fortidlp_connection.get(url, params=parameters)
82
+
83
+ def delete_case(self, case_id: str) -> dict:
84
+ '''
85
+ Class Cases
86
+ Description: Deletes a case.
87
+
88
+ Args:
89
+ case_id (str): The ID of the case to delete.
90
+
91
+ Returns:
92
+ bool: Status of the request (True or False).
93
+ None: This function does not return any data.
94
+ '''
95
+
96
+ url = f'/api/v1/cases/{case_id}'
97
+ return fortidlp_connection.delete(url)
98
+
99
+ class Operators:
100
+ ''''''
101
+
102
+ def list_operators(self) -> dict:
103
+ '''
104
+ Class Operators
105
+ Description: Return a list of operators.
106
+
107
+ Args:
108
+
109
+ Returns:
110
+ bool: Status of the request (True or False).
111
+ None: This function does not return any data.
112
+ '''
113
+
114
+ url = '/api/v1/operators'
115
+ return fortidlp_connection.get(url)
116
+
117
+ def create_operator(self, username:str, name: str, email: str, company: str, password: str, role, link_expiration: int = 1, password_reset_on_login: bool = True ) -> tuple[bool, None]:
118
+ '''
119
+ Class Operators
120
+ Description: Adds a new operator.
121
+
122
+ Args:
123
+
124
+ Returns:
125
+ bool: Status of the request (True or False).
126
+ None: This function does not return any data.
127
+ '''
128
+
129
+ url = '/api/v1/operators'
130
+
131
+ data = {
132
+ "operator": {
133
+ "company": company,
134
+ "display_name": name,
135
+ "email": email,
136
+ "name": username,
137
+ "roles": [
138
+ role
139
+ ],
140
+ },
141
+ "passphrase": password,
142
+ "passphrase_reset_link_expiry_duration": link_expiration,
143
+ "passphrase_reset_on_login": password_reset_on_login
144
+ }
145
+
146
+ return fortidlp_connection.send(url, params=data)
147
+
148
+ def delete_operator(self, operator_id: str) -> dict:
149
+ '''
150
+ Class Operators
151
+ Description: Deletes an operator.
152
+
153
+ Args:
154
+ operator_id (str): The ID of the operator to delete.
155
+
156
+ Returns:
157
+ bool: Status of the request (True or False).
158
+ None: This function does not return any data.
159
+ '''
160
+ url = f'/api/v1/operators/{operator_id}'
161
+ return fortidlp_connection.delete(url)
162
+
163
+ class Users:
164
+ '''
165
+ Class Users
166
+ Description: Return a list of users.
167
+ '''
168
+
169
+ def get_users(self) -> tuple[bool, None]:
170
+ '''
171
+ Class Users
172
+ Description: Return a list of users.
173
+
174
+ Args:
175
+
176
+ Returns:
177
+ bool: Status of the request (True or False).
178
+ None: This function does not return any data.
179
+ '''
180
+
181
+ url = '/api/v1/users'
182
+ return fortidlp_connection.get(url)
183
+
184
+ # Function to create a users, that might contain the following data as input:
185
+ # {
186
+ # "address_home": "123 Street Name, New York 12401, United States",
187
+ # "address_office": "123 Street Name, New York 12401, United States",
188
+ # "department": "Accounting",
189
+ # "description": "string",
190
+ # "directory_labels": [
191
+ # {
192
+ # "category": "DIRECTORY",
193
+ # "name": "Department | Accounting"
194
+ # }
195
+ # ],
196
+ # "email": "john.smith@example.com",
197
+ # "image_content": "string",
198
+ # "juid": "5b07da47-86a8-4fc2-a7d8-3241b74270ca",
199
+ # "manager": "Liz Brown",
200
+ # "manager_unique_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
201
+ # "name": "John Smith",
202
+ # "phone_number_mobile": "+1 234 555 6789",
203
+ # "phone_number_office": "+1 234 555 6789",
204
+ # "sync_info": {
205
+ # "sync_invocation_id": "5b07da47-86a8-4fc2-a7d8-3241b74270ca",
206
+ # "sync_source": "ldap://5b07da47-86a8-4fc2-a7d8-3241b74270ca"
207
+ # },
208
+ # "title": "Finance Assistant",
209
+ # "unique_data": "S-1-5-21-3623811015-3361044348-30300820-1013",
210
+ # "unique_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
211
+ # "user_uri": [
212
+ # "mail://john.smith@example.com",
213
+ # "sid://S-1-5-21-3623811015-3361044348-30300820-1013@domain"
214
+ # ]
215
+ # }
216
+
217
+ def create_user(self, address_home: str = None, address_office: str = None, department: str = None, description: str = None, directory_labels: str = None, category: str = None, label_name: str = None, name: str = None, email: str = None, image_content: str = None, juid: str = None, manager: str = None, manager_unique_id: str = None, phone_number_mobile: str = None, phone_number_office: str = None, sync_info: str = None, sync_invocation_id: str = None, sync_source: str = None, title: str = None, unique_data: str = None, unique_id: str = None, user_uri: str = None) -> tuple[bool, None]:
218
+
219
+ user = {
220
+ "address_home": address_home,
221
+ "address_office": address_office,
222
+ "department": department,
223
+ "description": description,
224
+ "directory_labels": directory_labels,
225
+ "email": email,
226
+ "image_content": image_content,
227
+ "juid": juid,
228
+ "manager": manager,
229
+ "manager_unique_id": manager_unique_id,
230
+ "name": name,
231
+ "phone_number_mobile": phone_number_mobile,
232
+ "phone_number_office": phone_number_office,
233
+ "sync_info": {
234
+ "sync_invocation_id": sync_invocation_id,
235
+ "sync_source": sync_source
236
+ },
237
+ "title": title,
238
+ "unique_data": unique_data,
239
+ "unique_id": unique_id,
240
+ }
241
+
242
+ url = '/api/v1/admin/users'
243
+
244
+ return fortidlp_connection.send(url, params=user)
245
+
246
+ class Policies:
247
+ '''
248
+ Class Policies
249
+ Description: Return a list of policies.
250
+ '''
251
+
252
+ def create_policies_groups(self, description: str, exclude_labels: list[str], include_labels: list[str], match: str, name: str) -> tuple[bool, None]:
253
+ '''
254
+ Class Policies
255
+ Description: Create a new policies group.
256
+
257
+ Args:
258
+ description (str): The description of the policies group.
259
+ exclude_labels (list[str]): The labels to exclude.
260
+ include_labels (list[str]): The labels to include.
261
+ match (str): The match criteria.
262
+ name (str): The name of the policies group.
263
+
264
+ Returns:
265
+ bool: Status of the request (True or False).
266
+ None: This function does not return any data.
267
+ '''
268
+
269
+ url = '/api/v1/policies/groups'
270
+ return fortidlp_connection.send(url)
271
+
272
+ def list_policies_groups(self) -> tuple[bool, None]:
273
+ '''
274
+ Class Policies
275
+ Description: Return a list of policies.
276
+
277
+ Args:
278
+
279
+ Returns:
280
+ bool: Status of the request (True or False).
281
+ None: This function does not return any data.
282
+ '''
283
+
284
+ url = '/api/v1/policies/groups'
285
+ return fortidlp_connection.get(url)
286
+
287
+ def delete_policy_group(self, group_id: str) -> dict:
288
+ '''
289
+ Class Policies
290
+ Description: Delete a policies group.
291
+
292
+ Args:
293
+ group_id (str): The ID of the policies group to delete.
294
+
295
+ Returns:
296
+ bool: Status of the request (True or False).
297
+ None: This function does not return any data.
298
+ '''
299
+
300
+ url = f'/api/v1/policies/groups/{group_id}'
301
+ return fortidlp_connection.delete(url)
302
+
303
+ def export_policy_groups(self, group_ids: list[str], include_data_objects: bool = True, include_labels: bool = True) -> tuple[bool, None]:
304
+ '''
305
+ Class Policies
306
+ Description: Export policy groups.
307
+
308
+ Args:
309
+ group_ids (list[str]): List of policy group IDs to export.
310
+
311
+ Returns:
312
+ bool: Status of the request (True or False).
313
+ None: This function does not return any data.
314
+ '''
315
+
316
+ url = '/api/v1/policies/export'
317
+ data = {
318
+ "group_ids": group_ids,
319
+ "include_data_objects": include_data_objects,
320
+ "include_labels": include_labels
321
+ }
322
+ return fortidlp_connection.download(url, params=data)
323
+
324
+ def list_policies_data(self) -> tuple[bool, None]:
325
+ '''
326
+ Class Policies
327
+ Description: Return a list of policies data.
328
+
329
+ Args:
330
+
331
+ Returns:
332
+ bool: Status of the request (True or False).
333
+ None: This function does not return any data.
334
+ '''
335
+
336
+ url = '/api/v1/policies/data'
337
+ return fortidlp_connection.get(url)
338
+
339
+ def delete_policy_asset(self, asset_id: str) -> dict:
340
+ '''
341
+ Class Policies
342
+ Description: Delete a specific policy asset.
343
+
344
+ Args:
345
+ asset_id (str): The ID of the policy asset to delete.
346
+
347
+ Returns:
348
+ bool: Status of the request (True or False).
349
+ None: This function does not return any data.
350
+ '''
351
+
352
+ url = f'/api/v1/policies/data/{asset_id}'
353
+ return fortidlp_connection.delete(url)
354
+
355
+ class PoliciesData:
356
+ '''
357
+ Class PoliciesData
358
+ Description: Return a list of policies data.
359
+ '''
360
+
361
+ def list_policies_data(self, filter: list = None, results_per_page: int = 100) -> tuple[bool, None]:
362
+ '''
363
+ Class PoliciesData
364
+ Description: Return a list of policies data.
365
+
366
+ Args:
367
+ filter (list): List of filters to apply to the policies data.
368
+ results_per_page (int): Number of results per page.
369
+
370
+ Returns:
371
+ bool: Status of the request (True or False).
372
+ None: This function does not return any data.
373
+ '''
374
+
375
+ url = '/api/v1/policies/data'
376
+ return fortidlp_connection.get(url)
377
+
378
+ def get_policy_data(self, policy_id: str) -> tuple[bool, None]:
379
+ '''
380
+ Class PoliciesData
381
+ Description: Return a specific policy data.
382
+
383
+ Args:
384
+ policy_id (str): The ID of the policy data to retrieve.
385
+
386
+ Returns:
387
+ bool: Status of the request (True or False).
388
+ None: This function does not return any data.
389
+ '''
390
+
391
+ url = f'/api/v1/policies/data/{policy_id}'
392
+ return fortidlp_connection.get(url)
393
+
394
+ def delete_policy_data(self, policy_id: str) -> tuple[bool, None]:
395
+ '''
396
+ Class PoliciesData
397
+ Description: Delete a specific policy data.
398
+
399
+ Args:
400
+ policy_id (str): The ID of the policy data to delete.
401
+
402
+ Returns:
403
+ bool: Status of the request (True or False).
404
+ None: This function does not return any data.
405
+ '''
406
+
407
+ url = f'/api/v1/policies/data/{policy_id}'
408
+ return fortidlp_connection.delete(url)
409
+
410
+ class Incidents:
411
+ '''
412
+ Class Incidents
413
+ Description: Return a list of incidents.
414
+ '''
415
+
416
+ def search_incidents(self, filter: list = [], include_agents: str = True, include_cluster_data: str = True, include_labels: str = True, include_users: str = True, results_per_page: int = 100) -> dict:
417
+ '''
418
+ Class Incidents
419
+ Description: Return a list of incidents.
420
+
421
+ Args:
422
+ filter (list): List of filters to apply to the incidents.
423
+ include_agents (bool): Whether to include agents in the response.
424
+ include_cluster_data (bool): Whether to include cluster data in the response.
425
+ include_labels (bool): Whether to include labels in the response.
426
+ include_users (bool): Whether to include users in the response.
427
+
428
+ Returns:
429
+ bool: Status of the request (True or False).
430
+ None: This function does not return any data.
431
+ '''
432
+
433
+ parameters = {
434
+ "filter": filter if isinstance(filter, list) else [filter]
435
+ }
436
+ if include_agents:
437
+ parameters["include_agents"] = include_agents
438
+ if include_cluster_data:
439
+ parameters["include_cluster_data"] = include_cluster_data
440
+ if include_labels:
441
+ parameters["include_labels"] = include_labels
442
+ if include_users:
443
+ parameters["include_users"] = include_users
444
+
445
+ url = '/api/v2/incidents/search'
446
+ if results_per_page:
447
+ url = f"{url}?results_per_page={results_per_page}"
448
+
449
+ return fortidlp_connection.send(url, params=parameters)
450
+
451
+ # Function to update incident status:
452
+ # This function receives: {
453
+ # "all": true,
454
+ # "filter": [ ],
455
+ # "reason": "string",
456
+ # "status": "RESOLVE"
457
+ # }
458
+
459
+ def update_status(self, status: str, all: Optional[bool] = None, filter: Optional[list] = None, reason = None) -> dict:
460
+ '''
461
+ Class Incidents
462
+ Description: Update the status of incidents.
463
+
464
+ Args:
465
+ all (bool): Whether to update all incidents.
466
+ filter (list): List of filters to apply to the incidents.
467
+ reason (str): Reason for the status update.
468
+ status (str): New status for the incidents.
469
+
470
+ Returns:
471
+ bool: Status of the request (True or False).
472
+ None: This function does not return any data.
473
+ '''
474
+
475
+ parameters = {
476
+ "status": status
477
+ }
478
+ if filter and not all:
479
+ parameters["filter"] = filter if isinstance(filter, list) else [filter]
480
+ if all and not filter:
481
+ parameters["all"] = all
482
+ if reason:
483
+ parameters["reason"] = reason
484
+
485
+ url = '/api/v2/incidents/status'
486
+ return fortidlp_connection.send(url, params=parameters)
487
+
488
+ class SaaS:
489
+ '''
490
+ Class SaaS
491
+ Description: Return a list of SaaS applications.
492
+ '''
493
+
494
+ def change_state(self, state: str, reason: str, all: Optional[bool] = None, filter: Optional[list] = None) -> dict:
495
+ '''
496
+ Class SaaS
497
+ Description: Change the state of a SaaS application.
498
+
499
+ Args:
500
+ state (str): The new state for the SaaS application.
501
+ all (bool): Whether to apply the state change to all applications.
502
+ filter (list): List of filters to apply to the SaaS applications.
503
+ reason (str): Reason for the state change.
504
+
505
+ Returns:
506
+ bool: Status of the request (True or False).
507
+ None: This function does not return any data.
508
+ '''
509
+
510
+ url = f'/api/v2/saas-applications/state'
511
+ data = {
512
+ "state": state,
513
+ "reason": reason,
514
+ }
515
+ if all and not filter:
516
+ data["all"] = all
517
+ if not all and filter:
518
+ data["filter"] = filter if isinstance(filter, list) else [filter]
519
+
520
+ return fortidlp_connection.send(url, params=data)
521
+
522
+ class Agents:
523
+ '''
524
+ Class Agents
525
+ Description: Return a list of agents.
526
+ '''
527
+
528
+ def get_agents(self, filter: list = [], results_per_page: int = 100, sort_order: str = "asc", cursor: Optional[str] = None) -> dict:
529
+ '''
530
+ Class Agents
531
+ Description: Return a list of agents.
532
+
533
+ Args:
534
+ filter: (list): List of filters to apply to the agents.
535
+ include_actions: (bool): Whether to include actions in the response.
536
+ include_health: (bool): Whether to include health data in the response.
537
+ include_labels: (bool): Whether to include labels in the response.
538
+ include_users: (bool): Whether to include users in the response.
539
+
540
+ Returns:
541
+ bool: Status of the request (True or False).
542
+ None: This function does not return any data.
543
+ '''
544
+
545
+ url = f'/api/v2/agents/search'
546
+
547
+ parameters = {}
548
+ if filter:
549
+ parameters["filter"] = filter if isinstance(filter, list) else [filter]
550
+ if cursor:
551
+ parameters["cursor"] = cursor
552
+
553
+ if results_per_page:
554
+ url = f"{url}?results_per_page={results_per_page}&sort_order={sort_order}"
555
+
556
+ return fortidlp_connection.send(url, params=parameters)
557
+
558
+ def update_status(self, filter: Optional[list], new_state: Optional[str], reason: Optional[str]) -> dict:
559
+ '''
560
+ Class Agents
561
+ Description: Update the status of agents.
562
+
563
+ Args:
564
+ filter (list): List of filters to apply to the agents.
565
+ new_state (str): New state for the agents.
566
+ reason (str): Reason for the status update.
567
+
568
+ Returns:
569
+ bool: Status of the request (True or False).
570
+ None: This function does not return any data.
571
+ '''
572
+
573
+ url = f'/api/v2/agents/state'
574
+ data = {
575
+ "new_state": new_state,
576
+ "reason": reason
577
+ }
578
+ if filter:
579
+ data["filter"] = filter if isinstance(filter, list) else [filter]
580
+
581
+ return fortidlp_connection.send(url, params=data)
582
+
583
+ # Function Delete archived agents
584
+ #{
585
+ # "agent_ids": [
586
+ # "string"
587
+ # ],
588
+ # "archived_days": "string",
589
+ # "inactive_days": 0,
590
+ # "never_reported": true,
591
+ # "revoked_days": "string"
592
+ # }
593
+
594
+ def delete_archived_agents(self, agent_ids: list, archived_days: Optional[str] = None, inactive_days: Optional[int] = None, never_reported: Optional[bool] = None, revoked_days: Optional[str] = None) -> dict:
595
+
596
+ data = {
597
+ "agent_ids": agent_ids if isinstance(agent_ids, list) else [agent_ids]
598
+ }
599
+
600
+ if archived_days:
601
+ data["archived_days"] = archived_days
602
+ if inactive_days:
603
+ data["inactive_days"] = inactive_days
604
+ if never_reported is not None:
605
+ data["never_reported"] = never_reported
606
+ if revoked_days:
607
+ data["revoked_days"] = revoked_days
608
+
609
+ print(json.dumps(data, indent=4))
610
+
611
+ url = '/api/v1/admin/agents/archived/delete'
612
+ return fortidlp_connection.insert(url, params=data)
613
+
614
+ def assign_labels(self, agent_ids: list[str], label_ids: list[str]) -> dict:
615
+ '''
616
+ Class Labels
617
+ Description: Assign labels to agents.
618
+
619
+ Args:
620
+ agent_ids (list[str], optional): List of agent IDs to assign labels to.
621
+ label_ids (list[str], optional): List of label IDs to assign.
622
+
623
+ Returns:
624
+ bool: Status of the request (True or False).
625
+ None: This function does not return any data.
626
+ '''
627
+
628
+ url = '/api/v1/admin/agents/labels/add'
629
+ param = {
630
+ "agent_ids": agent_ids if isinstance(agent_ids, list) else [agent_ids],
631
+ "label_ids": label_ids if isinstance(label_ids, list) else [label_ids]
632
+ }
633
+
634
+ return fortidlp_connection.insert(url, params=param)
635
+
636
+ def unassign_labels(self, agent_ids: list[str], label_ids: list[str]) -> dict:
637
+ '''
638
+ Class Labels
639
+ Description: Unassign labels from agents.
640
+ Args:
641
+ agent_ids (list[str], optional): List of agent IDs to unassign labels from.
642
+ label_ids (list[str], optional): List of label IDs to unassign.
643
+ Returns:
644
+ bool: Status of the request (True or False).
645
+ None: This function does not return any data.
646
+ '''
647
+
648
+ url = '/api/v1/admin/agents/labels/remove'
649
+ param = {
650
+ "agent_ids": agent_ids if isinstance(agent_ids, list) else [agent_ids],
651
+ "label_ids": label_ids if isinstance(label_ids, list) else [label_ids]
652
+ }
653
+ return fortidlp_connection.insert(url, params=param)
654
+
655
+ class AgentConfigs:
656
+
657
+ def get_agent_configs(self) -> dict:
658
+ '''
659
+ Class AgentConfigs
660
+ Description: Get all agent configurations.
661
+
662
+ Returns:
663
+ dict: The response from the API.
664
+ '''
665
+ url = '/api/v1/agent-configs'
666
+ return fortidlp_connection.get(url)
667
+
668
+
669
+ def delete_agent_config(self, config_id: str) -> dict:
670
+ '''
671
+ Class AgentConfigs
672
+ Description: Delete an agent configuration.
673
+
674
+ Args:
675
+ config_id (str): The ID of the agent configuration to delete.
676
+
677
+ Returns:
678
+ bool: Status of the request (True or False).
679
+ None: This function does not return any data.
680
+ '''
681
+
682
+ url = f'/api/v1/agent-configs/{config_id}'
683
+ return fortidlp_connection.delete(url)
684
+
685
+ class Labels:
686
+ '''Class Labels
687
+ Description: Return a list of labels.
688
+ '''
689
+
690
+ def create(self, name: str, description: Optional[str] = None, category: Optional[str] = None, anonymise: Optional[bool] = False, flagged: Optional[bool] = False) -> dict:
691
+ '''
692
+ Class Labels
693
+ Description: Create a new label.
694
+
695
+ Args:
696
+ name (str): The name of the label - Mandatory.
697
+ description (str, optional): The description of the label.
698
+ category (str, optional): The category of the label.
699
+ anonymise (bool, optional): Whether to anonymise the label. Defaults to False.
700
+ flagged (bool, optional): Whether the label is flagged. Defaults to False.
701
+
702
+ Returns:
703
+ bool: Status of the request (True or False).
704
+ None: This function does not return any data.
705
+ '''
706
+
707
+ url = '/api/v1/labels'
708
+ data = {
709
+ "name": name,
710
+ }
711
+ if description:
712
+ data["description"] = description
713
+ if category:
714
+ data["category"] = category
715
+ if anonymise is not None:
716
+ data["anonymise"] = anonymise
717
+ if flagged is not None:
718
+ data["flagged"] = flagged
719
+
720
+ return fortidlp_connection.send(url, params=data)
721
+
722
+ def delete(self, id: str, force: Optional[bool] = False) -> dict:
723
+ '''
724
+ Class Labels
725
+ Description: Delete a label by its ID.
726
+
727
+ Args:
728
+ id (str): The ID of the label to delete.
729
+ force (bool, optional): Whether to force delete the label. Defaults to False.
730
+
731
+ Returns:
732
+ bool: Status of the request (True or False).
733
+ None: This function does not return any data.
734
+ '''
735
+
736
+ url = f'/api/v1/labels/{id}'
737
+ return fortidlp_connection.delete(url)
738
+
739
+ def get_labels(self, filter: list = [], results_per_page: int = 100, sort_order: str = "asc", cursor: Optional[str] = None) -> dict:
740
+ '''
741
+ Class Labels
742
+ Description: Return a list of labels.
743
+ Args:
744
+ filter (list): List of filters to apply to the labels.
745
+ results_per_page (int): Number of results per page.
746
+ sort_order (str): Sort order for the results, either "asc" or "desc".
747
+ cursor (str, optional): Cursor for pagination.
748
+ Returns:
749
+
750
+ bool: Status of the request (True or False).
751
+ None: This function does not return any data.
752
+ '''
753
+ url = '/api/v1/labels/search'
754
+ parameters = {}
755
+ if filter:
756
+ parameters["filter"] = filter if isinstance(filter, list) else [filter]
757
+
758
+ if results_per_page:
759
+ url = f"{url}?results_per_page={results_per_page}"
760
+ if sort_order:
761
+ url = f"{url}&sort_order={sort_order}"
762
+ if cursor:
763
+ url = f"{url}&cursor={cursor}"
764
+
765
+ return fortidlp_connection.send(url, params=parameters)
766
+
767
+ debug = None
768
+ ssl_verification = True
769
+
770
+ def ignore_certificate():
771
+ global ssl_verification
772
+ ssl_verification = False
773
+ print("[!] - We strongly advise you to enable SSL validations. Use this at your own risk!")
774
+
775
+ def enable_debug():
776
+ global debug
777
+ debug = True
778
+
779
+ def auth( host: str, access_token: str):
780
+ global debug
781
+ global fortidlp_connection
782
+ login = AuthenticationHandler()
783
+
784
+ # ManagementHost = re.search(r'(https?://)?(([a-zA-Z0-9]+)(\.[a-zA-Z0-9.-]+))', host)
785
+ # host = ManagementHost.group(2)
786
+
787
+ headers, host = login.get_headers(
788
+ fdlp_host=host,
789
+ access_token=access_token,
790
+ )
791
+
792
+ if headers is None:
793
+ status = False
794
+ data = host
795
+ else:
796
+ status = True
797
+ data = 'AUTHENTICATION_SUCCEEDED'
798
+
799
+ fortidlp_connection = APIHandler()
800
+ authentication = fortidlp_connection.conn(headers, host, debug, ssl_verification)
801
+
802
+ cur_dir = os.path.dirname(__file__)
803
+
804
+ return {
805
+ 'status': status,
806
+ 'data': data
807
+ }
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: fortidlp
3
+ Version: 0.90
4
+ Summary: This FortiDLP module is an open-source Python library that simplifies interaction with the FortiDLP Cloud API.
5
+ Author: Rafael Foster
6
+ Author-email: rafaelgfoster@gmail.com
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Dist: Requests==2.31.0
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: classifier
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: summary
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ requirements.txt
5
+ setup.py
6
+ fortidlp/__init__.py
7
+ fortidlp/auth.py
8
+ fortidlp/connector.py
9
+ fortidlp/fortidlp.py
10
+ fortidlp.egg-info/PKG-INFO
11
+ fortidlp.egg-info/SOURCES.txt
12
+ fortidlp.egg-info/dependency_links.txt
13
+ fortidlp.egg-info/requires.txt
14
+ fortidlp.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ Requests==2.31.0
@@ -0,0 +1 @@
1
+ fortidlp
@@ -0,0 +1,2 @@
1
+ Requests==2.31.0
2
+ setuptools==45.2.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
fortidlp-0.90/setup.py ADDED
@@ -0,0 +1,18 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="fortidlp",
5
+ version="0.90",
6
+ author="Rafael Foster",
7
+ author_email="rafaelgfoster@gmail.com",
8
+ description="This FortiDLP module is an open-source Python library that simplifies interaction with the FortiDLP Cloud API.",
9
+ packages=find_packages(),
10
+ install_requires=["Requests==2.31.0"],
11
+ license="MIT",
12
+ license_files=("LICENSE"),
13
+ classifiers=[
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ ],
18
+ )