zscaler-sdk-python 0.2.0__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zscaler/__init__.py +1 -1
- zscaler/cache/zscaler_cache.py +1 -1
- zscaler/constants.py +3 -0
- zscaler/zpa/__init__.py +84 -56
- zscaler/zpa/app_segments.py +35 -0
- zscaler/zpa/policies.py +2196 -181
- {zscaler_sdk_python-0.2.0.dist-info → zscaler_sdk_python-0.3.1.dist-info}/METADATA +5 -5
- {zscaler_sdk_python-0.2.0.dist-info → zscaler_sdk_python-0.3.1.dist-info}/RECORD +10 -10
- {zscaler_sdk_python-0.2.0.dist-info → zscaler_sdk_python-0.3.1.dist-info}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.2.0.dist-info → zscaler_sdk_python-0.3.1.dist-info}/WHEEL +0 -0
zscaler/__init__.py
CHANGED
zscaler/cache/zscaler_cache.py
CHANGED
zscaler/constants.py
CHANGED
|
@@ -9,8 +9,11 @@ ZPA_BASE_URLS = {
|
|
|
9
9
|
"PREVIEW": "https://config.zpapreview.net",
|
|
10
10
|
"QA": "https://config.qa.zpath.net",
|
|
11
11
|
"QA2": "https://pdx2-zpa-config.qa2.zpath.net",
|
|
12
|
+
"DEV": "https://public-api.dev.zpath.net",
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
DEV_AUTH_URL = "https://authn1.dev.zpath.net/authn/v1/oauth/token"
|
|
16
|
+
|
|
14
17
|
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
|
|
15
18
|
MAX_RETRIES = 5
|
|
16
19
|
BACKOFF_FACTOR = 1
|
zscaler/zpa/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ from box import BoxList
|
|
|
11
11
|
from zscaler import __version__
|
|
12
12
|
from zscaler.cache.no_op_cache import NoOpCache
|
|
13
13
|
from zscaler.cache.zscaler_cache import ZscalerCache
|
|
14
|
-
from zscaler.constants import ZPA_BASE_URLS
|
|
14
|
+
from zscaler.constants import ZPA_BASE_URLS, DEV_AUTH_URL
|
|
15
15
|
from zscaler.errors.http_error import HTTPError, ZscalerAPIError
|
|
16
16
|
from zscaler.exceptions.exceptions import HTTPException, ZscalerAPIException
|
|
17
17
|
from zscaler.logger import setup_logging
|
|
@@ -117,6 +117,7 @@ class ZPAClientHelper(ZPAClient):
|
|
|
117
117
|
self.v2_lss_url = f"{self.baseurl}/mgmtconfig/v2/admin/lssConfig/customers/{customer_id}"
|
|
118
118
|
self.cbi_url = f"{self.baseurl}/cbiconfig/cbi/api/customers/{customer_id}"
|
|
119
119
|
self.fail_safe = fail_safe
|
|
120
|
+
|
|
120
121
|
# Cache setup
|
|
121
122
|
cache_enabled = os.environ.get("ZSCALER_CLIENT_CACHE_ENABLED", "true").lower() == "true"
|
|
122
123
|
if cache is None:
|
|
@@ -132,26 +133,27 @@ class ZPAClientHelper(ZPAClient):
|
|
|
132
133
|
# Initialize user-agent
|
|
133
134
|
ua = UserAgent()
|
|
134
135
|
self.user_agent = ua.get_user_agent_string()
|
|
136
|
+
self.access_token = None
|
|
137
|
+
self.headers = {}
|
|
135
138
|
self.refreshToken()
|
|
136
139
|
|
|
137
140
|
def refreshToken(self):
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
if not self.access_token or is_token_expired(self.access_token):
|
|
142
|
+
response = self.login()
|
|
143
|
+
if response is None or response.status_code > 299 or not response.json():
|
|
144
|
+
logger.error("Failed to login using provided credentials, response: %s", response)
|
|
145
|
+
raise Exception("Failed to login using provided credentials.")
|
|
146
|
+
self.access_token = response.json().get("access_token")
|
|
147
|
+
self.headers = {
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
"Accept": "application/json",
|
|
150
|
+
"Authorization": f"Bearer {self.access_token}",
|
|
151
|
+
"User-Agent": self.user_agent,
|
|
152
|
+
}
|
|
150
153
|
|
|
151
154
|
@retry_with_backoff(retries=5)
|
|
152
155
|
def login(self):
|
|
153
|
-
""
|
|
154
|
-
data = urllib.parse.urlencode({"client_id": self.client_id, "client_secret": self.client_secret})
|
|
156
|
+
params = {"client_id": self.client_id, "client_secret": self.client_secret}
|
|
155
157
|
headers = {
|
|
156
158
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
157
159
|
"Accept": "application/json",
|
|
@@ -159,8 +161,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
159
161
|
}
|
|
160
162
|
try:
|
|
161
163
|
url = f"{self.baseurl}/signin"
|
|
164
|
+
if self.cloud == "DEV":
|
|
165
|
+
url = DEV_AUTH_URL + "?grant_type=CLIENT_CREDENTIALS"
|
|
166
|
+
data = urllib.parse.urlencode(params)
|
|
162
167
|
resp = requests.post(url, data=data, headers=headers, timeout=self.timeout)
|
|
163
|
-
# Avoid logging all data from the response, focus on the status and a summary instead
|
|
164
168
|
logger.info("Login attempt with status: %d", resp.status_code)
|
|
165
169
|
return resp
|
|
166
170
|
except Exception as e:
|
|
@@ -168,16 +172,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
168
172
|
return None
|
|
169
173
|
|
|
170
174
|
def send(self, method, path, json=None, params=None, api_version: str = None):
|
|
171
|
-
"""
|
|
172
|
-
Send a request to the ZPA API.
|
|
173
|
-
|
|
174
|
-
Parameters:
|
|
175
|
-
- method (str): The HTTP method.
|
|
176
|
-
- path (str): API endpoint path.
|
|
177
|
-
- json (dict, optional): Request payload. Defaults to None.
|
|
178
|
-
Returns:
|
|
179
|
-
- Response: Response object from the request.
|
|
180
|
-
"""
|
|
181
175
|
api = self.url
|
|
182
176
|
if api_version is None:
|
|
183
177
|
api = self.url
|
|
@@ -192,13 +186,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
192
186
|
|
|
193
187
|
url = f"{api}/{path.lstrip('/')}"
|
|
194
188
|
start_time = time.time()
|
|
195
|
-
# Update headers to include the user agent
|
|
196
189
|
headers_with_user_agent = self.headers.copy()
|
|
197
190
|
headers_with_user_agent["User-Agent"] = self.user_agent
|
|
198
|
-
# Generate a unique UUID for this request
|
|
199
191
|
request_uuid = uuid.uuid4()
|
|
200
192
|
dump_request(logger, url, method, json, params, headers_with_user_agent, request_uuid)
|
|
201
|
-
# Check cache before sending request
|
|
202
193
|
cache_key = self.cache.create_key(url, params)
|
|
203
194
|
if method == "GET" and self.cache.contains(cache_key):
|
|
204
195
|
resp = self.cache.get(cache_key)
|
|
@@ -215,12 +206,13 @@ class ZPAClientHelper(ZPAClient):
|
|
|
215
206
|
return resp
|
|
216
207
|
|
|
217
208
|
attempts = 0
|
|
218
|
-
while attempts < 5:
|
|
209
|
+
while attempts < 5:
|
|
219
210
|
try:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
211
|
+
self.refreshToken()
|
|
212
|
+
should_wait, delay = self.rate_limiter.wait(method)
|
|
213
|
+
if should_wait:
|
|
214
|
+
logger.warning(f"Rate limit exceeded. Retrying in {delay} seconds.")
|
|
215
|
+
time.sleep(delay)
|
|
224
216
|
resp = requests.request(
|
|
225
217
|
method,
|
|
226
218
|
url,
|
|
@@ -237,37 +229,39 @@ class ZPAClientHelper(ZPAClient):
|
|
|
237
229
|
request_uuid=request_uuid,
|
|
238
230
|
start_time=start_time,
|
|
239
231
|
)
|
|
240
|
-
if resp.status_code == 429:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
232
|
+
if resp.status_code == 429:
|
|
233
|
+
retry_after = resp.headers.get("Retry-After")
|
|
234
|
+
if retry_after:
|
|
235
|
+
try:
|
|
236
|
+
sleep_time = int(retry_after)
|
|
237
|
+
except ValueError:
|
|
238
|
+
sleep_time = int(retry_after[:-1])
|
|
239
|
+
logger.warning(f"Rate limit exceeded. Retrying in {sleep_time} seconds.")
|
|
240
|
+
time.sleep(sleep_time)
|
|
241
|
+
else:
|
|
242
|
+
time.sleep(60)
|
|
246
243
|
attempts += 1
|
|
247
244
|
continue
|
|
248
245
|
else:
|
|
249
246
|
break
|
|
250
247
|
except requests.RequestException as e:
|
|
251
|
-
if attempts == 4:
|
|
248
|
+
if attempts == 4:
|
|
252
249
|
logger.error(f"Failed to send {method} request to {url} after 5 attempts. Error: {str(e)}")
|
|
253
250
|
raise e
|
|
254
251
|
else:
|
|
255
252
|
logger.warning(f"Failed to send {method} request to {url}. Retrying... Error: {str(e)}")
|
|
256
253
|
attempts += 1
|
|
257
|
-
sleep(5)
|
|
254
|
+
time.sleep(5)
|
|
258
255
|
|
|
259
|
-
# If Non-GET call, clear the
|
|
260
256
|
if method != "GET":
|
|
261
|
-
|
|
257
|
+
logger.info(f"Clearing cache for non-GET request: {method} {url}")
|
|
258
|
+
self.cache.clear()
|
|
262
259
|
|
|
263
|
-
# Detailed logging for request and response
|
|
264
260
|
try:
|
|
265
261
|
response_data = resp.json()
|
|
266
|
-
except ValueError:
|
|
262
|
+
except ValueError:
|
|
267
263
|
response_data = resp.text
|
|
268
|
-
# check if call was succesful
|
|
269
264
|
if 200 > resp.status_code or resp.status_code > 299:
|
|
270
|
-
# create errors
|
|
271
265
|
try:
|
|
272
266
|
error = ZscalerAPIError(url, resp, response_data)
|
|
273
267
|
if self.fail_safe:
|
|
@@ -280,7 +274,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
280
274
|
logger.error(response_data)
|
|
281
275
|
raise HTTPException(response_data)
|
|
282
276
|
logger.error(error)
|
|
283
|
-
# Cache the response if it's a successful GET request
|
|
284
277
|
if method == "GET" and resp.status_code == 200:
|
|
285
278
|
self.cache.add(cache_key, resp)
|
|
286
279
|
return resp
|
|
@@ -290,23 +283,34 @@ class ZPAClientHelper(ZPAClient):
|
|
|
290
283
|
Send a GET request to the ZPA API.
|
|
291
284
|
|
|
292
285
|
Parameters:
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
path (str): API endpoint path.
|
|
287
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
288
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
289
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
290
|
+
|
|
295
291
|
Returns:
|
|
296
|
-
|
|
292
|
+
dict: Formatted JSON response from the API.
|
|
297
293
|
"""
|
|
298
|
-
|
|
299
|
-
# Use rate limiter before making a request
|
|
300
294
|
should_wait, delay = self.rate_limiter.wait("GET")
|
|
301
295
|
if should_wait:
|
|
302
296
|
time.sleep(delay)
|
|
303
|
-
|
|
304
|
-
# Now proceed with sending the request
|
|
305
297
|
resp = self.send("GET", path, json, params, api_version=api_version)
|
|
306
298
|
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
307
299
|
return formatted_resp
|
|
308
300
|
|
|
309
301
|
def put(self, path, json=None, params=None, api_version: str = None):
|
|
302
|
+
"""
|
|
303
|
+
Send a PUT request to the ZPA API.
|
|
304
|
+
|
|
305
|
+
Parameters:
|
|
306
|
+
path (str): API endpoint path.
|
|
307
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
308
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
309
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
dict: Formatted JSON response from the API.
|
|
313
|
+
"""
|
|
310
314
|
should_wait, delay = self.rate_limiter.wait("PUT")
|
|
311
315
|
if should_wait:
|
|
312
316
|
time.sleep(delay)
|
|
@@ -315,6 +319,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
315
319
|
return formatted_resp
|
|
316
320
|
|
|
317
321
|
def post(self, path, json=None, params=None, api_version: str = None):
|
|
322
|
+
"""
|
|
323
|
+
Send a POST request to the ZPA API.
|
|
324
|
+
|
|
325
|
+
Parameters:
|
|
326
|
+
path (str): API endpoint path.
|
|
327
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
328
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
329
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
dict: Formatted JSON response from the API.
|
|
333
|
+
"""
|
|
318
334
|
should_wait, delay = self.rate_limiter.wait("POST")
|
|
319
335
|
if should_wait:
|
|
320
336
|
time.sleep(delay)
|
|
@@ -323,6 +339,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
323
339
|
return formatted_resp
|
|
324
340
|
|
|
325
341
|
def delete(self, path, json=None, params=None, api_version: str = None):
|
|
342
|
+
"""
|
|
343
|
+
Send a DELETE request to the ZPA API.
|
|
344
|
+
|
|
345
|
+
Parameters:
|
|
346
|
+
path (str): API endpoint path.
|
|
347
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
348
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
349
|
+
api_version (str, optional): API version to use. Defaults to None.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Response: Response object from the DELETE request.
|
|
353
|
+
"""
|
|
326
354
|
should_wait, delay = self.rate_limiter.wait("DELETE")
|
|
327
355
|
if should_wait:
|
|
328
356
|
time.sleep(delay)
|
zscaler/zpa/app_segments.py
CHANGED
|
@@ -91,6 +91,41 @@ class ApplicationSegmentAPI:
|
|
|
91
91
|
return app
|
|
92
92
|
return None
|
|
93
93
|
|
|
94
|
+
def get_segments_by_type(self, application_type: str, expand_all: bool = False, **kwargs) -> Box:
|
|
95
|
+
"""
|
|
96
|
+
Retrieve all configured application segments of a specified type, optionally expanding all related data.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
application_type (str): Type of application segment to retrieve.
|
|
100
|
+
Must be one of "BROWSER_ACCESS", "INSPECT", "SECURE_REMOTE_ACCESS".
|
|
101
|
+
expand_all (bool, optional): Whether to expand all related data. Defaults to False.
|
|
102
|
+
|
|
103
|
+
Keyword Args:
|
|
104
|
+
max_items (int, optional): The maximum number of items to request before stopping iteration.
|
|
105
|
+
max_pages (int, optional): The maximum number of pages to request before stopping iteration.
|
|
106
|
+
pagesize (int, optional): Specifies the page size. The default size is 20, but the maximum size is 500.
|
|
107
|
+
page (int, optional): Specifies the page number to begin fetching from.
|
|
108
|
+
search (str, optional): The search string used to match against features and fields.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
BoxList: List of application segments.
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
>>> app_type = 'BROWSER_ACCESS'
|
|
115
|
+
>>> expand_all = True
|
|
116
|
+
>>> search = "ba_server01"
|
|
117
|
+
>>> app_segments = zpa.app_segments.get_segments_by_type(app_type, expand_all, search=search)
|
|
118
|
+
"""
|
|
119
|
+
params = {"applicationType": application_type, "expandAll": "true" if expand_all else "false"}
|
|
120
|
+
# Include additional search parameters if specified
|
|
121
|
+
if "search" in kwargs:
|
|
122
|
+
params["search"] = kwargs["search"]
|
|
123
|
+
|
|
124
|
+
result, error = self.rest.get_paginated_data(path="/application/getAppsByType", params=params, **kwargs)
|
|
125
|
+
if error:
|
|
126
|
+
return BoxList([]) # Return an empty BoxList on failure due to the error
|
|
127
|
+
return result
|
|
128
|
+
|
|
94
129
|
def delete_segment(self, segment_id: str, force_delete: bool = False) -> int:
|
|
95
130
|
"""
|
|
96
131
|
Delete an application segment.
|