zscaler-sdk-python 0.3.0__tar.gz → 0.4.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.3.0 → zscaler_sdk_python-0.4.0}/PKG-INFO +6 -5
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/pyproject.toml +9 -7
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/__init__.py +1 -1
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/cache/zscaler_cache.py +1 -1
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/constants.py +1 -1
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/__init__.py +140 -85
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/app_segments.py +159 -77
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/app_segments_inspection.py +45 -54
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/app_segments_pra.py +49 -59
- zscaler_sdk_python-0.4.0/zscaler/zpa/authdomains.py +41 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/certificates.py +90 -59
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/client.py +3 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/connectors.py +139 -88
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/emergency_access.py +16 -1
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/idp.py +18 -3
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/inspection.py +215 -227
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/machine_groups.py +24 -10
- zscaler_sdk_python-0.4.0/zscaler/zpa/microtenants.py +217 -0
- zscaler_sdk_python-0.4.0/zscaler/zpa/policies.py +2709 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/privileged_remote_access.py +48 -5
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/provisioning.py +35 -16
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/segment_groups.py +51 -41
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/server_groups.py +34 -12
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/servers.py +42 -22
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/service_edges.py +241 -61
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/trusted_networks.py +20 -5
- zscaler_sdk_python-0.3.0/zscaler/zpa/policies.py +0 -857
- zscaler_sdk_python-0.3.0/zscaler/zpa/policiesv2.py +0 -1667
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/README.md +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/cache/__init__.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/cache/cache.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/cache/no_op_cache.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/errors/__init__.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/errors/error.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/errors/http_error.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/errors/zscaler_api_error.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/exceptions/__init__.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/exceptions/exceptions.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/logger.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/ratelimiter/__init__.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/ratelimiter/ratelimiter.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/user_agent.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/utils.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/__init__.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/activate.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/apptotal.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/audit_logs.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/authentication_settings.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/client.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/cloud_apps.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/device_management.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/dlp.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/errors.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/firewall.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/forwarding_control.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/isolation_profile.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/labels.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/locations.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/sandbox.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/security.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/ssl_inspection.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/traffic.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/url_categories.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/url_filtering.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/users.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/web_dlp.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/workload_groups.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zia/zpa_gateway.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/README.md +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/cloud_connector_groups.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/errors.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/isolation.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/lss.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/posture_profiles.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/saml_attributes.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/scim_attributes.py +0 -0
- {zscaler_sdk_python-0.3.0 → zscaler_sdk_python-0.4.0}/zscaler/zpa/scim_groups.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zscaler-sdk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Official Python SDK for the Zscaler Products (Beta)
|
|
5
5
|
Home-page: https://github.com/zscaler/zscaler-sdk-python
|
|
6
6
|
License: MIT
|
|
@@ -23,13 +23,14 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: aenum ; extra == "dev"
|
|
25
25
|
Requires-Dist: arrow
|
|
26
|
-
Requires-Dist: black ; extra == "dev"
|
|
27
|
-
Requires-Dist: certifi
|
|
26
|
+
Requires-Dist: black (>=24.3.0) ; extra == "dev"
|
|
27
|
+
Requires-Dist: certifi (>=2024.2.2)
|
|
28
28
|
Requires-Dist: charset-normalizer
|
|
29
|
-
Requires-Dist: cryptography (>=
|
|
29
|
+
Requires-Dist: cryptography (>=42.0.7)
|
|
30
30
|
Requires-Dist: flake8
|
|
31
31
|
Requires-Dist: flatdict
|
|
32
32
|
Requires-Dist: idna
|
|
33
|
+
Requires-Dist: okta (>=2.9.7)
|
|
33
34
|
Requires-Dist: pycryptodomex
|
|
34
35
|
Requires-Dist: pydash ; extra == "dev"
|
|
35
36
|
Requires-Dist: python-box (>=7.1.1,<8.0.0)
|
|
@@ -37,7 +38,7 @@ Requires-Dist: python-dateutil
|
|
|
37
38
|
Requires-Dist: pytz
|
|
38
39
|
Requires-Dist: pyyaml
|
|
39
40
|
Requires-Dist: requests (>=2.32.0)
|
|
40
|
-
Requires-Dist: responses
|
|
41
|
+
Requires-Dist: responses (>=0.25.0)
|
|
41
42
|
Requires-Dist: restfly
|
|
42
43
|
Requires-Dist: six
|
|
43
44
|
Requires-Dist: xmltodict
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "zscaler-sdk-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Official Python SDK for the Zscaler Products (Beta)"
|
|
5
5
|
authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -30,13 +30,13 @@ packages = [
|
|
|
30
30
|
[tool.poetry.dependencies]
|
|
31
31
|
python = ">=3.8,<4.0"
|
|
32
32
|
arrow = "*"
|
|
33
|
-
certifi = "
|
|
33
|
+
certifi = ">=2024.2.2"
|
|
34
34
|
charset-normalizer = "*"
|
|
35
35
|
idna = "*"
|
|
36
36
|
python-box = "^7.1.1"
|
|
37
37
|
python-dateutil = "*"
|
|
38
38
|
requests = ">=2.32.0"
|
|
39
|
-
responses = "
|
|
39
|
+
responses = ">=0.25.0"
|
|
40
40
|
restfly = "*"
|
|
41
41
|
six = "*"
|
|
42
42
|
flatdict = "*"
|
|
@@ -48,12 +48,14 @@ aenum = "*"
|
|
|
48
48
|
pydash = "*"
|
|
49
49
|
flake8 = "*"
|
|
50
50
|
pytz = "*"
|
|
51
|
-
black = "
|
|
52
|
-
cryptography = ">=3.4,<43.0"
|
|
51
|
+
black = ">=24.3.0"
|
|
52
|
+
# cryptography = ">=3.4,<43.0"
|
|
53
|
+
cryptography = ">=42.0.7"
|
|
54
|
+
okta = ">=2.9.7"
|
|
53
55
|
|
|
54
56
|
[tool.poetry.dev-dependencies]
|
|
55
|
-
black = "24.3.0"
|
|
56
|
-
pytest = "
|
|
57
|
+
black = ">=24.3.0"
|
|
58
|
+
pytest = ">=8.2.1"
|
|
57
59
|
pytest-asyncio = ">=0.23.7"
|
|
58
60
|
pytest-mock = "*"
|
|
59
61
|
pytest-recording = "*"
|
|
@@ -14,7 +14,7 @@ ZPA_BASE_URLS = {
|
|
|
14
14
|
|
|
15
15
|
DEV_AUTH_URL = "https://authn1.dev.zpath.net/authn/v1/oauth/token"
|
|
16
16
|
|
|
17
|
-
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
|
|
17
|
+
RETRYABLE_STATUS_CODES = {409, 429, 500, 502, 503, 504}
|
|
18
18
|
MAX_RETRIES = 5
|
|
19
19
|
BACKOFF_FACTOR = 1
|
|
20
20
|
BACKOFF_BASE_DURATION = 2
|
|
@@ -26,6 +26,7 @@ from zscaler.utils import (
|
|
|
26
26
|
retry_with_backoff,
|
|
27
27
|
snake_to_camel,
|
|
28
28
|
)
|
|
29
|
+
from zscaler.zpa.authdomains import AuthDomainsAPI
|
|
29
30
|
from zscaler.zpa.app_segments import ApplicationSegmentAPI
|
|
30
31
|
from zscaler.zpa.app_segments_inspection import AppSegmentsInspectionAPI
|
|
31
32
|
from zscaler.zpa.app_segments_pra import AppSegmentsPRAAPI
|
|
@@ -39,8 +40,8 @@ from zscaler.zpa.inspection import InspectionControllerAPI
|
|
|
39
40
|
from zscaler.zpa.isolation import IsolationAPI
|
|
40
41
|
from zscaler.zpa.lss import LSSConfigControllerAPI
|
|
41
42
|
from zscaler.zpa.machine_groups import MachineGroupsAPI
|
|
43
|
+
from zscaler.zpa.microtenants import MicrotenantsAPI
|
|
42
44
|
from zscaler.zpa.policies import PolicySetsAPI
|
|
43
|
-
from zscaler.zpa.policiesv2 import PolicySetsV2API
|
|
44
45
|
from zscaler.zpa.posture_profiles import PostureProfilesAPI
|
|
45
46
|
from zscaler.zpa.privileged_remote_access import PrivilegedRemoteAccessAPI
|
|
46
47
|
from zscaler.zpa.provisioning import ProvisioningKeyAPI
|
|
@@ -82,12 +83,12 @@ class ZPAClientHelper(ZPAClient):
|
|
|
82
83
|
client_secret,
|
|
83
84
|
customer_id,
|
|
84
85
|
cloud,
|
|
86
|
+
microtenant_id=None,
|
|
85
87
|
timeout=240,
|
|
86
88
|
cache=None,
|
|
87
89
|
fail_safe=False,
|
|
88
90
|
):
|
|
89
91
|
# Initialize rate limiter
|
|
90
|
-
# You may want to adjust these parameters as per your rate limit configuration
|
|
91
92
|
self.rate_limiter = RateLimiter(
|
|
92
93
|
get_limit=20, # Adjusted to allow 20 GET requests per 10 seconds
|
|
93
94
|
post_put_delete_limit=10, # Adjusted to allow 10 POST/PUT/DELETE requests per 10 seconds
|
|
@@ -95,7 +96,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
95
96
|
post_put_delete_freq=10, # Adjust frequency to 10 seconds
|
|
96
97
|
)
|
|
97
98
|
|
|
98
|
-
# Validate cloud value
|
|
99
99
|
if cloud not in ZPA_BASE_URLS:
|
|
100
100
|
valid_clouds = ", ".join(ZPA_BASE_URLS.keys())
|
|
101
101
|
raise ValueError(
|
|
@@ -103,22 +103,20 @@ class ZPAClientHelper(ZPAClient):
|
|
|
103
103
|
f"Please use one of the following supported values: {valid_clouds}"
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
-
# Continue with existing initialization...
|
|
107
|
-
# Select the appropriate URL
|
|
108
106
|
self.baseurl = ZPA_BASE_URLS.get(cloud, ZPA_BASE_URLS["PRODUCTION"])
|
|
109
|
-
|
|
110
107
|
self.timeout = timeout
|
|
111
108
|
self.client_id = client_id
|
|
112
109
|
self.client_secret = client_secret
|
|
113
110
|
self.customer_id = customer_id
|
|
114
111
|
self.cloud = cloud
|
|
112
|
+
self.microtenant_id = microtenant_id or os.getenv("ZPA_MICROTENANT_ID")
|
|
115
113
|
self.url = f"{self.baseurl}/mgmtconfig/v1/admin/customers/{customer_id}"
|
|
116
114
|
self.user_config_url = f"{self.baseurl}/userconfig/v1/customers/{customer_id}"
|
|
117
115
|
self.v2_url = f"{self.baseurl}/mgmtconfig/v2/admin/customers/{customer_id}"
|
|
118
116
|
self.v2_lss_url = f"{self.baseurl}/mgmtconfig/v2/admin/lssConfig/customers/{customer_id}"
|
|
119
117
|
self.cbi_url = f"{self.baseurl}/cbiconfig/cbi/api/customers/{customer_id}"
|
|
120
118
|
self.fail_safe = fail_safe
|
|
121
|
-
|
|
119
|
+
|
|
122
120
|
cache_enabled = os.environ.get("ZSCALER_CLIENT_CACHE_ENABLED", "true").lower() == "true"
|
|
123
121
|
if cache is None:
|
|
124
122
|
if cache_enabled:
|
|
@@ -130,28 +128,28 @@ class ZPAClientHelper(ZPAClient):
|
|
|
130
128
|
else:
|
|
131
129
|
self.cache = cache
|
|
132
130
|
|
|
133
|
-
# Initialize user-agent
|
|
134
131
|
ua = UserAgent()
|
|
135
132
|
self.user_agent = ua.get_user_agent_string()
|
|
133
|
+
self.access_token = None
|
|
134
|
+
self.headers = {}
|
|
136
135
|
self.refreshToken()
|
|
137
136
|
|
|
138
137
|
def refreshToken(self):
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
if not self.access_token or is_token_expired(self.access_token):
|
|
139
|
+
response = self.login()
|
|
140
|
+
if response is None or response.status_code > 299 or not response.json():
|
|
141
|
+
logger.error("Failed to login using provided credentials, response: %s", response)
|
|
142
|
+
raise Exception("Failed to login using provided credentials.")
|
|
143
|
+
self.access_token = response.json().get("access_token")
|
|
144
|
+
self.headers = {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
"Accept": "application/json",
|
|
147
|
+
"Authorization": f"Bearer {self.access_token}",
|
|
148
|
+
"User-Agent": self.user_agent,
|
|
149
|
+
}
|
|
151
150
|
|
|
152
151
|
@retry_with_backoff(retries=5)
|
|
153
152
|
def login(self):
|
|
154
|
-
"""Log in to the ZPA API and set the access token for subsequent requests."""
|
|
155
153
|
params = {"client_id": self.client_id, "client_secret": self.client_secret}
|
|
156
154
|
headers = {
|
|
157
155
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -164,7 +162,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
164
162
|
url = DEV_AUTH_URL + "?grant_type=CLIENT_CREDENTIALS"
|
|
165
163
|
data = urllib.parse.urlencode(params)
|
|
166
164
|
resp = requests.post(url, data=data, headers=headers, timeout=self.timeout)
|
|
167
|
-
# Avoid logging all data from the response, focus on the status and a summary instead
|
|
168
165
|
logger.info("Login attempt with status: %d", resp.status_code)
|
|
169
166
|
return resp
|
|
170
167
|
except Exception as e:
|
|
@@ -172,16 +169,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
172
169
|
return None
|
|
173
170
|
|
|
174
171
|
def send(self, method, path, json=None, params=None, api_version: str = None):
|
|
175
|
-
"""
|
|
176
|
-
Send a request to the ZPA API.
|
|
177
|
-
|
|
178
|
-
Parameters:
|
|
179
|
-
- method (str): The HTTP method.
|
|
180
|
-
- path (str): API endpoint path.
|
|
181
|
-
- json (dict, optional): Request payload. Defaults to None.
|
|
182
|
-
Returns:
|
|
183
|
-
- Response: Response object from the request.
|
|
184
|
-
"""
|
|
185
172
|
api = self.url
|
|
186
173
|
if api_version is None:
|
|
187
174
|
api = self.url
|
|
@@ -194,23 +181,34 @@ class ZPAClientHelper(ZPAClient):
|
|
|
194
181
|
elif api_version == "cbiconfig_v1":
|
|
195
182
|
api = self.cbi_url
|
|
196
183
|
|
|
184
|
+
if params is None:
|
|
185
|
+
params = {}
|
|
186
|
+
|
|
187
|
+
if json and "microtenant_id" in json:
|
|
188
|
+
microtenant_id = json.pop("microtenant_id")
|
|
189
|
+
else:
|
|
190
|
+
microtenant_id = self.microtenant_id
|
|
191
|
+
|
|
192
|
+
if microtenant_id:
|
|
193
|
+
params["microtenantId"] = microtenant_id
|
|
194
|
+
|
|
197
195
|
url = f"{api}/{path.lstrip('/')}"
|
|
196
|
+
if params:
|
|
197
|
+
url = f"{url}?{urllib.parse.urlencode(params)}"
|
|
198
|
+
|
|
198
199
|
start_time = time.time()
|
|
199
|
-
# Update headers to include the user agent
|
|
200
200
|
headers_with_user_agent = self.headers.copy()
|
|
201
201
|
headers_with_user_agent["User-Agent"] = self.user_agent
|
|
202
|
-
# Generate a unique UUID for this request
|
|
203
202
|
request_uuid = uuid.uuid4()
|
|
204
|
-
dump_request(logger, url, method, json,
|
|
205
|
-
|
|
206
|
-
cache_key = self.cache.create_key(url, params)
|
|
203
|
+
dump_request(logger, url, method, json, None, headers_with_user_agent, request_uuid)
|
|
204
|
+
cache_key = self.cache.create_key(url, None)
|
|
207
205
|
if method == "GET" and self.cache.contains(cache_key):
|
|
208
206
|
resp = self.cache.get(cache_key)
|
|
209
207
|
dump_response(
|
|
210
208
|
logger=logger,
|
|
211
209
|
url=url,
|
|
212
210
|
method=method,
|
|
213
|
-
params=
|
|
211
|
+
params=None,
|
|
214
212
|
resp=resp,
|
|
215
213
|
request_uuid=request_uuid,
|
|
216
214
|
start_time=start_time,
|
|
@@ -219,59 +217,63 @@ class ZPAClientHelper(ZPAClient):
|
|
|
219
217
|
return resp
|
|
220
218
|
|
|
221
219
|
attempts = 0
|
|
222
|
-
while attempts < 5:
|
|
220
|
+
while attempts < 5:
|
|
223
221
|
try:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
self.refreshToken()
|
|
223
|
+
should_wait, delay = self.rate_limiter.wait(method)
|
|
224
|
+
if should_wait:
|
|
225
|
+
logger.warning(f"Rate limit exceeded. Retrying in {delay} seconds.")
|
|
226
|
+
time.sleep(delay)
|
|
228
227
|
resp = requests.request(
|
|
229
228
|
method,
|
|
230
229
|
url,
|
|
231
230
|
json=json,
|
|
231
|
+
params=None,
|
|
232
232
|
headers=headers_with_user_agent,
|
|
233
233
|
timeout=self.timeout,
|
|
234
234
|
)
|
|
235
235
|
dump_response(
|
|
236
236
|
logger=logger,
|
|
237
237
|
url=url,
|
|
238
|
-
params=
|
|
238
|
+
params=None,
|
|
239
239
|
method=method,
|
|
240
240
|
resp=resp,
|
|
241
241
|
request_uuid=request_uuid,
|
|
242
242
|
start_time=start_time,
|
|
243
243
|
)
|
|
244
|
-
if resp.status_code == 429:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
244
|
+
if resp.status_code == 429:
|
|
245
|
+
retry_after = resp.headers.get("Retry-After")
|
|
246
|
+
if retry_after:
|
|
247
|
+
try:
|
|
248
|
+
sleep_time = int(retry_after)
|
|
249
|
+
except ValueError:
|
|
250
|
+
sleep_time = int(retry_after[:-1])
|
|
251
|
+
logger.warning(f"Rate limit exceeded. Retrying in {sleep_time} seconds.")
|
|
252
|
+
time.sleep(sleep_time)
|
|
253
|
+
else:
|
|
254
|
+
time.sleep(60)
|
|
250
255
|
attempts += 1
|
|
251
256
|
continue
|
|
252
257
|
else:
|
|
253
258
|
break
|
|
254
259
|
except requests.RequestException as e:
|
|
255
|
-
if attempts == 4:
|
|
260
|
+
if attempts == 4:
|
|
256
261
|
logger.error(f"Failed to send {method} request to {url} after 5 attempts. Error: {str(e)}")
|
|
257
262
|
raise e
|
|
258
263
|
else:
|
|
259
264
|
logger.warning(f"Failed to send {method} request to {url}. Retrying... Error: {str(e)}")
|
|
260
265
|
attempts += 1
|
|
261
|
-
sleep(5)
|
|
266
|
+
time.sleep(5)
|
|
262
267
|
|
|
263
|
-
# If Non-GET call, clear the
|
|
264
268
|
if method != "GET":
|
|
265
|
-
|
|
269
|
+
logger.info(f"Clearing cache for non-GET request: {method} {url}")
|
|
270
|
+
self.cache.clear()
|
|
266
271
|
|
|
267
|
-
# Detailed logging for request and response
|
|
268
272
|
try:
|
|
269
273
|
response_data = resp.json()
|
|
270
|
-
except ValueError:
|
|
274
|
+
except ValueError:
|
|
271
275
|
response_data = resp.text
|
|
272
|
-
# check if call was succesful
|
|
273
276
|
if 200 > resp.status_code or resp.status_code > 299:
|
|
274
|
-
# create errors
|
|
275
277
|
try:
|
|
276
278
|
error = ZscalerAPIError(url, resp, response_data)
|
|
277
279
|
if self.fail_safe:
|
|
@@ -284,7 +286,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
284
286
|
logger.error(response_data)
|
|
285
287
|
raise HTTPException(response_data)
|
|
286
288
|
logger.error(error)
|
|
287
|
-
# Cache the response if it's a successful GET request
|
|
288
289
|
if method == "GET" and resp.status_code == 200:
|
|
289
290
|
self.cache.add(cache_key, resp)
|
|
290
291
|
return resp
|
|
@@ -294,23 +295,34 @@ class ZPAClientHelper(ZPAClient):
|
|
|
294
295
|
Send a GET request to the ZPA API.
|
|
295
296
|
|
|
296
297
|
Parameters:
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
path (str): API endpoint path.
|
|
299
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
300
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
301
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
302
|
+
|
|
299
303
|
Returns:
|
|
300
|
-
|
|
304
|
+
dict: Formatted JSON response from the API.
|
|
301
305
|
"""
|
|
302
|
-
|
|
303
|
-
# Use rate limiter before making a request
|
|
304
306
|
should_wait, delay = self.rate_limiter.wait("GET")
|
|
305
307
|
if should_wait:
|
|
306
308
|
time.sleep(delay)
|
|
307
|
-
|
|
308
|
-
# Now proceed with sending the request
|
|
309
309
|
resp = self.send("GET", path, json, params, api_version=api_version)
|
|
310
310
|
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
311
311
|
return formatted_resp
|
|
312
312
|
|
|
313
313
|
def put(self, path, json=None, params=None, api_version: str = None):
|
|
314
|
+
"""
|
|
315
|
+
Send a PUT request to the ZPA API.
|
|
316
|
+
|
|
317
|
+
Parameters:
|
|
318
|
+
path (str): API endpoint path.
|
|
319
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
320
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
321
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
dict: Formatted JSON response from the API.
|
|
325
|
+
"""
|
|
314
326
|
should_wait, delay = self.rate_limiter.wait("PUT")
|
|
315
327
|
if should_wait:
|
|
316
328
|
time.sleep(delay)
|
|
@@ -319,6 +331,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
319
331
|
return formatted_resp
|
|
320
332
|
|
|
321
333
|
def post(self, path, json=None, params=None, api_version: str = None):
|
|
334
|
+
"""
|
|
335
|
+
Send a POST request to the ZPA API.
|
|
336
|
+
|
|
337
|
+
Parameters:
|
|
338
|
+
path (str): API endpoint path.
|
|
339
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
340
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
341
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
dict: Formatted JSON response from the API.
|
|
345
|
+
"""
|
|
322
346
|
should_wait, delay = self.rate_limiter.wait("POST")
|
|
323
347
|
if should_wait:
|
|
324
348
|
time.sleep(delay)
|
|
@@ -327,6 +351,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
327
351
|
return formatted_resp
|
|
328
352
|
|
|
329
353
|
def delete(self, path, json=None, params=None, api_version: str = None):
|
|
354
|
+
"""
|
|
355
|
+
Send a DELETE request to the ZPA API.
|
|
356
|
+
|
|
357
|
+
Parameters:
|
|
358
|
+
path (str): API endpoint path.
|
|
359
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
360
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
361
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Response: Response object from the DELETE request.
|
|
365
|
+
"""
|
|
330
366
|
should_wait, delay = self.rate_limiter.wait("DELETE")
|
|
331
367
|
if should_wait:
|
|
332
368
|
time.sleep(delay)
|
|
@@ -342,6 +378,7 @@ class ZPAClientHelper(ZPAClient):
|
|
|
342
378
|
search_field="name",
|
|
343
379
|
max_pages=None,
|
|
344
380
|
max_items=None,
|
|
381
|
+
all_entries=False, # Return all SCIM groups including the deleted ones if set to true
|
|
345
382
|
sort_order=None,
|
|
346
383
|
sort_by=None,
|
|
347
384
|
sort_dir=None,
|
|
@@ -349,8 +386,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
349
386
|
end_time=None,
|
|
350
387
|
idp_group_id=None,
|
|
351
388
|
scim_user_id=None,
|
|
389
|
+
scim_username=None,
|
|
352
390
|
page=None,
|
|
353
|
-
pagesize=
|
|
391
|
+
pagesize=None,
|
|
392
|
+
microtenant_id=None,
|
|
354
393
|
):
|
|
355
394
|
"""
|
|
356
395
|
Fetches paginated data from the ZPA API based on specified parameters and handles various types of API pagination.
|
|
@@ -393,15 +432,19 @@ class ZPAClientHelper(ZPAClient):
|
|
|
393
432
|
if params is None:
|
|
394
433
|
params = {}
|
|
395
434
|
|
|
396
|
-
if (page is not None or pagesize
|
|
435
|
+
if (page is not None or pagesize is not None) and (max_pages is not None or max_items is not None):
|
|
397
436
|
raise ValueError(
|
|
398
437
|
"Do not mix 'page' or 'pagesize' with 'max_pages' or 'max_items'. Choose either set of parameters."
|
|
399
438
|
)
|
|
400
439
|
|
|
401
|
-
params["
|
|
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
|
|
402
442
|
|
|
403
|
-
|
|
404
|
-
|
|
443
|
+
# Check for microtenantId in function arguments first, then environment variable
|
|
444
|
+
if microtenant_id:
|
|
445
|
+
params["microtenantId"] = microtenant_id
|
|
446
|
+
elif self.microtenant_id and "microtenantId" not in params:
|
|
447
|
+
params["microtenantId"] = self.microtenant_id
|
|
405
448
|
|
|
406
449
|
if search:
|
|
407
450
|
api_search_field = snake_to_camel(search_field)
|
|
@@ -419,6 +462,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
419
462
|
params["idpGroupId"] = idp_group_id
|
|
420
463
|
if scim_user_id:
|
|
421
464
|
params["scimUserId"] = scim_user_id
|
|
465
|
+
if scim_username:
|
|
466
|
+
params["scimUserName"] = scim_username
|
|
467
|
+
if all_entries:
|
|
468
|
+
params["allEntries"] = all_entries
|
|
422
469
|
|
|
423
470
|
total_collected = 0
|
|
424
471
|
ret_data = []
|
|
@@ -432,17 +479,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
432
479
|
if should_wait:
|
|
433
480
|
time.sleep(delay)
|
|
434
481
|
|
|
435
|
-
|
|
436
|
-
response = self.send("GET", url, api_version=api_version)
|
|
482
|
+
response = self.send("GET", path=path, params=params, api_version=api_version)
|
|
437
483
|
|
|
438
484
|
if response.status_code != expected_status_code:
|
|
439
|
-
error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
|
|
485
|
+
error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
|
|
486
|
+
status_code=response.status_code, page=params["page"]
|
|
487
|
+
)
|
|
440
488
|
logger.error(error_msg)
|
|
441
489
|
return BoxList([]), error_msg
|
|
442
490
|
|
|
443
491
|
response_data = response.json()
|
|
444
492
|
data = response_data.get("list", [])
|
|
445
|
-
if not data and (page
|
|
493
|
+
if not data and (params["page"] == 1):
|
|
446
494
|
error_msg = ERROR_MESSAGES["EMPTY_RESULTS"]
|
|
447
495
|
logger.warn(error_msg)
|
|
448
496
|
return BoxList([]), error_msg
|
|
@@ -454,12 +502,11 @@ class ZPAClientHelper(ZPAClient):
|
|
|
454
502
|
if max_items is not None and total_collected >= max_items:
|
|
455
503
|
break
|
|
456
504
|
|
|
457
|
-
|
|
458
|
-
if not
|
|
505
|
+
next_page = response_data.get("nextPage")
|
|
506
|
+
if not next_page or (max_pages is not None and params["page"] >= max_pages):
|
|
459
507
|
break
|
|
460
508
|
|
|
461
|
-
page =
|
|
462
|
-
params["page"] = page
|
|
509
|
+
params["page"] = next_page if params["page"] is None else params["page"] + 1
|
|
463
510
|
|
|
464
511
|
finally:
|
|
465
512
|
time.sleep(2) # Ensure a delay between requests regardless of outcome
|
|
@@ -471,6 +518,14 @@ class ZPAClientHelper(ZPAClient):
|
|
|
471
518
|
|
|
472
519
|
return BoxList(ret_data), None
|
|
473
520
|
|
|
521
|
+
@property
|
|
522
|
+
def authdomains(self):
|
|
523
|
+
"""
|
|
524
|
+
The interface object for the :ref:`ZPA Auth Domains interface <zpa-authdomains>`.
|
|
525
|
+
|
|
526
|
+
"""
|
|
527
|
+
return AuthDomainsAPI(self)
|
|
528
|
+
|
|
474
529
|
@property
|
|
475
530
|
def app_segments(self):
|
|
476
531
|
"""
|
|
@@ -568,20 +623,20 @@ class ZPAClientHelper(ZPAClient):
|
|
|
568
623
|
return MachineGroupsAPI(self)
|
|
569
624
|
|
|
570
625
|
@property
|
|
571
|
-
def
|
|
626
|
+
def microtenants(self):
|
|
572
627
|
"""
|
|
573
|
-
The interface object for the :ref:`ZPA
|
|
628
|
+
The interface object for the :ref:`ZPA Microtenants interface <zpa-microtenants>`.
|
|
574
629
|
|
|
575
630
|
"""
|
|
576
|
-
return
|
|
631
|
+
return MicrotenantsAPI(self)
|
|
577
632
|
|
|
578
633
|
@property
|
|
579
|
-
def
|
|
634
|
+
def policies(self):
|
|
580
635
|
"""
|
|
581
|
-
The interface object for the :ref:`ZPA Policy Sets
|
|
636
|
+
The interface object for the :ref:`ZPA Policy Sets interface <zpa-policies>`.
|
|
582
637
|
|
|
583
638
|
"""
|
|
584
|
-
return
|
|
639
|
+
return PolicySetsAPI(self)
|
|
585
640
|
|
|
586
641
|
@property
|
|
587
642
|
def posture_profiles(self):
|