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
|
@@ -0,0 +1,862 @@
|
|
|
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 typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
from box import Box, BoxList
|
|
21
|
+
from requests import Response
|
|
22
|
+
|
|
23
|
+
from zscaler.utils import is_valid_ssh_key, snake_to_camel, validate_and_convert_times
|
|
24
|
+
from zscaler.zpa.client import ZPAClient
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PrivilegedRemoteAccessAPI:
|
|
28
|
+
def __init__(self, client: ZPAClient):
|
|
29
|
+
self.rest = client
|
|
30
|
+
|
|
31
|
+
def list_portals(self, **kwargs) -> BoxList:
|
|
32
|
+
"""
|
|
33
|
+
Returns a list of all privileged remote access portals.
|
|
34
|
+
|
|
35
|
+
Keyword Args:
|
|
36
|
+
**max_items (int):
|
|
37
|
+
The maximum number of items to request before stopping iteration.
|
|
38
|
+
**max_pages (int):
|
|
39
|
+
The maximum number of pages to request before stopping iteration.
|
|
40
|
+
**pagesize (int):
|
|
41
|
+
Specifies the page size. The default size is 20, but the maximum size is 500.
|
|
42
|
+
**search (str, optional):
|
|
43
|
+
The search string used to match against features and fields.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
:obj:`BoxList`: A list of all configured privileged remote access portals.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
>>> for pra_portal in zpa.privilegedremoteaccess.list_portals():
|
|
50
|
+
... pprint(pra_portal)
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
list, _ = self.rest.get_paginated_data(
|
|
54
|
+
path="/praPortal", **kwargs, api_version="v1"
|
|
55
|
+
)
|
|
56
|
+
return list
|
|
57
|
+
|
|
58
|
+
def get_portal(self, portal_id: str) -> Box:
|
|
59
|
+
"""
|
|
60
|
+
Returns information on the specified pra portal.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
portal_id (str):
|
|
64
|
+
The unique identifier for the pra portal.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
:obj:`Box`: The resource record for the pra portal.
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
>>> pprint(zpa.privilegedremoteaccess.get_portal('99999'))
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
return self.rest.get(f"praPortal/{portal_id}")
|
|
74
|
+
|
|
75
|
+
def add_portal(
|
|
76
|
+
self,
|
|
77
|
+
name: str,
|
|
78
|
+
certificate_id: str,
|
|
79
|
+
domain: str,
|
|
80
|
+
enabled: bool = True,
|
|
81
|
+
user_notification_enabled: bool = True,
|
|
82
|
+
**kwargs,
|
|
83
|
+
) -> Box:
|
|
84
|
+
"""
|
|
85
|
+
Add a privileged remote access portal.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
name (str):
|
|
89
|
+
The name of the privileged portal.
|
|
90
|
+
enabled (bool):
|
|
91
|
+
Whether or not the privileged portal is enabled. Default is True.
|
|
92
|
+
certificate_id (bool):
|
|
93
|
+
The unique identifier of the certificate.
|
|
94
|
+
domain (str):
|
|
95
|
+
The domain of the privileged portal.
|
|
96
|
+
**kwargs:
|
|
97
|
+
Optional keyword args.
|
|
98
|
+
|
|
99
|
+
Keyword Args:
|
|
100
|
+
description (str):
|
|
101
|
+
The description of the privileged portal.
|
|
102
|
+
user_notification (str):
|
|
103
|
+
The notification message displayed in the banner of the privileged portallink, if enabled.
|
|
104
|
+
user_notification_enabled (bool):
|
|
105
|
+
Indicates if the Notification Banner is enabled (true) or disabled (false)
|
|
106
|
+
Returns:
|
|
107
|
+
:obj:`Box`: The resource record for the newly created portal.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
Create a pra portal with the minimum required parameters:
|
|
111
|
+
|
|
112
|
+
>>> zpa.privilegedremoteaccess.add_portal(
|
|
113
|
+
... name='PRA Portal Example',
|
|
114
|
+
... certificate_id='123456789',
|
|
115
|
+
... user_notification_enabled=True)
|
|
116
|
+
"""
|
|
117
|
+
payload = {
|
|
118
|
+
"name": name,
|
|
119
|
+
"enabled": enabled,
|
|
120
|
+
"domain": domain,
|
|
121
|
+
"userNotificationEnabled": user_notification_enabled,
|
|
122
|
+
"certificateId": certificate_id,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Add optional parameters to payload
|
|
126
|
+
for key, value in kwargs.items():
|
|
127
|
+
payload[snake_to_camel(key)] = value
|
|
128
|
+
|
|
129
|
+
response = self.rest.post("/praPortal", json=payload)
|
|
130
|
+
if isinstance(response, Response):
|
|
131
|
+
status_code = response.status_code
|
|
132
|
+
if status_code > 299:
|
|
133
|
+
return None
|
|
134
|
+
return self.get_portal(response.get("id"))
|
|
135
|
+
|
|
136
|
+
def update_portal(self, portal_id: str, **kwargs) -> Box:
|
|
137
|
+
"""
|
|
138
|
+
Updates the specified pra portal.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
portal_id (str):
|
|
142
|
+
The unique identifier for the portal being updated.
|
|
143
|
+
**kwargs:
|
|
144
|
+
Optional keyword args.
|
|
145
|
+
|
|
146
|
+
Keyword Args:
|
|
147
|
+
name (str):
|
|
148
|
+
The name of the privileged portal.
|
|
149
|
+
description (str):
|
|
150
|
+
The description of the privileged portal.
|
|
151
|
+
enabled (bool):
|
|
152
|
+
Whether or not the privileged portal is enabled. Default is True
|
|
153
|
+
certificate_id (bool):
|
|
154
|
+
Whether or not The unique identifier of the certificate.
|
|
155
|
+
domain (str):
|
|
156
|
+
The domain of the privileged portal.
|
|
157
|
+
user_notification (str):
|
|
158
|
+
The notification message displayed in the banner of the privileged portallink, if enabled.
|
|
159
|
+
user_notification_enabled (bool):
|
|
160
|
+
Indicates if the Notification Banner is enabled (true) or disabled (false)
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
:obj:`Box`: The resource record for the updated portal.
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
Update the name of a portal:
|
|
167
|
+
|
|
168
|
+
>>> zpa.privilegedremoteaccess.update_portal(
|
|
169
|
+
... '99999',
|
|
170
|
+
... name='Updated PRA Portal')
|
|
171
|
+
|
|
172
|
+
Update the pra portal:
|
|
173
|
+
|
|
174
|
+
>>> zpa.privilegedremoteaccess.update_portal(
|
|
175
|
+
... '99999',
|
|
176
|
+
... name='Updated PRA Portal')
|
|
177
|
+
|
|
178
|
+
"""
|
|
179
|
+
# Set payload to value of existing record
|
|
180
|
+
payload = {snake_to_camel(k): v for k, v in self.get_portal(portal_id).items()}
|
|
181
|
+
|
|
182
|
+
# Add optional parameters to payload
|
|
183
|
+
for key, value in kwargs.items():
|
|
184
|
+
payload[snake_to_camel(key)] = value
|
|
185
|
+
|
|
186
|
+
resp = self.rest.put(f"praPortal/{portal_id}", json=payload).status_code
|
|
187
|
+
|
|
188
|
+
if resp == 204:
|
|
189
|
+
return self.get_portal(portal_id)
|
|
190
|
+
|
|
191
|
+
def delete_portal(self, portal_id: str) -> int:
|
|
192
|
+
"""
|
|
193
|
+
Delete the specified pra portal.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
portal_id (str): The unique identifier for the portal to be deleted.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
:obj:`int`: The response code for the operation.
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
>>> zpa.privilegedremoteaccess.delete_portal('99999')
|
|
203
|
+
|
|
204
|
+
"""
|
|
205
|
+
return self.rest.delete(f"praPortal/{portal_id}").status_code
|
|
206
|
+
|
|
207
|
+
def list_consoles(self, **kwargs) -> BoxList:
|
|
208
|
+
"""
|
|
209
|
+
Returns a list of all privileged remote access consoles.
|
|
210
|
+
|
|
211
|
+
Keyword Args:
|
|
212
|
+
**max_items (int):
|
|
213
|
+
The maximum number of items to request before stopping iteration.
|
|
214
|
+
**max_pages (int):
|
|
215
|
+
The maximum number of pages to request before stopping iteration.
|
|
216
|
+
**pagesize (int):
|
|
217
|
+
Specifies the page size. The default size is 20, but the maximum size is 500.
|
|
218
|
+
**search (str, optional):
|
|
219
|
+
The search string used to match against features and fields.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
:obj:`BoxList`: A list of all configured privileged remote access consoles.
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
>>> for pra_console in zpa.privilegedremoteaccess.list_consoles():
|
|
226
|
+
... pprint(pra_console)
|
|
227
|
+
|
|
228
|
+
"""
|
|
229
|
+
list, _ = self.rest.get_paginated_data(
|
|
230
|
+
path="/praConsole", **kwargs, api_version="v1"
|
|
231
|
+
)
|
|
232
|
+
return list
|
|
233
|
+
|
|
234
|
+
def get_console(self, console_id: str) -> Box:
|
|
235
|
+
"""
|
|
236
|
+
Returns information on the specified pra console.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
console_id (str):
|
|
240
|
+
The unique identifier for the pra console.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
:obj:`Box`: The resource record for the pra console.
|
|
244
|
+
|
|
245
|
+
Examples:
|
|
246
|
+
>>> pprint(zpa.privilegedremoteaccess.get_console('99999'))
|
|
247
|
+
|
|
248
|
+
"""
|
|
249
|
+
return self.rest.get(f"praConsole/{console_id}")
|
|
250
|
+
|
|
251
|
+
def get_console_portal(self, portal_id: str) -> Box:
|
|
252
|
+
"""
|
|
253
|
+
Returns information on the specified pra console of the privileged portal.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
portal_id (str):
|
|
257
|
+
The unique identifier of the privileged portal.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
:obj:`Box`: The resource record for the privileged portal.
|
|
261
|
+
|
|
262
|
+
Examples:
|
|
263
|
+
>>> pprint(zpa.privilegedremoteaccess.get_console_portal('99999'))
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
return self.rest.get(f"praConsole/praPortal/{portal_id}")
|
|
267
|
+
|
|
268
|
+
def add_console(
|
|
269
|
+
self,
|
|
270
|
+
name: str,
|
|
271
|
+
pra_application_id: str,
|
|
272
|
+
pra_portal_ids: list,
|
|
273
|
+
enabled: bool = True,
|
|
274
|
+
**kwargs,
|
|
275
|
+
) -> Box:
|
|
276
|
+
"""
|
|
277
|
+
Adds a new Privileged Remote Access (PRA) console.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
name (str): The name of the PRA console.
|
|
281
|
+
pra_application_id (str): The unique identifier of the associated PRA application.
|
|
282
|
+
pra_portal_ids (list of str): A list of unique identifiers for the associated PRA portals.
|
|
283
|
+
enabled (bool, optional): Indicates whether the console is enabled. Defaults to True.
|
|
284
|
+
|
|
285
|
+
Keyword Args:
|
|
286
|
+
description (str, optional): A description for the PRA console.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Box: A Box object containing the details of the newly created console.
|
|
290
|
+
|
|
291
|
+
Examples:
|
|
292
|
+
>>> zpa.privilegedremoteaccess.add_console(
|
|
293
|
+
... name='PRA Console Example',
|
|
294
|
+
... pra_application_id='999999999',
|
|
295
|
+
... pra_portal_ids=['999999999'],
|
|
296
|
+
... description='PRA Console Description',
|
|
297
|
+
... enabled=True
|
|
298
|
+
... )
|
|
299
|
+
"""
|
|
300
|
+
payload = {
|
|
301
|
+
"name": name,
|
|
302
|
+
"enabled": enabled,
|
|
303
|
+
"praApplication": {"id": pra_application_id},
|
|
304
|
+
"praPortals": [{"id": portal_id} for portal_id in pra_portal_ids],
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# Add optional parameters to payload
|
|
308
|
+
for key, value in kwargs.items():
|
|
309
|
+
payload[snake_to_camel(key)] = value
|
|
310
|
+
|
|
311
|
+
response = self.rest.post("praConsole", json=payload)
|
|
312
|
+
if isinstance(response, Response):
|
|
313
|
+
# this is only true when the creation failed (status code is not 2xx)
|
|
314
|
+
status_code = response.status_code
|
|
315
|
+
# Handle error response
|
|
316
|
+
raise Exception(
|
|
317
|
+
f"API call failed with status {status_code}: {response.json()}"
|
|
318
|
+
)
|
|
319
|
+
return response
|
|
320
|
+
|
|
321
|
+
def update_console(
|
|
322
|
+
self,
|
|
323
|
+
console_id: str,
|
|
324
|
+
pra_application_id: str = None,
|
|
325
|
+
pra_portal_ids: list = None,
|
|
326
|
+
**kwargs,
|
|
327
|
+
) -> Box:
|
|
328
|
+
"""
|
|
329
|
+
Updates the specified PRA console. All the attributes are required by the API.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
console_id (str): The unique identifier of the console being updated.
|
|
333
|
+
|
|
334
|
+
Keyword Args:
|
|
335
|
+
name (str): The new name of the PRA console.
|
|
336
|
+
description (str): The new description of the PRA console.
|
|
337
|
+
enabled (bool): Indicates whether the console should be enabled.
|
|
338
|
+
pra_application_id (str): The unique identifier of the associated PRA application to be linked with the console.
|
|
339
|
+
pra_portal_ids (list of str): A list of unique identifiers for the associated PRA portals to be linked with the console.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Box: A Box object containing the details of the updated console.
|
|
343
|
+
|
|
344
|
+
Examples:
|
|
345
|
+
>>> zpa.privilegedremoteaccess.update_console(
|
|
346
|
+
... console_id='99999',
|
|
347
|
+
... name='Updated PRA Console',
|
|
348
|
+
... description='Updated Description',
|
|
349
|
+
... enabled=True,
|
|
350
|
+
... pra_application_id='999999999',
|
|
351
|
+
... pra_portal_ids=['999999999']
|
|
352
|
+
... )
|
|
353
|
+
"""
|
|
354
|
+
# Fetch existing console details first if necessary
|
|
355
|
+
existing_console = self.get_console(console_id)
|
|
356
|
+
|
|
357
|
+
# Set payload to value of existing record if needed
|
|
358
|
+
payload = {snake_to_camel(k): v for k, v in existing_console.items()}
|
|
359
|
+
|
|
360
|
+
if pra_application_id:
|
|
361
|
+
payload["praApplication"] = {"id": pra_application_id}
|
|
362
|
+
if pra_portal_ids:
|
|
363
|
+
payload["praPortals"] = [{"id": id} for id in pra_portal_ids]
|
|
364
|
+
|
|
365
|
+
# Add/Update optional parameters in payload
|
|
366
|
+
for key, value in kwargs.items():
|
|
367
|
+
payload[snake_to_camel(key)] = value
|
|
368
|
+
|
|
369
|
+
resp = self.rest.put(f"praConsole/{console_id}", json=payload).status_code
|
|
370
|
+
if not isinstance(resp, Response):
|
|
371
|
+
return self.get_console(console_id)
|
|
372
|
+
|
|
373
|
+
def delete_console(self, console_id: str) -> int:
|
|
374
|
+
"""
|
|
375
|
+
Delete the specified pra console.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
console_id (str): The unique identifier for the console to be deleted.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
:obj:`int`: The response code for the operation.
|
|
382
|
+
|
|
383
|
+
Examples:
|
|
384
|
+
>>> zpa.privilegedremoteaccess.delete_console('99999')
|
|
385
|
+
|
|
386
|
+
"""
|
|
387
|
+
response = self.rest.delete(f"/praConsole/{console_id}")
|
|
388
|
+
if response.status_code != 204:
|
|
389
|
+
raise Exception(f"Failed to delete console: {response.text}")
|
|
390
|
+
return response.status_code
|
|
391
|
+
|
|
392
|
+
def add_bulk_console(self, consoles: List[Dict[str, Any]]) -> Box:
|
|
393
|
+
"""
|
|
394
|
+
Adds a list of Privileged Remote Access (PRA) consoles in bulk.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
consoles (List[Dict[str, Any]]): A list of dictionaries where each dictionary
|
|
398
|
+
contains details of a PRA console to be added. Required keys in each dictionary include
|
|
399
|
+
'name', 'pra_application_id', and 'pra_portal_ids'. Optionally, 'enabled' and 'description'
|
|
400
|
+
can also be included.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
Box: A Box object containing the details of the newly created consoles.
|
|
404
|
+
|
|
405
|
+
Examples:
|
|
406
|
+
>>> zpa.privilegedremoteaccess.add_bulk_console([
|
|
407
|
+
... {
|
|
408
|
+
... 'name': 'PRA Console Example 1',
|
|
409
|
+
... 'pra_application_id': '999999999',
|
|
410
|
+
... 'pra_portal_ids': ['999999998'],
|
|
411
|
+
... 'description': 'PRA Console Description 1',
|
|
412
|
+
... 'enabled': True
|
|
413
|
+
... },
|
|
414
|
+
... {
|
|
415
|
+
... 'name': 'PRA Console Example 2',
|
|
416
|
+
... 'pra_application_id': '999999999',
|
|
417
|
+
... 'pra_portal_ids': ['999999997'],
|
|
418
|
+
... 'description': 'PRA Console Description 2',
|
|
419
|
+
... 'enabled': True
|
|
420
|
+
... }
|
|
421
|
+
... ])
|
|
422
|
+
"""
|
|
423
|
+
# Transform the input list of console dictionaries to the expected JSON payload format
|
|
424
|
+
payload = [
|
|
425
|
+
{
|
|
426
|
+
"name": console.get("name"),
|
|
427
|
+
"description": console.get("description", ""),
|
|
428
|
+
"enabled": console.get("enabled", True),
|
|
429
|
+
"praApplication": {"id": console.get("pra_application_id")},
|
|
430
|
+
"praPortals": [{"id": id} for id in console.get("pra_portal_ids", [])],
|
|
431
|
+
}
|
|
432
|
+
for console in consoles
|
|
433
|
+
]
|
|
434
|
+
|
|
435
|
+
response = self.rest.post("praConsole/bulk", json=payload)
|
|
436
|
+
if isinstance(response, Response):
|
|
437
|
+
status_code = response.status_code
|
|
438
|
+
# Handle error response
|
|
439
|
+
raise Exception(
|
|
440
|
+
f"API call failed with status {status_code}: {response.json()}"
|
|
441
|
+
)
|
|
442
|
+
return response
|
|
443
|
+
|
|
444
|
+
def list_credentials(self, **kwargs) -> BoxList:
|
|
445
|
+
"""
|
|
446
|
+
Returns a list of all privileged remote access credentials.
|
|
447
|
+
|
|
448
|
+
Keyword Args:
|
|
449
|
+
**max_items (int):
|
|
450
|
+
The maximum number of items to request before stopping iteration.
|
|
451
|
+
**max_pages (int):
|
|
452
|
+
The maximum number of pages to request before stopping iteration.
|
|
453
|
+
**pagesize (int):
|
|
454
|
+
Specifies the page size. The default size is 20, but the maximum size is 500.
|
|
455
|
+
**search (str, optional):
|
|
456
|
+
The search string used to match against features and fields.
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
:obj:`BoxList`: A list of all configured privileged remote access credentials.
|
|
460
|
+
|
|
461
|
+
Examples:
|
|
462
|
+
>>> for pra_credential in zpa.privilegedremoteaccess.list_credentials():
|
|
463
|
+
... pprint(pra_credential)
|
|
464
|
+
|
|
465
|
+
"""
|
|
466
|
+
list, _ = self.rest.get_paginated_data(
|
|
467
|
+
path="/credential", **kwargs, api_version="v1"
|
|
468
|
+
)
|
|
469
|
+
return list
|
|
470
|
+
|
|
471
|
+
def get_credential(self, credential_id: str) -> Box:
|
|
472
|
+
"""
|
|
473
|
+
Returns information on the specified pra credential.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
credential_id (str):
|
|
477
|
+
The unique identifier for the pra credential.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
:obj:`Box`: The resource record for the pra credential.
|
|
481
|
+
|
|
482
|
+
Examples:
|
|
483
|
+
>>> pprint(zpa.privilegedremoteaccess.get_credential('99999'))
|
|
484
|
+
|
|
485
|
+
"""
|
|
486
|
+
return self.rest.get(f"credential/{credential_id}")
|
|
487
|
+
# response = self.rest.get("/credential/%s" % (credential_id))
|
|
488
|
+
# if isinstance(response, Response):
|
|
489
|
+
# status_code = response.status_code
|
|
490
|
+
# if status_code != 200:
|
|
491
|
+
# return None
|
|
492
|
+
# return response
|
|
493
|
+
|
|
494
|
+
def add_credential(
|
|
495
|
+
self,
|
|
496
|
+
name: str,
|
|
497
|
+
credential_type: str,
|
|
498
|
+
username: Optional[str] = None,
|
|
499
|
+
password: Optional[str] = None,
|
|
500
|
+
private_key: Optional[str] = None,
|
|
501
|
+
**kwargs,
|
|
502
|
+
) -> Box:
|
|
503
|
+
"""
|
|
504
|
+
Validates input based on credential_type and adds a new credential.
|
|
505
|
+
"""
|
|
506
|
+
payload = {"name": name, "credentialType": credential_type}
|
|
507
|
+
|
|
508
|
+
if credential_type == "USERNAME_PASSWORD":
|
|
509
|
+
if not username or not password:
|
|
510
|
+
raise ValueError(
|
|
511
|
+
"Username and password must be provided for USERNAME_PASSWORD type."
|
|
512
|
+
)
|
|
513
|
+
payload.update({"userName": username, "password": password})
|
|
514
|
+
|
|
515
|
+
elif credential_type == "SSH_KEY":
|
|
516
|
+
if not username or not private_key:
|
|
517
|
+
raise ValueError(
|
|
518
|
+
"Username and private_key must be provided for SSH_KEY type."
|
|
519
|
+
)
|
|
520
|
+
if not is_valid_ssh_key(private_key):
|
|
521
|
+
raise ValueError("Invalid SSH key format.")
|
|
522
|
+
payload.update({"userName": username, "privateKey": private_key})
|
|
523
|
+
|
|
524
|
+
elif credential_type == "PASSWORD":
|
|
525
|
+
if not password:
|
|
526
|
+
raise ValueError("Password must be provided for PASSWORD type.")
|
|
527
|
+
payload["password"] = password
|
|
528
|
+
|
|
529
|
+
else:
|
|
530
|
+
raise ValueError(f"Unsupported credential_type: {credential_type}")
|
|
531
|
+
|
|
532
|
+
# Add optional parameters to payload
|
|
533
|
+
for key, value in kwargs.items():
|
|
534
|
+
if key in ["description", "user_domain", "passphrase"]:
|
|
535
|
+
payload[snake_to_camel(key)] = value
|
|
536
|
+
|
|
537
|
+
response = self.rest.post("credential", json=payload)
|
|
538
|
+
if isinstance(response, Response):
|
|
539
|
+
# this is only true when the creation failed (status code is not 2xx)
|
|
540
|
+
status_code = response.status_code
|
|
541
|
+
# Handle error response
|
|
542
|
+
raise Exception(
|
|
543
|
+
f"API call failed with status {status_code}: {response.json()}"
|
|
544
|
+
)
|
|
545
|
+
return response
|
|
546
|
+
|
|
547
|
+
def update_credential(self, credential_id: str, **kwargs) -> Box:
|
|
548
|
+
"""
|
|
549
|
+
Updates a specified credential based on provided keyword arguments.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
credential_id (str): The unique identifier for the credential being updated.
|
|
553
|
+
|
|
554
|
+
Keyword Args:
|
|
555
|
+
All attributes of the credential that can be updated including but not limited to:
|
|
556
|
+
- username (str): Username for 'USERNAME_PASSWORD' and 'SSH_KEY' types.
|
|
557
|
+
- password (str): Password for 'USERNAME_PASSWORD' and 'PASSWORD' types.
|
|
558
|
+
- private_key (str): SSH private key for 'SSH_KEY' type.
|
|
559
|
+
- description (str): Description of the credential.
|
|
560
|
+
- user_domain (str): Domain associated with the username.
|
|
561
|
+
- passphrase (str): Passphrase for the SSH private key, applicable only for 'SSH_KEY'.
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
Box: The resource record for the updated credential.
|
|
565
|
+
|
|
566
|
+
Raises:
|
|
567
|
+
Exception: If fetching the credential fails or the required parameters are missing based on the credential type.
|
|
568
|
+
|
|
569
|
+
Examples:
|
|
570
|
+
Update a USERNAME_PASSWORD credential:
|
|
571
|
+
>>> zpa.privilegedremoteaccess.update_credential(
|
|
572
|
+
... credential_id='2223',
|
|
573
|
+
... username='jdoe',
|
|
574
|
+
... name='John Doe',
|
|
575
|
+
... credential_type='USERNAME_PASSWORD',
|
|
576
|
+
... password='**********',
|
|
577
|
+
... description='Updated credential description'
|
|
578
|
+
... )
|
|
579
|
+
"""
|
|
580
|
+
# Fetch the existing credential to determine the credential type
|
|
581
|
+
existing_credential = self.get_credential(credential_id)
|
|
582
|
+
if not existing_credential:
|
|
583
|
+
raise Exception(f"Failed to fetch credential {credential_id}")
|
|
584
|
+
|
|
585
|
+
# Validate and enforce required fields based on the credential type
|
|
586
|
+
credential_type = existing_credential.credential_type
|
|
587
|
+
required_fields = (
|
|
588
|
+
["username", "password"]
|
|
589
|
+
if credential_type in ["USERNAME_PASSWORD", "SSH_KEY"]
|
|
590
|
+
else ["password"]
|
|
591
|
+
)
|
|
592
|
+
missing_fields = [field for field in required_fields if field not in kwargs]
|
|
593
|
+
if missing_fields:
|
|
594
|
+
raise ValueError(
|
|
595
|
+
f"Missing required fields for '{credential_type}': {', '.join(missing_fields)}"
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
# Prepare the payload with the existing details and updates from kwargs
|
|
599
|
+
payload = {
|
|
600
|
+
**existing_credential.to_dict(),
|
|
601
|
+
**{snake_to_camel(key): value for key, value in kwargs.items()},
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
# Execute the update operation
|
|
605
|
+
response = self.rest.put(f"credential/{credential_id}", json=payload)
|
|
606
|
+
if not response.ok:
|
|
607
|
+
raise Exception(
|
|
608
|
+
f"Failed to update credential {credential_id}: {response.text}"
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
# Fetch and return the updated credential details
|
|
612
|
+
return self.get_credential(credential_id)
|
|
613
|
+
|
|
614
|
+
def delete_credential(self, credential_id: str) -> int:
|
|
615
|
+
"""
|
|
616
|
+
Delete the specified pra credential.
|
|
617
|
+
|
|
618
|
+
Args:
|
|
619
|
+
credential_id (str): The unique identifier for the credential to be deleted.
|
|
620
|
+
|
|
621
|
+
Returns:
|
|
622
|
+
:obj:`int`: The response code for the operation.
|
|
623
|
+
|
|
624
|
+
Examples:
|
|
625
|
+
>>> zpa.privilegedremoteaccess.delete_credential('99999')
|
|
626
|
+
|
|
627
|
+
"""
|
|
628
|
+
return self.rest.delete(f"credential/{credential_id}").status_code
|
|
629
|
+
# response = self.rest.delete(f"/credential/{credential_id}")
|
|
630
|
+
# if response.status_code != 204:
|
|
631
|
+
# raise Exception(f"Failed to delete credential: {response.text}")
|
|
632
|
+
# return response.status_code
|
|
633
|
+
|
|
634
|
+
def list_approval(self, **kwargs) -> BoxList:
|
|
635
|
+
"""
|
|
636
|
+
Returns a list of all privileged remote access approvals.
|
|
637
|
+
|
|
638
|
+
Keyword Args:
|
|
639
|
+
max_items (int):
|
|
640
|
+
The maximum number of items to request before stopping iteration.
|
|
641
|
+
max_pages (int):
|
|
642
|
+
The maximum number of pages to request before stopping iteration.
|
|
643
|
+
pagesize (int):
|
|
644
|
+
Specifies the page size. The default size is 20, but the maximum size is 500.
|
|
645
|
+
search (str, optional):
|
|
646
|
+
The search string used to match against features and fields.
|
|
647
|
+
search_field (str, optional):
|
|
648
|
+
The field to search against. Defaults to 'name'. Commonly used fields include 'name' and 'email_ids'.
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
:obj:`BoxList`: A list of all configured privileged remote access approvals.
|
|
652
|
+
|
|
653
|
+
Examples:
|
|
654
|
+
Search by default field 'name':
|
|
655
|
+
>>> for pra_approval in zpa.privilegedremoteaccess.list_approval(search='Example_Name'):
|
|
656
|
+
... pprint(pra_approval)
|
|
657
|
+
|
|
658
|
+
Search by 'email_ids':
|
|
659
|
+
>>> for pra_approval in zpa.privilegedremoteaccess.list_approval(search='jdoe@example.com', search_field='email_ids'):
|
|
660
|
+
... pprint(pra_approval)
|
|
661
|
+
|
|
662
|
+
Specify maximum items and use an explicit search field:
|
|
663
|
+
>>> approvals = zpa.privilegedremoteaccess.list_approval(search='Example_Name', search_field='name', max_items=10)
|
|
664
|
+
>>> for approval in approvals:
|
|
665
|
+
... pprint(approval)
|
|
666
|
+
"""
|
|
667
|
+
list, _ = self.rest.get_paginated_data(
|
|
668
|
+
path="/approval", **kwargs, api_version="v1"
|
|
669
|
+
)
|
|
670
|
+
return list
|
|
671
|
+
|
|
672
|
+
def get_approval(self, approval_id: str) -> Box:
|
|
673
|
+
"""
|
|
674
|
+
Returns information on the specified pra approval.
|
|
675
|
+
|
|
676
|
+
Args:
|
|
677
|
+
approval_id (str):
|
|
678
|
+
The unique identifier for the pra approval.
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
:obj:`Box`: The resource record for the pra approval.
|
|
682
|
+
|
|
683
|
+
Examples:
|
|
684
|
+
>>> pprint(zpa.privilegedremoteaccess.get_approval('99999'))
|
|
685
|
+
|
|
686
|
+
"""
|
|
687
|
+
return self.rest.get(f"approval/{approval_id}")
|
|
688
|
+
|
|
689
|
+
def add_approval(
|
|
690
|
+
self,
|
|
691
|
+
email_ids: list,
|
|
692
|
+
application_ids: list,
|
|
693
|
+
start_time: str,
|
|
694
|
+
end_time: str,
|
|
695
|
+
status: str,
|
|
696
|
+
working_hours: dict,
|
|
697
|
+
**kwargs,
|
|
698
|
+
) -> Box:
|
|
699
|
+
"""
|
|
700
|
+
Add a privileged remote access approval.
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
email_ids (list): The email addresses of the users that you are assigning the privileged approval to.
|
|
704
|
+
application_ids (list of str): A list of unique identifiers for the associated application segment ids.
|
|
705
|
+
start_time (str): The start timestamp in UNIX format for when the approval begins.
|
|
706
|
+
end_time (str): The end timestamp in UNIX format for when the approval ends.
|
|
707
|
+
status (str): The status of the privileged approval. Supported values are: INVALID, ACTIVE, FUTURE, EXPIRED.
|
|
708
|
+
working_hours (dict): A dictionary containing the details of working hours including cron expressions for start and end times, actual start and end times, days of the week, and time zone.
|
|
709
|
+
|
|
710
|
+
Keyword Args:
|
|
711
|
+
Any additional optional parameters that can be included in the payload.
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
Box: The resource record for the newly created approval.
|
|
715
|
+
|
|
716
|
+
Examples:
|
|
717
|
+
Create a PRA approval with the minimum required parameters and working hours:
|
|
718
|
+
|
|
719
|
+
>>> zpa.privilegedremoteaccess.add_approval(
|
|
720
|
+
... email_ids=['jdoe@example.com'],
|
|
721
|
+
... application_ids=['999999999'],
|
|
722
|
+
... start_time='1712856502',
|
|
723
|
+
... end_time='1714498102',
|
|
724
|
+
... status='ACTIVE',
|
|
725
|
+
... working_hours={
|
|
726
|
+
... "start_time_cron": "0 0 16 ? * SUN,MON,TUE,WED,THU,FRI,SAT",
|
|
727
|
+
... "end_time_cron": "0 0 0 ? * MON,TUE,WED,THU,FRI,SAT,SUN",
|
|
728
|
+
... "start_time": "09:00",
|
|
729
|
+
... "end_time": "17:00",
|
|
730
|
+
... "days": ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"],
|
|
731
|
+
... "time_zone": "America/Vancouver"
|
|
732
|
+
... }
|
|
733
|
+
... )
|
|
734
|
+
"""
|
|
735
|
+
start_epoch, end_epoch = validate_and_convert_times(
|
|
736
|
+
start_time, end_time, working_hours["time_zone"]
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
payload = {
|
|
740
|
+
"emailIds": email_ids,
|
|
741
|
+
"applications": [
|
|
742
|
+
{"id": application_id} for application_id in application_ids
|
|
743
|
+
],
|
|
744
|
+
"startTime": start_epoch,
|
|
745
|
+
"endTime": end_epoch,
|
|
746
|
+
"status": status,
|
|
747
|
+
"workingHours": {
|
|
748
|
+
"startTimeCron": working_hours["start_time_cron"],
|
|
749
|
+
"endTimeCron": working_hours["end_time_cron"],
|
|
750
|
+
"startTime": working_hours["start_time"],
|
|
751
|
+
"endTime": working_hours["end_time"],
|
|
752
|
+
"days": working_hours["days"],
|
|
753
|
+
"timeZone": working_hours["time_zone"],
|
|
754
|
+
},
|
|
755
|
+
}
|
|
756
|
+
# Incorporate optional parameters
|
|
757
|
+
for key, value in kwargs.items():
|
|
758
|
+
payload[snake_to_camel(key)] = value
|
|
759
|
+
|
|
760
|
+
response = self.rest.post("approval", json=payload)
|
|
761
|
+
if isinstance(response, Response):
|
|
762
|
+
# this is only true when the creation failed (status code is not 2xx)
|
|
763
|
+
status_code = response.status_code
|
|
764
|
+
# Handle error response
|
|
765
|
+
raise Exception(
|
|
766
|
+
f"API call failed with status {status_code}: {response.json()}"
|
|
767
|
+
)
|
|
768
|
+
return response
|
|
769
|
+
|
|
770
|
+
def update_approval(self, approval_id: str, **kwargs) -> Box:
|
|
771
|
+
"""
|
|
772
|
+
Updates a specified approval based on provided keyword arguments.
|
|
773
|
+
...
|
|
774
|
+
"""
|
|
775
|
+
# Fetch existing approval details to get the current state
|
|
776
|
+
existing_approval = self.get_approval(approval_id)
|
|
777
|
+
if not existing_approval:
|
|
778
|
+
raise Exception(f"Failed to fetch approval {approval_id}")
|
|
779
|
+
|
|
780
|
+
# Pre-process and validate start_time and end_time if provided
|
|
781
|
+
if "start_time" in kwargs and "end_time" in kwargs:
|
|
782
|
+
start_time = kwargs["start_time"]
|
|
783
|
+
end_time = kwargs["end_time"]
|
|
784
|
+
# Assuming working_hours contains the time zone
|
|
785
|
+
time_zone = kwargs.get("working_hours", {}).get(
|
|
786
|
+
"time_zone", existing_approval.working_hours.time_zone
|
|
787
|
+
)
|
|
788
|
+
start_epoch, end_epoch = validate_and_convert_times(
|
|
789
|
+
start_time, end_time, time_zone
|
|
790
|
+
)
|
|
791
|
+
kwargs["start_time"] = start_epoch
|
|
792
|
+
kwargs["end_time"] = end_epoch
|
|
793
|
+
|
|
794
|
+
# Construct payload dynamically based on existing details and updates from kwargs
|
|
795
|
+
payload = {
|
|
796
|
+
"emailIds": kwargs.get("email_ids", existing_approval.email_ids),
|
|
797
|
+
"applications": [
|
|
798
|
+
{"id": app_id}
|
|
799
|
+
for app_id in kwargs.get(
|
|
800
|
+
"application_ids",
|
|
801
|
+
[app["id"] for app in existing_approval.applications],
|
|
802
|
+
)
|
|
803
|
+
],
|
|
804
|
+
"status": kwargs.get("status", existing_approval.status),
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
# Special handling for working_hours to preserve existing details if not fully specified in kwargs
|
|
808
|
+
working_hours = kwargs.get("working_hours", {})
|
|
809
|
+
existing_wh = existing_approval.working_hours
|
|
810
|
+
payload["workingHours"] = {
|
|
811
|
+
"startTimeCron": working_hours.get(
|
|
812
|
+
"start_time_cron", existing_wh.start_time_cron
|
|
813
|
+
),
|
|
814
|
+
"endTimeCron": working_hours.get(
|
|
815
|
+
"end_time_cron", existing_wh.end_time_cron
|
|
816
|
+
),
|
|
817
|
+
"startTime": working_hours.get("start_time", existing_wh.start_time),
|
|
818
|
+
"endTime": working_hours.get("end_time", existing_wh.end_time),
|
|
819
|
+
"days": working_hours.get("days", existing_wh.days),
|
|
820
|
+
"timeZone": working_hours.get("time_zone", existing_wh.time_zone),
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
# Add any additional provided parameters to payload
|
|
824
|
+
for key, value in kwargs.items():
|
|
825
|
+
if key not in ["email_ids", "application_ids", "working_hours"]:
|
|
826
|
+
payload[snake_to_camel(key)] = value
|
|
827
|
+
|
|
828
|
+
# Execute the update operation
|
|
829
|
+
response = self.rest.put(f"approval/{approval_id}", json=payload)
|
|
830
|
+
if response.status_code == 204:
|
|
831
|
+
return self.get_approval(approval_id)
|
|
832
|
+
else:
|
|
833
|
+
raise Exception(f"Failed to update approval {approval_id}: {response.text}")
|
|
834
|
+
|
|
835
|
+
def delete_approval(self, approval_id: str) -> int:
|
|
836
|
+
"""
|
|
837
|
+
Delete the specified pra approval.
|
|
838
|
+
|
|
839
|
+
Args:
|
|
840
|
+
approval_id (str): The unique identifier for the approval to be deleted.
|
|
841
|
+
|
|
842
|
+
Returns:
|
|
843
|
+
:obj:`int`: The response code for the operation.
|
|
844
|
+
|
|
845
|
+
Examples:
|
|
846
|
+
>>> zpa.privilegedremoteaccess.delete_approval('99999')
|
|
847
|
+
|
|
848
|
+
"""
|
|
849
|
+
return self.rest.delete(f"approval/{approval_id}").status_code
|
|
850
|
+
|
|
851
|
+
def expired_approval(self) -> int:
|
|
852
|
+
"""
|
|
853
|
+
Deletes all expired privileged approvals.
|
|
854
|
+
|
|
855
|
+
Returns:
|
|
856
|
+
:obj:`int`: The response code for the operation.
|
|
857
|
+
|
|
858
|
+
Examples:
|
|
859
|
+
>>> zpa.privilegedremoteaccess.expired_approval('99999')
|
|
860
|
+
|
|
861
|
+
"""
|
|
862
|
+
return self.rest.delete("approval/expired").status_code
|