zscaler-sdk-python 0.3.1__tar.gz → 0.5.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.1 → zscaler_sdk_python-0.5.0}/PKG-INFO +5 -5
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/pyproject.toml +7 -6
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/__init__.py +1 -1
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/constants.py +1 -1
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/utils.py +35 -3
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/firewall.py +0 -3
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/forwarding_control.py +11 -14
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/zpa_gateway.py +21 -26
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/__init__.py +63 -24
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments.py +157 -76
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments_inspection.py +45 -54
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments_pra.py +49 -59
- zscaler_sdk_python-0.5.0/zscaler/zpa/authdomains.py +41 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/certificates.py +90 -59
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/client.py +3 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/connectors.py +139 -88
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/emergency_access.py +16 -1
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/idp.py +18 -3
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/inspection.py +215 -227
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/machine_groups.py +24 -10
- zscaler_sdk_python-0.5.0/zscaler/zpa/microtenants.py +217 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/policies.py +242 -405
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/privileged_remote_access.py +48 -5
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/provisioning.py +35 -16
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/segment_groups.py +51 -41
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/server_groups.py +34 -12
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/servers.py +42 -22
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/service_edges.py +241 -61
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/trusted_networks.py +20 -5
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/README.md +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/__init__.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/cache.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/no_op_cache.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/zscaler_cache.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/__init__.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/error.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/http_error.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/zscaler_api_error.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/exceptions/__init__.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/exceptions/exceptions.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/logger.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/ratelimiter/__init__.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/ratelimiter/ratelimiter.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/user_agent.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/__init__.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/activate.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/apptotal.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/audit_logs.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/authentication_settings.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/client.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/cloud_apps.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/device_management.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/dlp.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/errors.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/isolation_profile.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/labels.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/locations.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/sandbox.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/security.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/ssl_inspection.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/traffic.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/url_categories.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/url_filtering.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/users.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/web_dlp.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/workload_groups.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/README.md +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/cloud_connector_groups.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/errors.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/isolation.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/lss.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/posture_profiles.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/saml_attributes.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/scim_attributes.py +0 -0
- {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.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.5.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
|
|
@@ -24,21 +24,21 @@ Provides-Extra: dev
|
|
|
24
24
|
Requires-Dist: aenum ; extra == "dev"
|
|
25
25
|
Requires-Dist: arrow
|
|
26
26
|
Requires-Dist: black (>=24.3.0) ; extra == "dev"
|
|
27
|
-
Requires-Dist: certifi
|
|
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
33
|
Requires-Dist: okta (>=2.9.7)
|
|
34
34
|
Requires-Dist: pycryptodomex
|
|
35
35
|
Requires-Dist: pydash ; extra == "dev"
|
|
36
|
-
Requires-Dist: python-box (>=7.
|
|
36
|
+
Requires-Dist: python-box (>=7.2.0,<8.0.0)
|
|
37
37
|
Requires-Dist: python-dateutil
|
|
38
38
|
Requires-Dist: pytz
|
|
39
39
|
Requires-Dist: pyyaml
|
|
40
40
|
Requires-Dist: requests (>=2.32.0)
|
|
41
|
-
Requires-Dist: responses
|
|
41
|
+
Requires-Dist: responses (>=0.25.3)
|
|
42
42
|
Requires-Dist: restfly
|
|
43
43
|
Requires-Dist: six
|
|
44
44
|
Requires-Dist: xmltodict
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "zscaler-sdk-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.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
|
-
python-box = "^7.
|
|
36
|
+
python-box = "^7.2.0"
|
|
37
37
|
python-dateutil = "*"
|
|
38
38
|
requests = ">=2.32.0"
|
|
39
|
-
responses = "
|
|
39
|
+
responses = ">=0.25.3"
|
|
40
40
|
restfly = "*"
|
|
41
41
|
six = "*"
|
|
42
42
|
flatdict = "*"
|
|
@@ -49,12 +49,13 @@ pydash = "*"
|
|
|
49
49
|
flake8 = "*"
|
|
50
50
|
pytz = "*"
|
|
51
51
|
black = ">=24.3.0"
|
|
52
|
-
cryptography = ">=3.4,<43.0"
|
|
52
|
+
# cryptography = ">=3.4,<43.0"
|
|
53
|
+
cryptography = ">=42.0.7"
|
|
53
54
|
okta = ">=2.9.7"
|
|
54
55
|
|
|
55
56
|
[tool.poetry.dev-dependencies]
|
|
56
57
|
black = ">=24.3.0"
|
|
57
|
-
pytest = "
|
|
58
|
+
pytest = ">=8.2.1"
|
|
58
59
|
pytest-asyncio = ">=0.23.7"
|
|
59
60
|
pytest-mock = "*"
|
|
60
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
|
|
@@ -147,11 +147,42 @@ def add_id_groups(id_groups: list, kwargs: dict, payload: dict):
|
|
|
147
147
|
return
|
|
148
148
|
|
|
149
149
|
|
|
150
|
+
# def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
151
|
+
# for entry in id_groups:
|
|
152
|
+
# if kwargs.get(entry[0]):
|
|
153
|
+
# # Ensure each ID is treated as an integer before adding it to the payload
|
|
154
|
+
# payload[entry[1]] = [{"id": int(param_id)} for param_id in kwargs.pop(entry[0])]
|
|
155
|
+
# return
|
|
156
|
+
|
|
157
|
+
# ####### Function used in the ZIA Forwarding Control Rule #######
|
|
158
|
+
# def transform_fwd_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
159
|
+
# for entry in id_groups:
|
|
160
|
+
# key, payload_key = entry
|
|
161
|
+
# if key in kwargs:
|
|
162
|
+
# value = kwargs.pop(key)
|
|
163
|
+
# if isinstance(value, dict):
|
|
164
|
+
# payload[payload_key] = {k: v for k, v in value.items() if k in ['id', 'name']}
|
|
165
|
+
# elif isinstance(value, list):
|
|
166
|
+
# payload[payload_key] = [{"id": int(item)} if isinstance(item, str) and item.isdigit() else item for item in value]
|
|
167
|
+
# return
|
|
168
|
+
|
|
150
169
|
def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
|
|
151
170
|
for entry in id_groups:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
key, payload_key = entry
|
|
172
|
+
if key in kwargs:
|
|
173
|
+
value = kwargs.pop(key)
|
|
174
|
+
if key in ["zpa_gateway", "proxy_gateway", "zpa_server_group"]:
|
|
175
|
+
# Handle zpa_gateway, proxy_gateway, and zpa_server_group
|
|
176
|
+
if isinstance(value, dict):
|
|
177
|
+
payload[payload_key] = {snake_to_camel(k): v for k, v in value.items() if k in ['id', 'name', 'external_id']}
|
|
178
|
+
elif key in ["zpa_app_segments", "zpa_application_segments", "zpa_application_segment_groups"]:
|
|
179
|
+
# Handle zpa_app_segments, zpa_application_segments, and zpa_application_segment_groups
|
|
180
|
+
if isinstance(value, list):
|
|
181
|
+
payload[payload_key] = [{"externalId": item["external_id"], "name": item["name"]} for item in value]
|
|
182
|
+
else:
|
|
183
|
+
# General case for ID transformations
|
|
184
|
+
if isinstance(value, list):
|
|
185
|
+
payload[payload_key] = [{"id": int(item)} if isinstance(item, (str, int)) else item for item in value]
|
|
155
186
|
return
|
|
156
187
|
|
|
157
188
|
|
|
@@ -544,3 +575,4 @@ def dump_response(
|
|
|
544
575
|
log_lines.append(f"\n{response_body}")
|
|
545
576
|
log_lines.append("-" * 68)
|
|
546
577
|
logger.info("\n".join(log_lines))
|
|
578
|
+
|
|
@@ -233,10 +233,7 @@ class FirewallPolicyAPI:
|
|
|
233
233
|
|
|
234
234
|
response = self.rest.put(f"firewallFilteringRules/{rule_id}", json=payload)
|
|
235
235
|
if isinstance(response, Response) and not response.ok:
|
|
236
|
-
# Handle error response
|
|
237
236
|
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
238
|
-
|
|
239
|
-
# Return the updated object
|
|
240
237
|
return self.get_rule(rule_id)
|
|
241
238
|
|
|
242
239
|
def delete_rule(self, rule_id: str) -> int:
|
|
@@ -68,10 +68,7 @@ class ForwardingControlAPI:
|
|
|
68
68
|
... pprint(rule)
|
|
69
69
|
|
|
70
70
|
"""
|
|
71
|
-
|
|
72
|
-
if isinstance(response, Response):
|
|
73
|
-
return None
|
|
74
|
-
return response
|
|
71
|
+
return self.rest.get("forwardingRules")
|
|
75
72
|
|
|
76
73
|
def get_rule(self, rule_id: str) -> Box:
|
|
77
74
|
"""
|
|
@@ -158,18 +155,19 @@ class ForwardingControlAPI:
|
|
|
158
155
|
|
|
159
156
|
# Transform ID fields in kwargs
|
|
160
157
|
transform_common_id_fields(self.reformat_params, kwargs, payload)
|
|
158
|
+
|
|
161
159
|
for key, value in kwargs.items():
|
|
162
160
|
if value is not None:
|
|
163
|
-
|
|
161
|
+
if key == "state" and isinstance(value, bool):
|
|
162
|
+
payload[key] = "ENABLED" if value else "DISABLED"
|
|
163
|
+
else:
|
|
164
|
+
payload[key] = value
|
|
164
165
|
|
|
165
166
|
# Convert the entire payload's keys to camelCase before sending
|
|
166
167
|
camel_payload = recursive_snake_to_camel(payload)
|
|
167
|
-
for key, value in kwargs.items():
|
|
168
|
-
if value is not None:
|
|
169
|
-
camel_payload[snake_to_camel(key)] = value
|
|
170
168
|
|
|
171
169
|
# Send POST request to create the rule
|
|
172
|
-
response = self.rest.post("forwardingRules", json=
|
|
170
|
+
response = self.rest.post("forwardingRules", json=camel_payload)
|
|
173
171
|
if isinstance(response, Response):
|
|
174
172
|
# Handle error response
|
|
175
173
|
status_code = response.status_code
|
|
@@ -227,7 +225,6 @@ class ForwardingControlAPI:
|
|
|
227
225
|
... description="TT#1965232866")
|
|
228
226
|
|
|
229
227
|
"""
|
|
230
|
-
|
|
231
228
|
# Set payload to value of existing record and convert nested dict keys.
|
|
232
229
|
payload = convert_keys(self.get_rule(rule_id))
|
|
233
230
|
|
|
@@ -240,14 +237,14 @@ class ForwardingControlAPI:
|
|
|
240
237
|
|
|
241
238
|
# Add remaining optional parameters to payload
|
|
242
239
|
for key, value in kwargs.items():
|
|
243
|
-
|
|
240
|
+
if key == "state" and isinstance(value, bool):
|
|
241
|
+
payload[snake_to_camel(key)] = "ENABLED" if value else "DISABLED"
|
|
242
|
+
else:
|
|
243
|
+
payload[snake_to_camel(key)] = value
|
|
244
244
|
|
|
245
245
|
response = self.rest.put(f"forwardingRules/{rule_id}", json=payload)
|
|
246
246
|
if isinstance(response, Response) and not response.ok:
|
|
247
|
-
# Handle error response
|
|
248
247
|
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
249
|
-
|
|
250
|
-
# Return the updated object
|
|
251
248
|
return self.get_rule(rule_id)
|
|
252
249
|
|
|
253
250
|
def delete_rule(self, rule_id: str) -> int:
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
from box import Box, BoxList
|
|
18
18
|
from requests import Response
|
|
19
19
|
|
|
20
|
-
from zscaler.utils import convert_keys, snake_to_camel
|
|
20
|
+
from zscaler.utils import convert_keys, snake_to_camel, transform_common_id_fields
|
|
21
21
|
from zscaler.zia import ZIAClient
|
|
22
22
|
|
|
23
23
|
|
|
@@ -68,7 +68,6 @@ class ZPAGatewayAPI:
|
|
|
68
68
|
self,
|
|
69
69
|
name: str,
|
|
70
70
|
zpa_server_group: dict = None,
|
|
71
|
-
zpa_app_segments: list = None,
|
|
72
71
|
**kwargs,
|
|
73
72
|
) -> Box:
|
|
74
73
|
"""
|
|
@@ -95,24 +94,21 @@ class ZPAGatewayAPI:
|
|
|
95
94
|
"""
|
|
96
95
|
payload = {"name": name, "type": "ZPA"}
|
|
97
96
|
|
|
97
|
+
# Add zpa_server_group to kwargs
|
|
98
98
|
if zpa_server_group:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
kwargs["zpa_server_group"] = zpa_server_group
|
|
100
|
+
|
|
101
|
+
# Define the id groups specific to this function
|
|
102
|
+
zpa_gateway_id_groups = [
|
|
103
|
+
("zpa_server_group", "zpaServerGroup"),
|
|
104
|
+
]
|
|
105
|
+
transform_common_id_fields(zpa_gateway_id_groups, kwargs, payload)
|
|
103
106
|
|
|
104
|
-
if zpa_app_segments:
|
|
105
|
-
payload["zpaAppSegments"] = [
|
|
106
|
-
{"externalId": segment.get("external_id"), "name": segment.get("name")} for segment in zpa_app_segments
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
# Add other optional parameters to payload
|
|
110
107
|
for key, value in kwargs.items():
|
|
111
108
|
payload[snake_to_camel(key)] = value
|
|
112
109
|
|
|
113
110
|
response = self.rest.post("zpaGateways", json=payload)
|
|
114
111
|
if isinstance(response, Response):
|
|
115
|
-
# Handle error response
|
|
116
112
|
status_code = response.status_code
|
|
117
113
|
raise Exception(f"API call failed with status {status_code}: {response.json()}")
|
|
118
114
|
return response
|
|
@@ -143,25 +139,24 @@ class ZPAGatewayAPI:
|
|
|
143
139
|
"""
|
|
144
140
|
payload = convert_keys(self.get_gateway(gateway_id))
|
|
145
141
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
value = [{snake_to_camel(k): v for k, v in item.items()} for item in value]
|
|
142
|
+
if "zpa_server_group" in kwargs:
|
|
143
|
+
kwargs["zpa_server_group"] = kwargs["zpa_server_group"]
|
|
144
|
+
|
|
145
|
+
# Define the id groups specific to this function
|
|
146
|
+
zpa_gateway_id_groups = [
|
|
147
|
+
("zpa_server_group", "zpaServerGroup"),
|
|
148
|
+
]
|
|
154
149
|
|
|
150
|
+
transform_common_id_fields(zpa_gateway_id_groups, kwargs, payload)
|
|
151
|
+
|
|
152
|
+
for key, value in kwargs.items():
|
|
155
153
|
payload[snake_to_camel(key)] = value
|
|
156
154
|
|
|
157
155
|
response = self.rest.put(f"zpaGateways/{gateway_id}", json=payload)
|
|
158
156
|
if isinstance(response, Response) and not response.ok:
|
|
159
|
-
|
|
160
|
-
raise Exception(f"API call failed with status {response.status_code}: " f"{response.json()}")
|
|
161
|
-
|
|
162
|
-
# Return the updated object
|
|
157
|
+
raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
|
|
163
158
|
return self.get_gateway(gateway_id)
|
|
164
|
-
|
|
159
|
+
|
|
165
160
|
def delete_gateway(self, gateway_id):
|
|
166
161
|
"""
|
|
167
162
|
Deletes the specified ZPA Gateway.
|
|
@@ -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,6 +40,7 @@ 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
45
|
from zscaler.zpa.posture_profiles import PostureProfilesAPI
|
|
44
46
|
from zscaler.zpa.privileged_remote_access import PrivilegedRemoteAccessAPI
|
|
@@ -81,12 +83,12 @@ class ZPAClientHelper(ZPAClient):
|
|
|
81
83
|
client_secret,
|
|
82
84
|
customer_id,
|
|
83
85
|
cloud,
|
|
86
|
+
microtenant_id=None,
|
|
84
87
|
timeout=240,
|
|
85
88
|
cache=None,
|
|
86
89
|
fail_safe=False,
|
|
87
90
|
):
|
|
88
91
|
# Initialize rate limiter
|
|
89
|
-
# You may want to adjust these parameters as per your rate limit configuration
|
|
90
92
|
self.rate_limiter = RateLimiter(
|
|
91
93
|
get_limit=20, # Adjusted to allow 20 GET requests per 10 seconds
|
|
92
94
|
post_put_delete_limit=10, # Adjusted to allow 10 POST/PUT/DELETE requests per 10 seconds
|
|
@@ -94,7 +96,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
94
96
|
post_put_delete_freq=10, # Adjust frequency to 10 seconds
|
|
95
97
|
)
|
|
96
98
|
|
|
97
|
-
# Validate cloud value
|
|
98
99
|
if cloud not in ZPA_BASE_URLS:
|
|
99
100
|
valid_clouds = ", ".join(ZPA_BASE_URLS.keys())
|
|
100
101
|
raise ValueError(
|
|
@@ -102,15 +103,13 @@ class ZPAClientHelper(ZPAClient):
|
|
|
102
103
|
f"Please use one of the following supported values: {valid_clouds}"
|
|
103
104
|
)
|
|
104
105
|
|
|
105
|
-
# Continue with existing initialization...
|
|
106
|
-
# Select the appropriate URL
|
|
107
106
|
self.baseurl = ZPA_BASE_URLS.get(cloud, ZPA_BASE_URLS["PRODUCTION"])
|
|
108
|
-
|
|
109
107
|
self.timeout = timeout
|
|
110
108
|
self.client_id = client_id
|
|
111
109
|
self.client_secret = client_secret
|
|
112
110
|
self.customer_id = customer_id
|
|
113
111
|
self.cloud = cloud
|
|
112
|
+
self.microtenant_id = microtenant_id or os.getenv("ZPA_MICROTENANT_ID")
|
|
114
113
|
self.url = f"{self.baseurl}/mgmtconfig/v1/admin/customers/{customer_id}"
|
|
115
114
|
self.user_config_url = f"{self.baseurl}/userconfig/v1/customers/{customer_id}"
|
|
116
115
|
self.v2_url = f"{self.baseurl}/mgmtconfig/v2/admin/customers/{customer_id}"
|
|
@@ -118,7 +117,6 @@ class ZPAClientHelper(ZPAClient):
|
|
|
118
117
|
self.cbi_url = f"{self.baseurl}/cbiconfig/cbi/api/customers/{customer_id}"
|
|
119
118
|
self.fail_safe = fail_safe
|
|
120
119
|
|
|
121
|
-
# Cache setup
|
|
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,7 +128,6 @@ 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()
|
|
136
133
|
self.access_token = None
|
|
@@ -184,20 +181,34 @@ class ZPAClientHelper(ZPAClient):
|
|
|
184
181
|
elif api_version == "cbiconfig_v1":
|
|
185
182
|
api = self.cbi_url
|
|
186
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
|
+
|
|
187
195
|
url = f"{api}/{path.lstrip('/')}"
|
|
196
|
+
if params:
|
|
197
|
+
url = f"{url}?{urllib.parse.urlencode(params)}"
|
|
198
|
+
|
|
188
199
|
start_time = time.time()
|
|
189
200
|
headers_with_user_agent = self.headers.copy()
|
|
190
201
|
headers_with_user_agent["User-Agent"] = self.user_agent
|
|
191
202
|
request_uuid = uuid.uuid4()
|
|
192
|
-
dump_request(logger, url, method, json,
|
|
193
|
-
cache_key = self.cache.create_key(url,
|
|
203
|
+
dump_request(logger, url, method, json, None, headers_with_user_agent, request_uuid)
|
|
204
|
+
cache_key = self.cache.create_key(url, None)
|
|
194
205
|
if method == "GET" and self.cache.contains(cache_key):
|
|
195
206
|
resp = self.cache.get(cache_key)
|
|
196
207
|
dump_response(
|
|
197
208
|
logger=logger,
|
|
198
209
|
url=url,
|
|
199
210
|
method=method,
|
|
200
|
-
params=
|
|
211
|
+
params=None,
|
|
201
212
|
resp=resp,
|
|
202
213
|
request_uuid=request_uuid,
|
|
203
214
|
start_time=start_time,
|
|
@@ -217,13 +228,14 @@ class ZPAClientHelper(ZPAClient):
|
|
|
217
228
|
method,
|
|
218
229
|
url,
|
|
219
230
|
json=json,
|
|
231
|
+
params=None,
|
|
220
232
|
headers=headers_with_user_agent,
|
|
221
233
|
timeout=self.timeout,
|
|
222
234
|
)
|
|
223
235
|
dump_response(
|
|
224
236
|
logger=logger,
|
|
225
237
|
url=url,
|
|
226
|
-
params=
|
|
238
|
+
params=None,
|
|
227
239
|
method=method,
|
|
228
240
|
resp=resp,
|
|
229
241
|
request_uuid=request_uuid,
|
|
@@ -366,6 +378,7 @@ class ZPAClientHelper(ZPAClient):
|
|
|
366
378
|
search_field="name",
|
|
367
379
|
max_pages=None,
|
|
368
380
|
max_items=None,
|
|
381
|
+
all_entries=False, # Return all SCIM groups including the deleted ones if set to true
|
|
369
382
|
sort_order=None,
|
|
370
383
|
sort_by=None,
|
|
371
384
|
sort_dir=None,
|
|
@@ -373,8 +386,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
373
386
|
end_time=None,
|
|
374
387
|
idp_group_id=None,
|
|
375
388
|
scim_user_id=None,
|
|
389
|
+
scim_username=None,
|
|
376
390
|
page=None,
|
|
377
|
-
pagesize=
|
|
391
|
+
pagesize=None,
|
|
392
|
+
microtenant_id=None,
|
|
378
393
|
):
|
|
379
394
|
"""
|
|
380
395
|
Fetches paginated data from the ZPA API based on specified parameters and handles various types of API pagination.
|
|
@@ -417,15 +432,19 @@ class ZPAClientHelper(ZPAClient):
|
|
|
417
432
|
if params is None:
|
|
418
433
|
params = {}
|
|
419
434
|
|
|
420
|
-
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):
|
|
421
436
|
raise ValueError(
|
|
422
437
|
"Do not mix 'page' or 'pagesize' with 'max_pages' or 'max_items'. Choose either set of parameters."
|
|
423
438
|
)
|
|
424
439
|
|
|
425
|
-
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
|
|
426
442
|
|
|
427
|
-
|
|
428
|
-
|
|
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
|
|
429
448
|
|
|
430
449
|
if search:
|
|
431
450
|
api_search_field = snake_to_camel(search_field)
|
|
@@ -443,6 +462,10 @@ class ZPAClientHelper(ZPAClient):
|
|
|
443
462
|
params["idpGroupId"] = idp_group_id
|
|
444
463
|
if scim_user_id:
|
|
445
464
|
params["scimUserId"] = scim_user_id
|
|
465
|
+
if scim_username:
|
|
466
|
+
params["scimUserName"] = scim_username
|
|
467
|
+
if all_entries:
|
|
468
|
+
params["allEntries"] = all_entries
|
|
446
469
|
|
|
447
470
|
total_collected = 0
|
|
448
471
|
ret_data = []
|
|
@@ -456,17 +479,18 @@ class ZPAClientHelper(ZPAClient):
|
|
|
456
479
|
if should_wait:
|
|
457
480
|
time.sleep(delay)
|
|
458
481
|
|
|
459
|
-
|
|
460
|
-
response = self.send("GET", url, api_version=api_version)
|
|
482
|
+
response = self.send("GET", path=path, params=params, api_version=api_version)
|
|
461
483
|
|
|
462
484
|
if response.status_code != expected_status_code:
|
|
463
|
-
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
|
+
)
|
|
464
488
|
logger.error(error_msg)
|
|
465
489
|
return BoxList([]), error_msg
|
|
466
490
|
|
|
467
491
|
response_data = response.json()
|
|
468
492
|
data = response_data.get("list", [])
|
|
469
|
-
if not data and (page
|
|
493
|
+
if not data and (params["page"] == 1):
|
|
470
494
|
error_msg = ERROR_MESSAGES["EMPTY_RESULTS"]
|
|
471
495
|
logger.warn(error_msg)
|
|
472
496
|
return BoxList([]), error_msg
|
|
@@ -478,12 +502,11 @@ class ZPAClientHelper(ZPAClient):
|
|
|
478
502
|
if max_items is not None and total_collected >= max_items:
|
|
479
503
|
break
|
|
480
504
|
|
|
481
|
-
|
|
482
|
-
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):
|
|
483
507
|
break
|
|
484
508
|
|
|
485
|
-
page =
|
|
486
|
-
params["page"] = page
|
|
509
|
+
params["page"] = next_page if params["page"] is None else params["page"] + 1
|
|
487
510
|
|
|
488
511
|
finally:
|
|
489
512
|
time.sleep(2) # Ensure a delay between requests regardless of outcome
|
|
@@ -495,6 +518,14 @@ class ZPAClientHelper(ZPAClient):
|
|
|
495
518
|
|
|
496
519
|
return BoxList(ret_data), None
|
|
497
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
|
+
|
|
498
529
|
@property
|
|
499
530
|
def app_segments(self):
|
|
500
531
|
"""
|
|
@@ -591,6 +622,14 @@ class ZPAClientHelper(ZPAClient):
|
|
|
591
622
|
"""
|
|
592
623
|
return MachineGroupsAPI(self)
|
|
593
624
|
|
|
625
|
+
@property
|
|
626
|
+
def microtenants(self):
|
|
627
|
+
"""
|
|
628
|
+
The interface object for the :ref:`ZPA Microtenants interface <zpa-microtenants>`.
|
|
629
|
+
|
|
630
|
+
"""
|
|
631
|
+
return MicrotenantsAPI(self)
|
|
632
|
+
|
|
594
633
|
@property
|
|
595
634
|
def policies(self):
|
|
596
635
|
"""
|