zscaler-sdk-python 1.0.0__py2.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 +34 -0
- zscaler/cache/__init__.py +0 -0
- zscaler/cache/cache.py +105 -0
- zscaler/cache/no_op_cache.py +68 -0
- zscaler/cache/zscaler_cache.py +161 -0
- zscaler/constants.py +26 -0
- zscaler/errors/__init__.py +0 -0
- zscaler/errors/error.py +10 -0
- zscaler/errors/http_error.py +20 -0
- zscaler/errors/zscaler_api_error.py +24 -0
- zscaler/exceptions/__init__.py +1 -0
- zscaler/exceptions/exceptions.py +101 -0
- zscaler/logger.py +57 -0
- zscaler/ratelimiter/__init__.py +0 -0
- zscaler/ratelimiter/ratelimiter.py +39 -0
- zscaler/user_agent.py +23 -0
- zscaler/utils.py +577 -0
- zscaler/zia/__init__.py +657 -0
- zscaler/zia/activate.py +52 -0
- zscaler/zia/admin_and_role_management.py +344 -0
- zscaler/zia/apptotal.py +71 -0
- zscaler/zia/audit_logs.py +95 -0
- zscaler/zia/authentication_settings.py +98 -0
- zscaler/zia/client.py +88 -0
- zscaler/zia/cloud_apps.py +406 -0
- zscaler/zia/device_management.py +90 -0
- zscaler/zia/dlp.py +784 -0
- zscaler/zia/errors.py +37 -0
- zscaler/zia/firewall.py +1104 -0
- zscaler/zia/forwarding_control.py +271 -0
- zscaler/zia/isolation_profile.py +83 -0
- zscaler/zia/labels.py +180 -0
- zscaler/zia/locations.py +661 -0
- zscaler/zia/sandbox.py +180 -0
- zscaler/zia/security.py +236 -0
- zscaler/zia/ssl_inspection.py +175 -0
- zscaler/zia/traffic.py +853 -0
- zscaler/zia/url_categories.py +442 -0
- zscaler/zia/url_filtering.py +310 -0
- zscaler/zia/users.py +386 -0
- zscaler/zia/web_dlp.py +295 -0
- zscaler/zia/workload_groups.py +58 -0
- zscaler/zia/zpa_gateway.py +187 -0
- zscaler/zpa/__init__.py +683 -0
- zscaler/zpa/app_segments.py +331 -0
- zscaler/zpa/app_segments_inspection.py +311 -0
- zscaler/zpa/app_segments_pra.py +310 -0
- zscaler/zpa/certificates.py +234 -0
- zscaler/zpa/client.py +113 -0
- zscaler/zpa/cloud_connector_groups.py +75 -0
- zscaler/zpa/connectors.py +518 -0
- zscaler/zpa/emergency_access.py +178 -0
- zscaler/zpa/errors.py +37 -0
- zscaler/zpa/idp.py +83 -0
- zscaler/zpa/inspection.py +1012 -0
- zscaler/zpa/isolation_profile.py +85 -0
- zscaler/zpa/lss.py +568 -0
- zscaler/zpa/machine_groups.py +79 -0
- zscaler/zpa/policies.py +848 -0
- zscaler/zpa/posture_profiles.py +122 -0
- zscaler/zpa/privileged_remote_access.py +862 -0
- zscaler/zpa/provisioning.py +271 -0
- zscaler/zpa/saml_attributes.py +100 -0
- zscaler/zpa/scim_attributes.py +117 -0
- zscaler/zpa/scim_groups.py +146 -0
- zscaler/zpa/segment_groups.py +191 -0
- zscaler/zpa/server_groups.py +217 -0
- zscaler/zpa/servers.py +202 -0
- zscaler/zpa/service_edges.py +404 -0
- zscaler/zpa/trusted_networks.py +127 -0
- zscaler_sdk_python-1.0.0.dist-info/LICENSE.md +21 -0
- zscaler_sdk_python-1.0.0.dist-info/METADATA +59 -0
- zscaler_sdk_python-1.0.0.dist-info/RECORD +75 -0
- zscaler_sdk_python-1.0.0.dist-info/WHEEL +6 -0
- zscaler_sdk_python-1.0.0.dist-info/top_level.txt +1 -0
zscaler/zia/__init__.py
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from time import sleep
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from box import Box, BoxList
|
|
11
|
+
|
|
12
|
+
from zscaler import __version__
|
|
13
|
+
from zscaler.cache.no_op_cache import NoOpCache
|
|
14
|
+
from zscaler.cache.zscaler_cache import ZscalerCache
|
|
15
|
+
from zscaler.errors.http_error import HTTPError, ZscalerAPIError
|
|
16
|
+
from zscaler.exceptions.exceptions import HTTPException, ZscalerAPIException
|
|
17
|
+
from zscaler.logger import setup_logging
|
|
18
|
+
from zscaler.ratelimiter.ratelimiter import RateLimiter
|
|
19
|
+
from zscaler.user_agent import UserAgent
|
|
20
|
+
from zscaler.utils import (
|
|
21
|
+
convert_keys_to_snake,
|
|
22
|
+
dump_request,
|
|
23
|
+
dump_response,
|
|
24
|
+
format_json_response,
|
|
25
|
+
obfuscate_api_key,
|
|
26
|
+
retry_with_backoff,
|
|
27
|
+
)
|
|
28
|
+
from zscaler.zia.client import ZIAClient
|
|
29
|
+
from zscaler.zia.activate import ActivationAPI
|
|
30
|
+
from zscaler.zia.admin_and_role_management import AdminAndRoleManagementAPI
|
|
31
|
+
from zscaler.zia.apptotal import AppTotalAPI
|
|
32
|
+
from zscaler.zia.audit_logs import AuditLogsAPI
|
|
33
|
+
from zscaler.zia.authentication_settings import AuthenticationSettingsAPI
|
|
34
|
+
from zscaler.zia.device_management import DeviceManagementAPI
|
|
35
|
+
from zscaler.zia.dlp import DLPAPI
|
|
36
|
+
from zscaler.zia.firewall import FirewallPolicyAPI
|
|
37
|
+
from zscaler.zia.forwarding_control import ForwardingControlAPI
|
|
38
|
+
from zscaler.zia.isolation_profile import IsolationProfileAPI
|
|
39
|
+
from zscaler.zia.labels import RuleLabelsAPI
|
|
40
|
+
from zscaler.zia.locations import LocationsAPI
|
|
41
|
+
from zscaler.zia.sandbox import CloudSandboxAPI
|
|
42
|
+
from zscaler.zia.security import SecurityPolicyAPI
|
|
43
|
+
from zscaler.zia.ssl_inspection import SSLInspectionAPI
|
|
44
|
+
from zscaler.zia.traffic import TrafficForwardingAPI
|
|
45
|
+
from zscaler.zia.url_categories import URLCategoriesAPI
|
|
46
|
+
from zscaler.zia.url_filtering import URLFilteringAPI
|
|
47
|
+
from zscaler.zia.users import UserManagementAPI
|
|
48
|
+
from zscaler.zia.web_dlp import WebDLPAPI
|
|
49
|
+
from zscaler.zia.workload_groups import WorkloadGroupsAPI
|
|
50
|
+
from zscaler.zia.zpa_gateway import ZPAGatewayAPI
|
|
51
|
+
|
|
52
|
+
# Setup the logger
|
|
53
|
+
setup_logging(logger_name="zscaler-sdk-python")
|
|
54
|
+
logger = logging.getLogger("zscaler-sdk-python")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ZIAClientHelper(ZIAClient):
|
|
58
|
+
"""
|
|
59
|
+
A Controller to access Endpoints in the Zscaler Internet Access (ZIA) API.
|
|
60
|
+
|
|
61
|
+
The ZIA object stores the session token and simplifies access to CRUD options within the ZIA platform.
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
api_key (str): The ZIA API key generated from the ZIA console.
|
|
65
|
+
username (str): The ZIA administrator username.
|
|
66
|
+
password (str): The ZIA administrator password.
|
|
67
|
+
cloud (str): The Zscaler cloud for your tenancy, accepted values are:
|
|
68
|
+
|
|
69
|
+
* ``zscaler``
|
|
70
|
+
* ``zscloud``
|
|
71
|
+
* ``zscalerbeta``
|
|
72
|
+
* ``zspreview``
|
|
73
|
+
* ``zscalerone``
|
|
74
|
+
* ``zscalertwo``
|
|
75
|
+
* ``zscalerthree``
|
|
76
|
+
* ``zscalergov``
|
|
77
|
+
* ``zscalerten``
|
|
78
|
+
|
|
79
|
+
override_url (str):
|
|
80
|
+
If supplied, this attribute can be used to override the production URL that is derived
|
|
81
|
+
from supplying the `cloud` attribute. Use this attribute if you have a non-standard tenant URL
|
|
82
|
+
(e.g. internal test instance etc). When using this attribute, there is no need to supply the `cloud`
|
|
83
|
+
attribute. The override URL will be prepended to the API endpoint suffixes. The protocol must be included
|
|
84
|
+
i.e. http:// or https://.
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
_vendor = "Zscaler"
|
|
89
|
+
_product = "Zscaler Internet Access"
|
|
90
|
+
_build = __version__
|
|
91
|
+
_env_base = "ZIA"
|
|
92
|
+
url = "https://zsapi.zscaler.net/api/v1"
|
|
93
|
+
env_cloud = "zscaler"
|
|
94
|
+
|
|
95
|
+
def __init__(self, cloud, timeout=240, cache=None, fail_safe=False, **kw):
|
|
96
|
+
self.api_key = kw.get("api_key", os.getenv(f"{self._env_base}_API_KEY"))
|
|
97
|
+
self.username = kw.get("username", os.getenv(f"{self._env_base}_USERNAME"))
|
|
98
|
+
self.password = kw.get("password", os.getenv(f"{self._env_base}_PASSWORD"))
|
|
99
|
+
# The 'cloud' parameter should have precedence over environment variables
|
|
100
|
+
self.env_cloud = (
|
|
101
|
+
cloud or kw.get("cloud") or os.getenv(f"{self._env_base}_CLOUD")
|
|
102
|
+
)
|
|
103
|
+
if not self.env_cloud:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Cloud environment must be set via the 'cloud' argument or the {self._env_base}_CLOUD environment variable."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# URL construction
|
|
109
|
+
if cloud == "zspreview":
|
|
110
|
+
self.url = f"https://admin.{self.env_cloud}.net/api/v1"
|
|
111
|
+
else:
|
|
112
|
+
# Use override URL if provided, else construct the URL
|
|
113
|
+
self.url = (
|
|
114
|
+
kw.get("override_url")
|
|
115
|
+
or os.getenv(f"{self._env_base}_OVERRIDE_URL")
|
|
116
|
+
or f"https://zsapi.{self.env_cloud}.net/api/v1"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
self.conv_box = True
|
|
120
|
+
self.sandbox_token = kw.get("sandbox_token") or os.getenv(
|
|
121
|
+
f"{self._env_base}_SANDBOX_TOKEN"
|
|
122
|
+
)
|
|
123
|
+
self.timeout = timeout
|
|
124
|
+
self.fail_safe = fail_safe
|
|
125
|
+
cache_enabled = (
|
|
126
|
+
os.environ.get("ZSCALER_CLIENT_CACHE_ENABLED", "true").lower() == "true"
|
|
127
|
+
)
|
|
128
|
+
if cache is None:
|
|
129
|
+
if cache_enabled:
|
|
130
|
+
ttl = int(os.environ.get("ZSCALER_CLIENT_CACHE_DEFAULT_TTL", 3600))
|
|
131
|
+
tti = int(os.environ.get("ZSCALER_CLIENT_CACHE_DEFAULT_TTI", 1800))
|
|
132
|
+
self.cache = ZscalerCache(ttl=ttl, tti=tti)
|
|
133
|
+
else:
|
|
134
|
+
self.cache = NoOpCache()
|
|
135
|
+
else:
|
|
136
|
+
self.cache = cache
|
|
137
|
+
# Initialize user-agent
|
|
138
|
+
ua = UserAgent()
|
|
139
|
+
self.user_agent = ua.get_user_agent_string()
|
|
140
|
+
# Initialize rate limiter
|
|
141
|
+
# You may want to adjust these parameters as per your rate limit configuration
|
|
142
|
+
self.rate_limiter = RateLimiter(
|
|
143
|
+
get_limit=2, # Adjust as per actual limit
|
|
144
|
+
post_put_delete_limit=2, # Adjust as per actual limit
|
|
145
|
+
get_freq=2, # Adjust as per actual frequency (in seconds)
|
|
146
|
+
post_put_delete_freq=2, # Adjust as per actual frequency (in seconds)
|
|
147
|
+
)
|
|
148
|
+
self.headers = {
|
|
149
|
+
"Content-Type": "application/json",
|
|
150
|
+
"Accept": "application/json",
|
|
151
|
+
"User-Agent": self.user_agent,
|
|
152
|
+
}
|
|
153
|
+
self.session_timeout_offset = datetime.timedelta(minutes=5)
|
|
154
|
+
self.session_refreshed = None
|
|
155
|
+
self.auth_details = None
|
|
156
|
+
self.session_id = None
|
|
157
|
+
self.authenticate()
|
|
158
|
+
|
|
159
|
+
def extractJSessionIDFromHeaders(self, header):
|
|
160
|
+
session_id_str = header.get("Set-Cookie", "")
|
|
161
|
+
|
|
162
|
+
if not session_id_str:
|
|
163
|
+
raise ValueError("no Set-Cookie header received")
|
|
164
|
+
|
|
165
|
+
regex = re.compile(r"JSESSIONID=(.*?);")
|
|
166
|
+
result = regex.search(session_id_str)
|
|
167
|
+
|
|
168
|
+
if not result:
|
|
169
|
+
raise ValueError("couldn't find JSESSIONID in header value")
|
|
170
|
+
|
|
171
|
+
return result.group(1)
|
|
172
|
+
|
|
173
|
+
def is_session_expired(self):
|
|
174
|
+
if self.auth_details is None:
|
|
175
|
+
return True
|
|
176
|
+
now = datetime.datetime.now()
|
|
177
|
+
if self.auth_details["passwordExpiryTime"] > 0 and (
|
|
178
|
+
self.session_refreshed
|
|
179
|
+
+ datetime.timedelta(seconds=-self.session_timeout_offset)
|
|
180
|
+
< now
|
|
181
|
+
):
|
|
182
|
+
return True
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
@retry_with_backoff(retries=5)
|
|
186
|
+
def authenticate(self) -> Box:
|
|
187
|
+
"""
|
|
188
|
+
Creates a ZIA authentication session.
|
|
189
|
+
"""
|
|
190
|
+
api_key_chars = list(self.api_key)
|
|
191
|
+
api_obf = obfuscate_api_key(api_key_chars)
|
|
192
|
+
|
|
193
|
+
payload = {
|
|
194
|
+
"apiKey": api_obf["key"],
|
|
195
|
+
"username": self.username,
|
|
196
|
+
"password": self.password,
|
|
197
|
+
"timestamp": api_obf["timestamp"],
|
|
198
|
+
}
|
|
199
|
+
resp = requests.request(
|
|
200
|
+
"POST",
|
|
201
|
+
self.url + "/authenticatedSession",
|
|
202
|
+
json=payload,
|
|
203
|
+
headers=self.headers,
|
|
204
|
+
timeout=self.timeout,
|
|
205
|
+
)
|
|
206
|
+
if resp.status_code > 299:
|
|
207
|
+
return resp
|
|
208
|
+
self.session_refreshed = datetime.datetime.now()
|
|
209
|
+
self.session_id = self.extractJSessionIDFromHeaders(resp.headers)
|
|
210
|
+
self.auth_details = resp.json()
|
|
211
|
+
return resp
|
|
212
|
+
|
|
213
|
+
def deauthenticate(self):
|
|
214
|
+
"""
|
|
215
|
+
Ends the ZIA authentication session.
|
|
216
|
+
"""
|
|
217
|
+
logout_url = self.url + "/authenticatedSession"
|
|
218
|
+
|
|
219
|
+
headers = self.headers.copy()
|
|
220
|
+
headers.update({"Cookie": f"JSESSIONID={self.session_id}"})
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
response = requests.delete(
|
|
224
|
+
logout_url, headers=headers, timeout=self.timeout
|
|
225
|
+
)
|
|
226
|
+
if response.status_code == 204:
|
|
227
|
+
self.session_id = None
|
|
228
|
+
self.auth_details = None
|
|
229
|
+
return True
|
|
230
|
+
else:
|
|
231
|
+
return False
|
|
232
|
+
except requests.RequestException as e:
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
def send(self, method, path, json=None, params=None, data=None, headers=None):
|
|
236
|
+
"""
|
|
237
|
+
Send a request to the ZIA API.
|
|
238
|
+
|
|
239
|
+
Parameters:
|
|
240
|
+
- method (str): The HTTP method.
|
|
241
|
+
- path (str): API endpoint path.
|
|
242
|
+
- json (dict, optional): Request payload. Defaults to None.
|
|
243
|
+
Returns:
|
|
244
|
+
- Response: Response object from the request.
|
|
245
|
+
"""
|
|
246
|
+
is_sandbox = "zscsb" in path
|
|
247
|
+
api = self.url
|
|
248
|
+
if is_sandbox:
|
|
249
|
+
api = f"https://csbapi.{self.env_cloud}.net"
|
|
250
|
+
url = f"{api}/{path.lstrip('/')}"
|
|
251
|
+
start_time = time.time()
|
|
252
|
+
# Update headers to include the user agent
|
|
253
|
+
headers_with_user_agent = self.headers.copy()
|
|
254
|
+
headers_with_user_agent["User-Agent"] = self.user_agent
|
|
255
|
+
# Generate a unique UUID for this request
|
|
256
|
+
request_uuid = uuid.uuid4()
|
|
257
|
+
if headers is not None:
|
|
258
|
+
headers_with_user_agent.update(headers)
|
|
259
|
+
dump_request(
|
|
260
|
+
logger,
|
|
261
|
+
url,
|
|
262
|
+
method,
|
|
263
|
+
json,
|
|
264
|
+
params,
|
|
265
|
+
headers_with_user_agent,
|
|
266
|
+
request_uuid,
|
|
267
|
+
body=not is_sandbox,
|
|
268
|
+
)
|
|
269
|
+
# Check cache before sending request
|
|
270
|
+
cache_key = self.cache.create_key(url, params)
|
|
271
|
+
if method == "GET" and self.cache.contains(cache_key):
|
|
272
|
+
resp = self.cache.get(cache_key)
|
|
273
|
+
dump_response(
|
|
274
|
+
logger=logger,
|
|
275
|
+
url=url,
|
|
276
|
+
method=method,
|
|
277
|
+
params=params,
|
|
278
|
+
resp=resp,
|
|
279
|
+
request_uuid=request_uuid,
|
|
280
|
+
start_time=start_time,
|
|
281
|
+
from_cache=True,
|
|
282
|
+
)
|
|
283
|
+
return resp
|
|
284
|
+
|
|
285
|
+
attempts = 0
|
|
286
|
+
while attempts < 5: # Trying a maximum of 5 times
|
|
287
|
+
try:
|
|
288
|
+
# If the token is None or expired, fetch a new token
|
|
289
|
+
if self.is_session_expired():
|
|
290
|
+
logger.warning("The provided sesion expired. Refreshing...")
|
|
291
|
+
self.authenticate()
|
|
292
|
+
resp = requests.request(
|
|
293
|
+
method=method,
|
|
294
|
+
url=url,
|
|
295
|
+
json=json,
|
|
296
|
+
data=data,
|
|
297
|
+
params=params,
|
|
298
|
+
headers=headers_with_user_agent,
|
|
299
|
+
timeout=self.timeout,
|
|
300
|
+
cookies={"JSESSIONID": self.session_id},
|
|
301
|
+
)
|
|
302
|
+
dump_response(
|
|
303
|
+
logger=logger,
|
|
304
|
+
url=url,
|
|
305
|
+
params=params,
|
|
306
|
+
method=method,
|
|
307
|
+
resp=resp,
|
|
308
|
+
request_uuid=request_uuid,
|
|
309
|
+
start_time=start_time,
|
|
310
|
+
)
|
|
311
|
+
if (
|
|
312
|
+
resp.status_code == 429
|
|
313
|
+
): # HTTP Status code 429 indicates "Too Many Requests"
|
|
314
|
+
sleep_time = int(
|
|
315
|
+
resp.headers.get("Retry-After", 2)
|
|
316
|
+
) # Default to 60 seconds if 'Retry-After' header is missing
|
|
317
|
+
logger.warning(
|
|
318
|
+
f"Rate limit exceeded. Retrying in {sleep_time} seconds."
|
|
319
|
+
)
|
|
320
|
+
sleep(sleep_time)
|
|
321
|
+
attempts += 1
|
|
322
|
+
continue
|
|
323
|
+
else:
|
|
324
|
+
break
|
|
325
|
+
except requests.RequestException as e:
|
|
326
|
+
if attempts == 4: # If it's the last attempt, raise the exception
|
|
327
|
+
logger.error(
|
|
328
|
+
f"Failed to send {method} request to {url} after 5 attempts. Error: {str(e)}"
|
|
329
|
+
)
|
|
330
|
+
raise e
|
|
331
|
+
else:
|
|
332
|
+
logger.warning(
|
|
333
|
+
f"Failed to send {method} request to {url}. Retrying... Error: {str(e)}"
|
|
334
|
+
)
|
|
335
|
+
attempts += 1
|
|
336
|
+
sleep(5) # Sleep for 5 seconds before retrying
|
|
337
|
+
|
|
338
|
+
# If Non-GET call, clear the
|
|
339
|
+
if method != "GET":
|
|
340
|
+
self.cache.delete(cache_key)
|
|
341
|
+
|
|
342
|
+
# Detailed logging for request and response
|
|
343
|
+
try:
|
|
344
|
+
response_data = resp.json()
|
|
345
|
+
except ValueError: # Using ValueError for JSON decoding errors
|
|
346
|
+
response_data = resp.text
|
|
347
|
+
# check if call was succesful
|
|
348
|
+
if 200 > resp.status_code or resp.status_code > 299:
|
|
349
|
+
# create errors
|
|
350
|
+
try:
|
|
351
|
+
error = ZscalerAPIError(url, resp, response_data)
|
|
352
|
+
if self.fail_safe:
|
|
353
|
+
raise ZscalerAPIException(url, resp, response_data)
|
|
354
|
+
except ZscalerAPIException:
|
|
355
|
+
raise
|
|
356
|
+
except Exception:
|
|
357
|
+
error = HTTPError(url, resp, response_data)
|
|
358
|
+
if self.fail_safe:
|
|
359
|
+
logger.error(response_data)
|
|
360
|
+
raise HTTPException(url, resp, response_data)
|
|
361
|
+
logger.error(error)
|
|
362
|
+
# Cache the response if it's a successful GET request
|
|
363
|
+
if method == "GET" and resp.status_code == 200:
|
|
364
|
+
self.cache.add(cache_key, resp)
|
|
365
|
+
return resp
|
|
366
|
+
|
|
367
|
+
def get(self, path, json=None, params=None):
|
|
368
|
+
"""
|
|
369
|
+
Send a GET request to the ZIA API.
|
|
370
|
+
|
|
371
|
+
Parameters:
|
|
372
|
+
- path (str): API endpoint path.
|
|
373
|
+
- data (dict, optional): Request payload. Defaults to None.
|
|
374
|
+
Returns:
|
|
375
|
+
- Response: Response object from the request.
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
# Use rate limiter before making a request
|
|
379
|
+
should_wait, delay = self.rate_limiter.wait("GET")
|
|
380
|
+
if should_wait:
|
|
381
|
+
time.sleep(delay)
|
|
382
|
+
|
|
383
|
+
# Now proceed with sending the request
|
|
384
|
+
resp = self.send("GET", path, json, params)
|
|
385
|
+
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
386
|
+
return formatted_resp
|
|
387
|
+
|
|
388
|
+
def put(self, path, json=None, params=None):
|
|
389
|
+
should_wait, delay = self.rate_limiter.wait("PUT")
|
|
390
|
+
if should_wait:
|
|
391
|
+
time.sleep(delay)
|
|
392
|
+
resp = self.send("PUT", path, json, params)
|
|
393
|
+
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
394
|
+
return formatted_resp
|
|
395
|
+
|
|
396
|
+
def post(self, path, json=None, params=None, data=None, headers=None):
|
|
397
|
+
should_wait, delay = self.rate_limiter.wait("POST")
|
|
398
|
+
if should_wait:
|
|
399
|
+
time.sleep(delay)
|
|
400
|
+
resp = self.send("POST", path, json, params, data=data, headers=headers)
|
|
401
|
+
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
402
|
+
return formatted_resp
|
|
403
|
+
|
|
404
|
+
def delete(self, path, json=None, params=None):
|
|
405
|
+
should_wait, delay = self.rate_limiter.wait("DELETE")
|
|
406
|
+
if should_wait:
|
|
407
|
+
time.sleep(delay)
|
|
408
|
+
return self.send("DELETE", path, json, params)
|
|
409
|
+
|
|
410
|
+
ERROR_MESSAGES = {
|
|
411
|
+
"UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
|
|
412
|
+
"MISSING_DATA_KEY": "The key '{data_key_name}' was not found in the response for page {page}.",
|
|
413
|
+
"EMPTY_RESULTS": "No results found for page {page}.",
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
def get_paginated_data(
|
|
417
|
+
self, path=None, data_key_name=None, data_per_page=5, expected_status_code=200
|
|
418
|
+
):
|
|
419
|
+
"""
|
|
420
|
+
Fetch paginated data from the ZIA API.
|
|
421
|
+
...
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
- list: List of fetched items.
|
|
425
|
+
- str: Error message, if any occurred.
|
|
426
|
+
"""
|
|
427
|
+
|
|
428
|
+
page = 1
|
|
429
|
+
ret_data = []
|
|
430
|
+
error_message = None
|
|
431
|
+
|
|
432
|
+
while True:
|
|
433
|
+
required_url = f"{path}"
|
|
434
|
+
should_wait, delay = self.rate_limiter.wait("GET")
|
|
435
|
+
if should_wait:
|
|
436
|
+
time.sleep(delay)
|
|
437
|
+
|
|
438
|
+
# Now proceed with sending the request
|
|
439
|
+
response = self.send(
|
|
440
|
+
method="GET",
|
|
441
|
+
path=required_url,
|
|
442
|
+
params={"page": page, "pageSize": data_per_page},
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if response.status_code != expected_status_code:
|
|
446
|
+
error_message = self.ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
|
|
447
|
+
status_code=response.status_code, page=page
|
|
448
|
+
)
|
|
449
|
+
logger.error(error_message)
|
|
450
|
+
break
|
|
451
|
+
data_json = response.json()
|
|
452
|
+
if isinstance(data_json, list):
|
|
453
|
+
data = data_json
|
|
454
|
+
else:
|
|
455
|
+
data = data_json.get(data_key_name)
|
|
456
|
+
|
|
457
|
+
if data is None:
|
|
458
|
+
error_message = self.ERROR_MESSAGES["MISSING_DATA_KEY"].format(
|
|
459
|
+
data_key_name=data_key_name, page=page
|
|
460
|
+
)
|
|
461
|
+
logger.error(error_message)
|
|
462
|
+
break
|
|
463
|
+
|
|
464
|
+
if not data: # Checks for empty data
|
|
465
|
+
logger.info(self.ERROR_MESSAGES["EMPTY_RESULTS"].format(page=page))
|
|
466
|
+
break
|
|
467
|
+
|
|
468
|
+
ret_data.extend(convert_keys_to_snake(data))
|
|
469
|
+
|
|
470
|
+
# Check for more pages
|
|
471
|
+
if (
|
|
472
|
+
len(data) == 0
|
|
473
|
+
or isinstance(data_json, dict)
|
|
474
|
+
and int(response.json().get("totalPages")) <= page + 1
|
|
475
|
+
):
|
|
476
|
+
break
|
|
477
|
+
|
|
478
|
+
page += 1
|
|
479
|
+
|
|
480
|
+
return BoxList(ret_data), error_message
|
|
481
|
+
|
|
482
|
+
@property
|
|
483
|
+
def admin_and_role_management(self):
|
|
484
|
+
"""
|
|
485
|
+
The interface object for the :ref:`ZIA Admin and Role Management interface <zia-admin_and_role_management>`.
|
|
486
|
+
|
|
487
|
+
"""
|
|
488
|
+
return AdminAndRoleManagementAPI(self)
|
|
489
|
+
|
|
490
|
+
@property
|
|
491
|
+
def apptotal(self):
|
|
492
|
+
"""
|
|
493
|
+
The interface object for the :ref:`ZIA AppTotal interface <zia-apptotal>`.
|
|
494
|
+
|
|
495
|
+
"""
|
|
496
|
+
return AppTotalAPI(self)
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def audit_logs(self):
|
|
500
|
+
"""
|
|
501
|
+
The interface object for the :ref:`ZIA Admin Audit Logs interface <zia-audit_logs>`.
|
|
502
|
+
|
|
503
|
+
"""
|
|
504
|
+
return AuditLogsAPI(self)
|
|
505
|
+
|
|
506
|
+
@property
|
|
507
|
+
def activate(self):
|
|
508
|
+
"""
|
|
509
|
+
The interface object for the :ref:`ZIA Activation interface <zia-activate>`.
|
|
510
|
+
|
|
511
|
+
"""
|
|
512
|
+
return ActivationAPI(self)
|
|
513
|
+
|
|
514
|
+
@property
|
|
515
|
+
def dlp(self):
|
|
516
|
+
"""
|
|
517
|
+
The interface object for the :ref:`ZIA DLP Dictionaries interface <zia-dlp>`.
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
"""
|
|
521
|
+
return DLPAPI(self)
|
|
522
|
+
|
|
523
|
+
@property
|
|
524
|
+
def firewall(self):
|
|
525
|
+
"""
|
|
526
|
+
The interface object for the :ref:`ZIA Firewall Policies interface <zia-firewall>`.
|
|
527
|
+
|
|
528
|
+
"""
|
|
529
|
+
return FirewallPolicyAPI(self)
|
|
530
|
+
|
|
531
|
+
@property
|
|
532
|
+
def forwarding_control(self):
|
|
533
|
+
"""
|
|
534
|
+
The interface object for the :ref:`ZIA Forwarding Control Policies interface <zia-forwarding_control>`.
|
|
535
|
+
|
|
536
|
+
"""
|
|
537
|
+
return ForwardingControlAPI(self)
|
|
538
|
+
|
|
539
|
+
@property
|
|
540
|
+
def labels(self):
|
|
541
|
+
"""
|
|
542
|
+
The interface object for the :ref:`ZIA Rule Labels interface <zia-labels>`.
|
|
543
|
+
|
|
544
|
+
"""
|
|
545
|
+
return RuleLabelsAPI(self)
|
|
546
|
+
|
|
547
|
+
@property
|
|
548
|
+
def device_management(self):
|
|
549
|
+
"""
|
|
550
|
+
The interface object for the :ref:`ZIA device interface <zia-device_management>`.
|
|
551
|
+
|
|
552
|
+
"""
|
|
553
|
+
return DeviceManagementAPI(self)
|
|
554
|
+
|
|
555
|
+
@property
|
|
556
|
+
def locations(self):
|
|
557
|
+
"""
|
|
558
|
+
The interface object for the :ref:`ZIA Locations interface <zia-locations>`.
|
|
559
|
+
|
|
560
|
+
"""
|
|
561
|
+
return LocationsAPI(self)
|
|
562
|
+
|
|
563
|
+
@property
|
|
564
|
+
def sandbox(self):
|
|
565
|
+
"""
|
|
566
|
+
The interface object for the :ref:`ZIA Cloud Sandbox interface <zia-sandbox>`.
|
|
567
|
+
|
|
568
|
+
"""
|
|
569
|
+
return CloudSandboxAPI(self)
|
|
570
|
+
|
|
571
|
+
@property
|
|
572
|
+
def security(self):
|
|
573
|
+
"""
|
|
574
|
+
The interface object for the :ref:`ZIA Security Policy Settings interface <zia-security>`.
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
return SecurityPolicyAPI(self)
|
|
578
|
+
|
|
579
|
+
@property
|
|
580
|
+
def authentication_settings(self):
|
|
581
|
+
"""
|
|
582
|
+
The interface object for the :ref:`ZIA Authentication Security Settings interface <zia-authentication_settings>`.
|
|
583
|
+
|
|
584
|
+
"""
|
|
585
|
+
return AuthenticationSettingsAPI(self)
|
|
586
|
+
|
|
587
|
+
@property
|
|
588
|
+
def ssl(self):
|
|
589
|
+
"""
|
|
590
|
+
The interface object for the :ref:`ZIA SSL Inspection interface <zia-ssl_inspection>`.
|
|
591
|
+
|
|
592
|
+
"""
|
|
593
|
+
return SSLInspectionAPI(self)
|
|
594
|
+
|
|
595
|
+
@property
|
|
596
|
+
def traffic(self):
|
|
597
|
+
"""
|
|
598
|
+
The interface object for the :ref:`ZIA Traffic Forwarding interface <zia-traffic>`.
|
|
599
|
+
|
|
600
|
+
"""
|
|
601
|
+
return TrafficForwardingAPI(self)
|
|
602
|
+
|
|
603
|
+
@property
|
|
604
|
+
def url_categories(self):
|
|
605
|
+
"""
|
|
606
|
+
The interface object for the :ref:`ZIA URL Categories interface <zia-url_categories>`.
|
|
607
|
+
|
|
608
|
+
"""
|
|
609
|
+
return URLCategoriesAPI(self)
|
|
610
|
+
|
|
611
|
+
@property
|
|
612
|
+
def url_filtering(self):
|
|
613
|
+
"""
|
|
614
|
+
The interface object for the :ref:`ZIA URL Filtering interface <zia-url_filtering>`.
|
|
615
|
+
|
|
616
|
+
"""
|
|
617
|
+
return URLFilteringAPI(self)
|
|
618
|
+
|
|
619
|
+
@property
|
|
620
|
+
def users(self):
|
|
621
|
+
"""
|
|
622
|
+
The interface object for the :ref:`ZIA User Management interface <zia-users>`.
|
|
623
|
+
|
|
624
|
+
"""
|
|
625
|
+
return UserManagementAPI(self)
|
|
626
|
+
|
|
627
|
+
@property
|
|
628
|
+
def web_dlp(self):
|
|
629
|
+
"""
|
|
630
|
+
The interface object for the :ref:`ZIA Web DLP interface <zia-web_dlp>`.
|
|
631
|
+
|
|
632
|
+
"""
|
|
633
|
+
return WebDLPAPI(self)
|
|
634
|
+
|
|
635
|
+
@property
|
|
636
|
+
def zpa_gateway(self):
|
|
637
|
+
"""
|
|
638
|
+
The interface object for the :ref:`ZPA Gateway <zia-zpa_gateway>`.
|
|
639
|
+
|
|
640
|
+
"""
|
|
641
|
+
return ZPAGatewayAPI(self)
|
|
642
|
+
|
|
643
|
+
@property
|
|
644
|
+
def isolation_profile(self):
|
|
645
|
+
"""
|
|
646
|
+
The interface object for the :ref:`ZIA Cloud Browser Isolation Profile <zia-isolation_profile>`.
|
|
647
|
+
|
|
648
|
+
"""
|
|
649
|
+
return IsolationProfileAPI(self)
|
|
650
|
+
|
|
651
|
+
@property
|
|
652
|
+
def workload_groups(self):
|
|
653
|
+
"""
|
|
654
|
+
The interface object for the :ref:`ZIA Workload Groups <zia-workload_groups>`.
|
|
655
|
+
|
|
656
|
+
"""
|
|
657
|
+
return WorkloadGroupsAPI(self)
|
zscaler/zia/activate.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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 zscaler.zia import ZIAClient
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ActivationAPI:
|
|
22
|
+
|
|
23
|
+
def __init__(self, client: ZIAClient):
|
|
24
|
+
self.rest = client
|
|
25
|
+
|
|
26
|
+
def status(self) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Returns the activation status for a configuration change.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
:obj:`str`
|
|
32
|
+
Configuration status.
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
>>> config_status = zia.config.status()
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
return self.rest.get("status").status
|
|
39
|
+
|
|
40
|
+
def activate(self) -> str:
|
|
41
|
+
"""
|
|
42
|
+
Activates configuration changes.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
:obj:`str`
|
|
46
|
+
Configuration status.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
>>> config_activate = zia.config.activate()
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
return self.rest.post("status/activate").status
|