zscaler-sdk-python 0.9.6__tar.gz → 0.10.0__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.
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/PKG-INFO +1 -1
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/pyproject.toml +1 -1
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/__init__.py +1 -1
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/__init__.py +55 -9
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/labels.py +1 -1
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/locations.py +10 -21
- zscaler_sdk_python-0.10.0/zscaler/zia/pac_files.py +429 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/security.py +17 -5
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/users.py +1 -1
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/__init__.py +14 -14
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/policies.py +24 -50
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/README.md +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/cache/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/cache/cache.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/cache/no_op_cache.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/cache/zscaler_cache.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/constants.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/errors/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/errors/error.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/errors/http_error.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/errors/zscaler_api_error.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/exceptions/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/exceptions/exceptions.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/logger.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/ratelimiter/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/ratelimiter/ratelimiter.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/user_agent.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/utils.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcc/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcc/client.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcc/devices.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcc/secrets.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/activation.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/client.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/ecgroups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/locations.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/provisioning.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/__init__.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/admin.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/alerts.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/apps.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/devices.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/filters.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/inventory.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/troubleshooting.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/users.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zdx/zdx_client.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/activate.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/apptotal.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/audit_logs.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/authentication_settings.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/client.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/cloud_apps.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/cloudappcontrol.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/device_management.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/dlp.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/errors.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/firewall.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/forwarding_control.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/isolation_profile.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/sandbox.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/ssl_inspection.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/traffic.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/url_categories.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/url_filtering.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/web_dlp.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/workload_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/zpa_gateway.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/README.md +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/app_segments.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/app_segments_inspection.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/app_segments_pra.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/authdomains.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/certificates.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/client.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/cloud_connector_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/connectors.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/emergency_access.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/errors.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/idp.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/inspection.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/isolation.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/lss.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/machine_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/microtenants.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/posture_profiles.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/privileged_remote_access.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/provisioning.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/saml_attributes.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/scim_attributes.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/scim_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/segment_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/server_groups.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/servers.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/service_edges.py +0 -0
- {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/trusted_networks.py +0 -0
|
@@ -39,6 +39,7 @@ from zscaler.zia.forwarding_control import ForwardingControlAPI
|
|
|
39
39
|
from zscaler.zia.cloudappcontrol import CloudAppControlAPI
|
|
40
40
|
from zscaler.zia.isolation_profile import IsolationProfileAPI
|
|
41
41
|
from zscaler.zia.labels import RuleLabelsAPI
|
|
42
|
+
from zscaler.zia.pac_files import PacFilesAPI
|
|
42
43
|
from zscaler.zia.locations import LocationsAPI
|
|
43
44
|
from zscaler.zia.sandbox import CloudSandboxAPI
|
|
44
45
|
from zscaler.zia.security import SecurityPolicyAPI
|
|
@@ -406,6 +407,14 @@ class ZIAClientHelper(ZIAClient):
|
|
|
406
407
|
page=None,
|
|
407
408
|
pagesize=None,
|
|
408
409
|
search=None,
|
|
410
|
+
max_page_size=1000, # Default to 1000, can be adjusted based on endpoint constraints
|
|
411
|
+
max_items=None, # Maximum number of items to retrieve across pages
|
|
412
|
+
max_pages=None, # Maximum number of pages to retrieve
|
|
413
|
+
type=None, # Specify type of VPN credentials (CN, IP, UFQDN, XAUTH)
|
|
414
|
+
include_only_without_location=None, # Include only VPN credentials not associated with any location
|
|
415
|
+
location_id=None, # VPN credentials for a specific location ID
|
|
416
|
+
managed_by=None, # VPN credentials managed by a given partner
|
|
417
|
+
prefix=None, # VPN credentials managed by a given partner
|
|
409
418
|
):
|
|
410
419
|
"""
|
|
411
420
|
Fetches paginated data from the API based on specified parameters and handles pagination.
|
|
@@ -416,6 +425,13 @@ class ZIAClientHelper(ZIAClient):
|
|
|
416
425
|
page (int): Specific page number to fetch. Defaults to 1 if not provided.
|
|
417
426
|
pagesize (int): Number of items per page, default is 100, with a maximum of 1000.
|
|
418
427
|
search (str): Search query to filter the results.
|
|
428
|
+
max_items (int): Maximum number of items to retrieve.
|
|
429
|
+
max_pages (int): Maximum number of pages to fetch.
|
|
430
|
+
type (str, optional): Type of VPN credentials (e.g., CN, IP, UFQDN, XAUTH).
|
|
431
|
+
include_only_without_location (bool, optional): Filter to include only VPN credentials not associated with a location.
|
|
432
|
+
location_id (int, optional): Retrieve VPN credentials for the specified location ID.
|
|
433
|
+
managed_by (int, optional): Retrieve VPN credentials managed by the specified partner.
|
|
434
|
+
prefix (int, optional): Retrieve VPN credentials managed by the specified partner.
|
|
419
435
|
|
|
420
436
|
Returns:
|
|
421
437
|
tuple: A tuple containing:
|
|
@@ -428,15 +444,29 @@ class ZIAClientHelper(ZIAClient):
|
|
|
428
444
|
"UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
|
|
429
445
|
"EMPTY_RESULTS": "No results found for page {page}.",
|
|
430
446
|
}
|
|
431
|
-
|
|
432
|
-
params = {}
|
|
433
|
-
params["page"] = page if page is not None else 1 # Default to page 1
|
|
434
|
-
params["pagesize"] = min(pagesize if pagesize is not None else 100, 1000) # Default pagesize is 100, max 1000
|
|
435
447
|
|
|
448
|
+
# Initialize pagination parameters
|
|
449
|
+
params = {
|
|
450
|
+
"page": page if page is not None else 1, # Start at page 1 if not specified
|
|
451
|
+
"pagesize": min(pagesize if pagesize is not None else 100, max_page_size), # Apply max_page_size limit
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# Add optional filters to the params if provided
|
|
436
455
|
if search:
|
|
437
456
|
params["search"] = search
|
|
457
|
+
if type:
|
|
458
|
+
params["type"] = type
|
|
459
|
+
if include_only_without_location is not None:
|
|
460
|
+
params["includeOnlyWithoutLocation"] = include_only_without_location
|
|
461
|
+
if location_id:
|
|
462
|
+
params["locationId"] = location_id
|
|
463
|
+
if managed_by:
|
|
464
|
+
params["managedBy"] = managed_by
|
|
465
|
+
if prefix:
|
|
466
|
+
params["prefix"] = prefix
|
|
438
467
|
|
|
439
468
|
ret_data = []
|
|
469
|
+
total_collected = 0
|
|
440
470
|
|
|
441
471
|
try:
|
|
442
472
|
while True:
|
|
@@ -464,18 +494,27 @@ class ZIAClientHelper(ZIAClient):
|
|
|
464
494
|
return BoxList([]), error_msg
|
|
465
495
|
|
|
466
496
|
data = convert_keys_to_snake(response_data)
|
|
467
|
-
|
|
497
|
+
|
|
468
498
|
# If searching for a specific item, stop if we find a match
|
|
469
499
|
if search:
|
|
470
500
|
for item in data:
|
|
471
501
|
if item.get("name") == search:
|
|
472
502
|
ret_data.append(item)
|
|
473
503
|
return BoxList(ret_data), None
|
|
474
|
-
|
|
475
|
-
#
|
|
504
|
+
|
|
505
|
+
# Limit data collection based on max_items
|
|
506
|
+
if max_items is not None:
|
|
507
|
+
data = data[: max_items - total_collected] # Limit items on the current page
|
|
476
508
|
ret_data.extend(data)
|
|
509
|
+
total_collected += len(data)
|
|
510
|
+
|
|
511
|
+
# Check if we've reached max_items or max_pages limits
|
|
512
|
+
if (max_items is not None and total_collected >= max_items) or (
|
|
513
|
+
max_pages is not None and params["page"] >= max_pages
|
|
514
|
+
):
|
|
515
|
+
break
|
|
477
516
|
|
|
478
|
-
# Stop if we've processed all available pages
|
|
517
|
+
# Stop if we've processed all available pages (i.e., less than requested page size)
|
|
479
518
|
if len(data) < params["pagesize"]:
|
|
480
519
|
break
|
|
481
520
|
|
|
@@ -492,7 +531,6 @@ class ZIAClientHelper(ZIAClient):
|
|
|
492
531
|
|
|
493
532
|
return BoxList(ret_data), None
|
|
494
533
|
|
|
495
|
-
|
|
496
534
|
@property
|
|
497
535
|
def admin_and_role_management(self):
|
|
498
536
|
"""
|
|
@@ -677,3 +715,11 @@ class ZIAClientHelper(ZIAClient):
|
|
|
677
715
|
|
|
678
716
|
"""
|
|
679
717
|
return WorkloadGroupsAPI(self)
|
|
718
|
+
|
|
719
|
+
@property
|
|
720
|
+
def pac_files(self):
|
|
721
|
+
"""
|
|
722
|
+
The interface object for the :ref:`ZIA Pac Files interface <zia-pac_files>`.
|
|
723
|
+
|
|
724
|
+
"""
|
|
725
|
+
return PacFilesAPI(self)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from box import Box, BoxList
|
|
19
19
|
from requests import Response
|
|
20
20
|
|
|
21
|
-
from zscaler.utils import
|
|
21
|
+
from zscaler.utils import snake_to_camel
|
|
22
22
|
from zscaler.zia import ZIAClient
|
|
23
23
|
|
|
24
24
|
|
|
@@ -188,7 +188,7 @@ class LocationsAPI:
|
|
|
188
188
|
Additional notes or information regarding the location or sub-location. The description cannot
|
|
189
189
|
exceed 1024 characters.
|
|
190
190
|
static_location_groups (list): IDs for static location groups.
|
|
191
|
-
|
|
191
|
+
|
|
192
192
|
Returns:
|
|
193
193
|
:obj:`Box`: The newly created location resource record
|
|
194
194
|
|
|
@@ -258,14 +258,8 @@ class LocationsAPI:
|
|
|
258
258
|
... pprint(sub_location)
|
|
259
259
|
|
|
260
260
|
"""
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
self.rest,
|
|
264
|
-
f"locations/{location_id}/sublocations",
|
|
265
|
-
max_pages=1,
|
|
266
|
-
**kwargs,
|
|
267
|
-
)
|
|
268
|
-
)
|
|
261
|
+
list, _ = self.rest.get_paginated_data(path=f"/locations/{location_id}/sublocations", **kwargs)
|
|
262
|
+
return list
|
|
269
263
|
|
|
270
264
|
def list_locations_lite(self, **kwargs) -> BoxList:
|
|
271
265
|
"""
|
|
@@ -305,7 +299,8 @@ class LocationsAPI:
|
|
|
305
299
|
... print(location)
|
|
306
300
|
|
|
307
301
|
"""
|
|
308
|
-
|
|
302
|
+
list, _ = self.rest.get_paginated_data(path="/locations/lite", **kwargs)
|
|
303
|
+
return list
|
|
309
304
|
|
|
310
305
|
def update_location(self, location_id: str, **kwargs) -> Box:
|
|
311
306
|
"""
|
|
@@ -504,14 +499,8 @@ class LocationsAPI:
|
|
|
504
499
|
Get a list of all configured location groups:
|
|
505
500
|
>>> location = zia.locations.list_location_groups()
|
|
506
501
|
"""
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
self.rest,
|
|
510
|
-
f"locations/groups",
|
|
511
|
-
max_pages=1,
|
|
512
|
-
**kwargs,
|
|
513
|
-
)
|
|
514
|
-
)
|
|
502
|
+
list, _ = self.rest.get_paginated_data(path="/locations/groups", **kwargs)
|
|
503
|
+
return list
|
|
515
504
|
|
|
516
505
|
def get_location_group_by_id(self, group_id: int) -> Box:
|
|
517
506
|
"""
|
|
@@ -665,5 +654,5 @@ class LocationsAPI:
|
|
|
665
654
|
returned. Ensure you narrow your search result as much as possible to avoid this.
|
|
666
655
|
|
|
667
656
|
"""
|
|
668
|
-
|
|
669
|
-
|
|
657
|
+
list, _ = self.rest.get_paginated_data(path="/region/search", **kwargs)
|
|
658
|
+
return list
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023, Zscaler Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
# copyright notice and this permission notice appear in all copies.
|
|
8
|
+
#
|
|
9
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from box import Box, BoxList
|
|
19
|
+
from requests import Response
|
|
20
|
+
|
|
21
|
+
from zscaler.utils import snake_to_camel
|
|
22
|
+
from zscaler.zia import ZIAClient
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PacFilesAPI:
|
|
26
|
+
def __init__(self, client: ZIAClient):
|
|
27
|
+
self.rest = client
|
|
28
|
+
|
|
29
|
+
def list_pac_files(self, **kwargs) -> BoxList:
|
|
30
|
+
"""
|
|
31
|
+
Returns the list of ZIA Pac Files.
|
|
32
|
+
|
|
33
|
+
Keyword Args:
|
|
34
|
+
**max_items (int, optional):
|
|
35
|
+
The maximum number of items to request before stopping iteration.
|
|
36
|
+
**max_pages (int, optional):
|
|
37
|
+
The maximum number of pages to request before stopping iteration.
|
|
38
|
+
**page_size (int, optional):
|
|
39
|
+
Specifies the page size. The default size is 100, but the maximum size is 1000.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
:obj:`BoxList`: The list of Rule Labels configured in ZIA.
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
List Rule Labels using default settings:
|
|
46
|
+
|
|
47
|
+
>>> for label in zia.labels.list_labels():
|
|
48
|
+
... print(label)
|
|
49
|
+
|
|
50
|
+
List labels, limiting to a maximum of 10 items:
|
|
51
|
+
|
|
52
|
+
>>> for label in zia.labels.list_labels(max_items=10):
|
|
53
|
+
... print(label)
|
|
54
|
+
|
|
55
|
+
List labels, returning 200 items per page for a maximum of 2 pages:
|
|
56
|
+
|
|
57
|
+
>>> for label in zia.labels.list_labels(page_size=200, max_pages=2):
|
|
58
|
+
... print(label)
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
list, _ = self.rest.get_paginated_data(path="/pacFiles", **kwargs)
|
|
62
|
+
return list
|
|
63
|
+
|
|
64
|
+
def get_pac_file(self, pac_id: str, filter: str = None) -> Box:
|
|
65
|
+
"""
|
|
66
|
+
Returns the PAC file details for a given PAC ID.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
pac_id (str): The unique identifier for the PAC file.
|
|
70
|
+
filter (str, optional): Excludes specific information about the PAC file from
|
|
71
|
+
the response such as the PAC file content.
|
|
72
|
+
Accepts only the value 'pac_content'.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
:obj:`Box`: The PAC file resource record.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> pac_file = zia.get_pac_file('12345', filter='pac_content')
|
|
79
|
+
"""
|
|
80
|
+
# Construct URL with optional filter
|
|
81
|
+
url = f"/pacFiles/{pac_id}/version"
|
|
82
|
+
if filter == "pac_content":
|
|
83
|
+
url += f"?filter={filter}"
|
|
84
|
+
|
|
85
|
+
response = self.rest.get(url)
|
|
86
|
+
if isinstance(response, Response):
|
|
87
|
+
if response.status_code != 200:
|
|
88
|
+
return None
|
|
89
|
+
return response
|
|
90
|
+
|
|
91
|
+
def get_pac_file_version(self, pac_id: str, pac_version: str, filter: str = None) -> Box:
|
|
92
|
+
"""
|
|
93
|
+
Returns the PAC file version details for a given PAC ID and version.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
pac_id (str): The unique identifier for the PAC file.
|
|
97
|
+
pac_version (str): The specific version of the PAC file.
|
|
98
|
+
filter (str, optional): Excludes specific information about the PAC file from
|
|
99
|
+
the response such as the PAC file content.
|
|
100
|
+
Accepts only the value 'pac_content'.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
:obj:`Box`: The PAC file version resource record.
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> pac_file_version = zia.get_pac_file_version('12345', '1', filter='pac_content')
|
|
107
|
+
"""
|
|
108
|
+
# Construct URL with optional filter
|
|
109
|
+
url = f"/pacFiles/{pac_id}/version/{pac_version}"
|
|
110
|
+
if filter == "pac_content":
|
|
111
|
+
url += f"?filter={filter}"
|
|
112
|
+
|
|
113
|
+
response = self.rest.get(url)
|
|
114
|
+
if isinstance(response, Response):
|
|
115
|
+
if response.status_code != 200:
|
|
116
|
+
return None
|
|
117
|
+
return response
|
|
118
|
+
|
|
119
|
+
def validate_pac_file(self, pac_file_content: str) -> Box:
|
|
120
|
+
"""
|
|
121
|
+
Sends the PAC file content for validation and returns the validation result.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
pac_file_content (str): The PAC file content to be validated.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Box: The API response containing the validation result.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
Exception: If the API call fails with a non-2xx status code.
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
To validate PAC file content:
|
|
134
|
+
|
|
135
|
+
.. code-block:: python
|
|
136
|
+
|
|
137
|
+
pac_file_content = '''
|
|
138
|
+
function FindProxyForURL(url, host) {
|
|
139
|
+
return "PROXY gateway.example.com:80";
|
|
140
|
+
}
|
|
141
|
+
'''
|
|
142
|
+
|
|
143
|
+
response = client.validate_pac_file(pac_file_content=pac_file_content)
|
|
144
|
+
if response["success"]:
|
|
145
|
+
print("PAC file is valid.")
|
|
146
|
+
else:
|
|
147
|
+
print("PAC file validation failed.")
|
|
148
|
+
"""
|
|
149
|
+
# Send only the PAC content as the raw body
|
|
150
|
+
response = self.rest.post("pacFiles/validate", data=pac_file_content)
|
|
151
|
+
|
|
152
|
+
# Check if the response is successful and already in Box format
|
|
153
|
+
if isinstance(response, Box):
|
|
154
|
+
return response
|
|
155
|
+
elif isinstance(response, Response):
|
|
156
|
+
if response.status_code != 200:
|
|
157
|
+
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
158
|
+
# Convert to Box if needed
|
|
159
|
+
return Box(response.json())
|
|
160
|
+
|
|
161
|
+
def add_pac_file(
|
|
162
|
+
self,
|
|
163
|
+
name: str,
|
|
164
|
+
description: str,
|
|
165
|
+
domain: str,
|
|
166
|
+
pac_commit_message: str,
|
|
167
|
+
pac_verification_status: str,
|
|
168
|
+
pac_version_status: str,
|
|
169
|
+
pac_content: str,
|
|
170
|
+
**kwargs,
|
|
171
|
+
) -> Box:
|
|
172
|
+
"""
|
|
173
|
+
Adds a new custom PAC file after validating the PAC content.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
name (str): The name of the new PAC file.
|
|
177
|
+
description (str): The description of the new PAC file.
|
|
178
|
+
domain (str): The domain of your organization to which the PAC file applies.
|
|
179
|
+
pac_commit_message (str): The commit message entered while saving the PAC file version as indicated by the pacVersion field.
|
|
180
|
+
pac_verification_status (str): Indicates the verification status of the PAC file and if any errors are identified in the syntax.
|
|
181
|
+
Supported Values: `VERIFY_NOERR`, `VERIFY_ERR`, `NOVERIFY`
|
|
182
|
+
pac_version_status (str): Version status of the new PAC file.
|
|
183
|
+
Supported Values: `DEPLOYED`, `STAGE`, `LKG`
|
|
184
|
+
pac_content (str): The actual PAC file content to be validated and cloned.
|
|
185
|
+
|
|
186
|
+
Keyword Args:
|
|
187
|
+
Additional optional parameters as key-value pairs.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Box: The newly added PAC file resource record.
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
>>> pac_file = zia.add_pac_file(
|
|
194
|
+
name="Test_Pac_File_01",
|
|
195
|
+
description="Test_Pac_File_01",
|
|
196
|
+
domain="bd-hashicorp.com",
|
|
197
|
+
pacCommitMessage="Test_Pac_File_01",
|
|
198
|
+
pacVerificationStatus="VERIFY_NOERR",
|
|
199
|
+
pacVersionStatus="DEPLOYED",
|
|
200
|
+
pacContent="function FindProxyForURL(url, host) { return 'PROXY gateway.example.com:80'; }"
|
|
201
|
+
)
|
|
202
|
+
"""
|
|
203
|
+
validation_result = self.validate_pac_file(pac_content)
|
|
204
|
+
if not validation_result.success:
|
|
205
|
+
raise Exception("PAC content validation failed: {}".format(validation_result))
|
|
206
|
+
|
|
207
|
+
payload = {
|
|
208
|
+
"name": name,
|
|
209
|
+
"description": description,
|
|
210
|
+
"domain": domain,
|
|
211
|
+
"pacCommitMessage": pac_commit_message,
|
|
212
|
+
"pacVerificationStatus": pac_verification_status,
|
|
213
|
+
"pacVersionStatus": pac_version_status,
|
|
214
|
+
"pacContent": pac_content,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for key, value in kwargs.items():
|
|
218
|
+
payload[snake_to_camel(key)] = value
|
|
219
|
+
|
|
220
|
+
response = self.rest.post("pacFiles", json=payload)
|
|
221
|
+
if isinstance(response, Response):
|
|
222
|
+
if response.status_code != 200:
|
|
223
|
+
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
224
|
+
return Box(response.json())
|
|
225
|
+
return response
|
|
226
|
+
|
|
227
|
+
def clone_pac_file(
|
|
228
|
+
self,
|
|
229
|
+
pac_id: str,
|
|
230
|
+
pac_version: str,
|
|
231
|
+
name: str,
|
|
232
|
+
description: str,
|
|
233
|
+
domain: str,
|
|
234
|
+
pac_commit_message: str,
|
|
235
|
+
pac_verification_status: str,
|
|
236
|
+
pac_version_status: str,
|
|
237
|
+
pac_content: str,
|
|
238
|
+
delete_version: int = None,
|
|
239
|
+
**kwargs,
|
|
240
|
+
) -> Box:
|
|
241
|
+
"""
|
|
242
|
+
Clones an existing PAC file by creating a new PAC file based on the specified PAC ID and version.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
pac_id (str): The unique identifier of the PAC file to be cloned.
|
|
246
|
+
pac_version (str): The specific version of the PAC file to be cloned.
|
|
247
|
+
name (str): The name of the new PAC file.
|
|
248
|
+
domain (str): The domain associated with the new PAC file.
|
|
249
|
+
pac_commit_message (str): Commit message for the new PAC file.
|
|
250
|
+
pac_verification_status (str): Verification status of the new PAC file.
|
|
251
|
+
Supported Values: `VERIFY_NOERR`, `VERIFY_ERR`, `NOVERIFY`
|
|
252
|
+
pac_version_status (str): Version status of the new PAC file.
|
|
253
|
+
Supported Values: `DEPLOYED`, `STAGE`, `LKG`
|
|
254
|
+
pac_content (str): The actual PAC file content to be validated and cloned.
|
|
255
|
+
delete_version (int, optional): Specifies the PAC file version to replace if the version limit of 10 is reached.
|
|
256
|
+
|
|
257
|
+
Keyword Args:
|
|
258
|
+
Additional optional parameters as key-value pairs.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Box: The newly cloned PAC file resource record.
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
>>> pac_file = zia.clone_pac_file(
|
|
265
|
+
pac_id="12345",
|
|
266
|
+
pac_version="1",
|
|
267
|
+
name="Cloned_Pac_File_01",
|
|
268
|
+
description="Cloned_Pac_File_01",
|
|
269
|
+
domain="bd-hashicorp.com",
|
|
270
|
+
pac_commit_message="Clone of Test_Pac_File_01",
|
|
271
|
+
pac_verification_status="VERIFY_NOERR",
|
|
272
|
+
pac_version_status="DEPLOYED",
|
|
273
|
+
pac_content="function FindProxyForURL(url, host) { return 'PROXY gateway.example.com:80'; }",
|
|
274
|
+
delete_version=5
|
|
275
|
+
)
|
|
276
|
+
"""
|
|
277
|
+
# Step 1: Validate the PAC content
|
|
278
|
+
validation_result = self.validate_pac_file(pac_content)
|
|
279
|
+
if not validation_result.success:
|
|
280
|
+
raise Exception("PAC content validation failed: {}".format(validation_result))
|
|
281
|
+
|
|
282
|
+
# Step 2: Check the number of PAC file versions
|
|
283
|
+
pac_file_details = self.get_pac_file(pac_id)
|
|
284
|
+
if isinstance(pac_file_details, Box):
|
|
285
|
+
pac_file_details = pac_file_details.to_list() # Convert Box to a list of dictionaries if needed
|
|
286
|
+
|
|
287
|
+
# Extract pac versions from details
|
|
288
|
+
pac_versions = [entry.get("pacVersion") for entry in pac_file_details] if pac_file_details else []
|
|
289
|
+
total_versions = len(pac_versions)
|
|
290
|
+
|
|
291
|
+
# Step 3: Decide on including delete_version in the URL
|
|
292
|
+
if total_versions >= 10:
|
|
293
|
+
if delete_version is None:
|
|
294
|
+
# If delete_version was not provided, default to the oldest version (smallest number)
|
|
295
|
+
delete_version = min(pac_versions)
|
|
296
|
+
url = f"/pacFiles/{pac_id}/version/{pac_version}?deleteVersion={delete_version}"
|
|
297
|
+
else:
|
|
298
|
+
url = f"/pacFiles/{pac_id}/version/{pac_version}"
|
|
299
|
+
|
|
300
|
+
# Step 4: Construct the payload with mandatory fields
|
|
301
|
+
payload = {
|
|
302
|
+
"name": name,
|
|
303
|
+
"description": description,
|
|
304
|
+
"domain": domain,
|
|
305
|
+
"pacCommitMessage": pac_commit_message,
|
|
306
|
+
"pacVerificationStatus": pac_verification_status,
|
|
307
|
+
"pacVersionStatus": pac_version_status,
|
|
308
|
+
"pacContent": pac_content,
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
# Add any additional optional parameters
|
|
312
|
+
for key, value in kwargs.items():
|
|
313
|
+
payload[snake_to_camel(key)] = value
|
|
314
|
+
|
|
315
|
+
# Step 5: Make the request to clone the PAC file
|
|
316
|
+
response = self.rest.post(url, json=payload)
|
|
317
|
+
if isinstance(response, Response):
|
|
318
|
+
if response.status_code != 200:
|
|
319
|
+
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
320
|
+
return Box(response.json())
|
|
321
|
+
return response
|
|
322
|
+
|
|
323
|
+
def update_pac_file(
|
|
324
|
+
self,
|
|
325
|
+
pac_id: str,
|
|
326
|
+
pac_version: str,
|
|
327
|
+
pac_version_action: str,
|
|
328
|
+
name: str,
|
|
329
|
+
description: str,
|
|
330
|
+
domain: str,
|
|
331
|
+
pac_commit_message: str,
|
|
332
|
+
pac_verification_status: str,
|
|
333
|
+
# pac_version_status: str,
|
|
334
|
+
pac_content: str,
|
|
335
|
+
new_lkg_ver: int = None,
|
|
336
|
+
**kwargs,
|
|
337
|
+
) -> Box:
|
|
338
|
+
"""
|
|
339
|
+
Performs the specified action on the PAC file version and updates the file status.
|
|
340
|
+
Supported actions include deploying, staging, unstaging, and marking or unmarking
|
|
341
|
+
the file as last known good version.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
pac_id (str): The unique identifier of the PAC file to be updated.
|
|
345
|
+
pac_version (str): The specific version of the PAC file to be updated.
|
|
346
|
+
pac_version_action (str): Action to perform on the PAC version.
|
|
347
|
+
Supported Values: `DEPLOY`, `STAGE`, `LKG`, `UNSTAGE`, `REMOVE_LKG`
|
|
348
|
+
name (str): The name of the PAC file.
|
|
349
|
+
description (str): Description of the PAC file.
|
|
350
|
+
domain (str): The domain associated with the PAC file.
|
|
351
|
+
pac_commit_message (str): Commit message for the PAC file.
|
|
352
|
+
pac_verification_status (str): Verification status of the PAC file.
|
|
353
|
+
Supported Values: `VERIFY_NOERR`, `VERIFY_ERR`, `NOVERIFY`
|
|
354
|
+
pac_version_status (str): Version status of the PAC file.
|
|
355
|
+
Supported Values: `DEPLOYED`, `STAGE`, `LKG`
|
|
356
|
+
pac_content (str): The actual PAC file content to be updated.
|
|
357
|
+
new_lkg_ver (int, optional): Specifies a different version to be marked as the last known good version
|
|
358
|
+
if the action is removing the current last known good version.
|
|
359
|
+
|
|
360
|
+
Keyword Args:
|
|
361
|
+
Additional optional parameters as key-value pairs.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Box: The updated PAC file resource record.
|
|
365
|
+
|
|
366
|
+
Example:
|
|
367
|
+
>>> pac_file = zia.update_pac_file(
|
|
368
|
+
pac_id="12345",
|
|
369
|
+
pac_version="1",
|
|
370
|
+
pac_version_action="DEPLOY",
|
|
371
|
+
name="Update_Pac_File_01",
|
|
372
|
+
description="Update_Pac_File_01",
|
|
373
|
+
domain="bd-hashicorp.com",
|
|
374
|
+
pac_commit_message="Update_Pac_File_01",
|
|
375
|
+
pac_verification_status="VERIFY_NOERR",
|
|
376
|
+
pac_version_status="DEPLOYED",
|
|
377
|
+
pac_content="function FindProxyForURL(url, host) { return 'PROXY gateway.example.com:80'; }",
|
|
378
|
+
new_lkg_ver=5
|
|
379
|
+
)
|
|
380
|
+
"""
|
|
381
|
+
# Step 1: Validate the PAC content
|
|
382
|
+
validation_result = self.validate_pac_file(pac_content)
|
|
383
|
+
if not validation_result.success:
|
|
384
|
+
raise Exception("PAC content validation failed: {}".format(validation_result))
|
|
385
|
+
|
|
386
|
+
# Step 2: Construct the URL with mandatory parameters and optional newLKGVer
|
|
387
|
+
url = f"/pacFiles/{pac_id}/version/{pac_version}/action/{pac_version_action}"
|
|
388
|
+
if new_lkg_ver is not None:
|
|
389
|
+
url += f"?newLKGVer={new_lkg_ver}"
|
|
390
|
+
|
|
391
|
+
# Step 3: Construct the payload with required fields
|
|
392
|
+
payload = {
|
|
393
|
+
"name": name,
|
|
394
|
+
"description": description,
|
|
395
|
+
"domain": domain,
|
|
396
|
+
"pacCommitMessage": pac_commit_message,
|
|
397
|
+
"pacVerificationStatus": pac_verification_status,
|
|
398
|
+
# "pacVersionStatus": pac_version_status,
|
|
399
|
+
"pacContent": pac_content,
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
# Add any additional optional parameters
|
|
403
|
+
for key, value in kwargs.items():
|
|
404
|
+
payload[snake_to_camel(key)] = value
|
|
405
|
+
|
|
406
|
+
# Step 4: Make the request to update the PAC file
|
|
407
|
+
response = self.rest.put(url, json=payload)
|
|
408
|
+
if isinstance(response, Response):
|
|
409
|
+
if response.status_code != 200:
|
|
410
|
+
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
411
|
+
return Box(response.json())
|
|
412
|
+
return response
|
|
413
|
+
|
|
414
|
+
def delete_pac_file(self, pac_id):
|
|
415
|
+
"""
|
|
416
|
+
Deletes the specified Pac File.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
pac_id (str): The unique identifier of the Pac File that will be deleted.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
:obj:`int`: The response code for the request.
|
|
423
|
+
|
|
424
|
+
Examples
|
|
425
|
+
>>> user = zia.pac_files.delete_pac_file('99999')
|
|
426
|
+
|
|
427
|
+
"""
|
|
428
|
+
return self.rest.delete(f"pacFiles/{pac_id}").status_code
|
|
429
|
+
|
|
@@ -163,11 +163,23 @@ class SecurityPolicyAPI:
|
|
|
163
163
|
|
|
164
164
|
payload = {"blacklistUrls": url_list}
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
try:
|
|
167
|
+
# Send the POST request to add URLs to the blacklist
|
|
168
|
+
response = self.rest.post("security/advanced/blacklistUrls?action=ADD_TO_LIST", json=payload)
|
|
169
|
+
|
|
170
|
+
# Check if the response includes an empty 'blacklistUrls', signaling no update
|
|
171
|
+
if "blacklistUrls" in response and not response["blacklistUrls"]:
|
|
172
|
+
raise Exception("Failed to add URLs to blacklist: The API response returned an empty 'blacklistUrls' list.")
|
|
173
|
+
|
|
174
|
+
# Verify the URLs were added by checking the current blacklist
|
|
175
|
+
updated_blacklist = self.get_blacklist()
|
|
176
|
+
if all(url in updated_blacklist for url in url_list):
|
|
177
|
+
return updated_blacklist
|
|
178
|
+
else:
|
|
179
|
+
raise Exception("Failed to add URLs to blacklist: URLs were not present in the updated blacklist.")
|
|
180
|
+
|
|
181
|
+
except Exception as exc:
|
|
182
|
+
raise Exception(f"Failed to add URLs to blacklist: {exc}")
|
|
171
183
|
|
|
172
184
|
def replace_blacklist(self, url_list: list) -> BoxList:
|
|
173
185
|
"""
|
|
@@ -378,7 +378,7 @@ class ZPAClientHelper(ZPAClient):
|
|
|
378
378
|
search_field="name",
|
|
379
379
|
max_pages=None,
|
|
380
380
|
max_items=None,
|
|
381
|
-
all_entries=False,
|
|
381
|
+
all_entries=False,
|
|
382
382
|
sort_order=None,
|
|
383
383
|
sort_by=None,
|
|
384
384
|
sort_dir=None,
|
|
@@ -432,15 +432,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
432
432
|
if params is None:
|
|
433
433
|
params = {}
|
|
434
434
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
params["page"] = page if page is not None else 1 # Default to page 1 if not specified
|
|
441
|
-
params["pagesize"] = min(pagesize if pagesize is not None else 20, 500) # Apply maximum constraint and handle default
|
|
435
|
+
# Set initial pagination params
|
|
436
|
+
params["page"] = page or 1
|
|
437
|
+
params["pagesize"] = min(pagesize, 500) if pagesize else 500
|
|
442
438
|
|
|
443
|
-
# Check for microtenantId in function arguments first, then environment variable
|
|
444
439
|
if microtenant_id:
|
|
445
440
|
params["microtenantId"] = microtenant_id
|
|
446
441
|
elif self.microtenant_id and "microtenantId" not in params:
|
|
@@ -472,7 +467,8 @@ class ZPAClientHelper(ZPAClient):
|
|
|
472
467
|
|
|
473
468
|
try:
|
|
474
469
|
while True:
|
|
475
|
-
if max_pages
|
|
470
|
+
# Stop if max_pages reached
|
|
471
|
+
if max_pages is not None and params["page"] > max_pages:
|
|
476
472
|
break
|
|
477
473
|
|
|
478
474
|
should_wait, delay = self.rate_limiter.wait("GET")
|
|
@@ -490,23 +486,27 @@ class ZPAClientHelper(ZPAClient):
|
|
|
490
486
|
|
|
491
487
|
response_data = response.json()
|
|
492
488
|
data = response_data.get("list", [])
|
|
493
|
-
if not data and
|
|
489
|
+
if not data and params["page"] == 1:
|
|
494
490
|
error_msg = ERROR_MESSAGES["EMPTY_RESULTS"]
|
|
495
491
|
logger.warn(error_msg)
|
|
496
492
|
return BoxList([]), error_msg
|
|
497
493
|
|
|
494
|
+
# Convert and extend the collected data
|
|
498
495
|
data = convert_keys_to_snake(data)
|
|
499
496
|
ret_data.extend(data[: max_items - total_collected] if max_items is not None else data)
|
|
500
497
|
total_collected += len(data)
|
|
501
498
|
|
|
499
|
+
# Check if we’ve collected the max_items
|
|
502
500
|
if max_items is not None and total_collected >= max_items:
|
|
503
501
|
break
|
|
504
502
|
|
|
505
|
-
|
|
506
|
-
|
|
503
|
+
# Determine if there is a next page based on totalPages, converting totalPages to an integer if present
|
|
504
|
+
total_pages = int(response_data.get("totalPages", 0)) # Default to 0 if not provided
|
|
505
|
+
if not total_pages or params["page"] >= total_pages:
|
|
507
506
|
break
|
|
508
507
|
|
|
509
|
-
|
|
508
|
+
# Move to the next page
|
|
509
|
+
params["page"] += 1
|
|
510
510
|
|
|
511
511
|
finally:
|
|
512
512
|
time.sleep(2) # Ensure a delay between requests regardless of outcome
|
|
@@ -56,7 +56,7 @@ class PolicySetsAPI:
|
|
|
56
56
|
conditions (list): List of condition dicts or tuples.
|
|
57
57
|
|
|
58
58
|
Returns:
|
|
59
|
-
:obj:`
|
|
59
|
+
:obj:`list`: The conditions template.
|
|
60
60
|
|
|
61
61
|
"""
|
|
62
62
|
template = []
|
|
@@ -78,75 +78,47 @@ class PolicySetsAPI:
|
|
|
78
78
|
"COUNTRY_CODE": [],
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
operators_for_types = {} # Dictionary to store specific operators for each object type
|
|
82
|
+
|
|
83
83
|
for condition in conditions:
|
|
84
|
-
# Check if the first item is an operator, like "AND" or "OR"
|
|
84
|
+
# Check if the first item in a tuple is an operator, like "AND" or "OR"
|
|
85
85
|
if isinstance(condition, tuple) and isinstance(condition[0], str) and condition[0].upper() in ["AND", "OR"]:
|
|
86
|
-
|
|
86
|
+
operator = condition[0].upper()
|
|
87
87
|
condition = condition[1] # The second element is the actual condition
|
|
88
|
+
else:
|
|
89
|
+
operator = "OR" # Default operator if none specified
|
|
88
90
|
|
|
91
|
+
# Process each condition and categorize by object type and operator
|
|
89
92
|
if isinstance(condition, tuple) and len(condition) == 3:
|
|
90
|
-
# Handle each object type according to its pattern
|
|
91
93
|
object_type = condition[0].upper()
|
|
92
94
|
lhs = condition[1]
|
|
93
95
|
rhs = condition[2]
|
|
96
|
+
operand = {"objectType": object_type, "lhs": lhs, "rhs": rhs}
|
|
97
|
+
|
|
98
|
+
# Track the operator for the current object type
|
|
99
|
+
operators_for_types[object_type] = operator
|
|
94
100
|
|
|
95
101
|
if object_type in ["APP", "APP_GROUP"]:
|
|
96
|
-
app_and_app_group_operands.append(
|
|
102
|
+
app_and_app_group_operands.append(operand)
|
|
97
103
|
elif object_type in object_types_to_operands:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"zpn_client_type_exporter",
|
|
101
|
-
"zpn_client_type_machine_tunnel",
|
|
102
|
-
"zpn_client_type_ip_anchoring",
|
|
103
|
-
"zpn_client_type_edge_connector",
|
|
104
|
-
"zpn_client_type_zapp",
|
|
105
|
-
"zpn_client_type_slogger",
|
|
106
|
-
}:
|
|
107
|
-
object_types_to_operands[object_type].append({"objectType": object_type, "lhs": "id", "rhs": rhs})
|
|
108
|
-
elif object_type in [
|
|
109
|
-
"PLATFORM",
|
|
110
|
-
"POSTURE",
|
|
111
|
-
"TRUSTED_NETWORK",
|
|
112
|
-
"SAML",
|
|
113
|
-
"SCIM",
|
|
114
|
-
"SCIM_GROUP",
|
|
115
|
-
"COUNTRY_CODE",
|
|
116
|
-
]:
|
|
117
|
-
object_types_to_operands[object_type].append({"objectType": object_type, "lhs": lhs, "rhs": rhs})
|
|
118
|
-
else:
|
|
119
|
-
object_types_to_operands[object_type].append({"objectType": object_type, "lhs": "id", "rhs": rhs})
|
|
120
|
-
|
|
104
|
+
object_types_to_operands[object_type].append(operand)
|
|
105
|
+
|
|
121
106
|
elif isinstance(condition, dict):
|
|
122
|
-
# This part allows passing operator explicitly through conditions
|
|
123
107
|
if "operator" in condition:
|
|
124
|
-
|
|
125
|
-
continue #
|
|
108
|
+
operators_for_types["default"] = condition["operator"]
|
|
109
|
+
continue # Skip to the next condition after setting the operator
|
|
126
110
|
|
|
127
|
-
# Handle the dictionary logic based on the Go code schema
|
|
128
111
|
condition_template = {}
|
|
129
|
-
# Extracting keys from the condition dictionary
|
|
130
112
|
for key in ["id", "negated", "operator"]:
|
|
131
113
|
if key in condition:
|
|
132
114
|
condition_template[key] = condition[key]
|
|
133
115
|
|
|
134
|
-
# Handling the operands
|
|
135
116
|
operands = condition.get("operands", [])
|
|
136
117
|
condition_template["operands"] = []
|
|
137
118
|
|
|
138
119
|
for operand in operands:
|
|
139
120
|
operand_template = {}
|
|
140
|
-
|
|
141
|
-
# Extracting keys from the operand dictionary
|
|
142
|
-
for operand_key in [
|
|
143
|
-
"id",
|
|
144
|
-
"idp_id",
|
|
145
|
-
"name",
|
|
146
|
-
"lhs",
|
|
147
|
-
"rhs",
|
|
148
|
-
"objectType",
|
|
149
|
-
]:
|
|
121
|
+
for operand_key in ["id", "idp_id", "name", "lhs", "rhs", "objectType"]:
|
|
150
122
|
if operand_key in operand:
|
|
151
123
|
operand_template[operand_key] = operand[operand_key]
|
|
152
124
|
|
|
@@ -154,14 +126,16 @@ class PolicySetsAPI:
|
|
|
154
126
|
|
|
155
127
|
template.append(condition_template)
|
|
156
128
|
|
|
157
|
-
# Combine APP and APP_GROUP operands
|
|
129
|
+
# Combine APP and APP_GROUP operands with their specific operator
|
|
158
130
|
if app_and_app_group_operands:
|
|
159
|
-
|
|
131
|
+
app_group_operator = operators_for_types.get("APP", "OR")
|
|
132
|
+
template.append({"operator": app_group_operator, "operands": app_and_app_group_operands})
|
|
160
133
|
|
|
161
|
-
# Combine other object types into their
|
|
134
|
+
# Combine other object types into their blocks with their respective operator
|
|
162
135
|
for object_type, operands in object_types_to_operands.items():
|
|
163
136
|
if operands:
|
|
164
|
-
|
|
137
|
+
operator = operators_for_types.get(object_type, "OR")
|
|
138
|
+
template.append({"operator": operator, "operands": operands})
|
|
165
139
|
|
|
166
140
|
return template
|
|
167
141
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zcon/admin_and_role_management.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/admin_and_role_management.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zia/authentication_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/app_segments_inspection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/cloud_connector_groups.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.10.0}/zscaler/zpa/privileged_remote_access.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|