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.
Files changed (75) hide show
  1. zscaler/__init__.py +34 -0
  2. zscaler/cache/__init__.py +0 -0
  3. zscaler/cache/cache.py +105 -0
  4. zscaler/cache/no_op_cache.py +68 -0
  5. zscaler/cache/zscaler_cache.py +161 -0
  6. zscaler/constants.py +26 -0
  7. zscaler/errors/__init__.py +0 -0
  8. zscaler/errors/error.py +10 -0
  9. zscaler/errors/http_error.py +20 -0
  10. zscaler/errors/zscaler_api_error.py +24 -0
  11. zscaler/exceptions/__init__.py +1 -0
  12. zscaler/exceptions/exceptions.py +101 -0
  13. zscaler/logger.py +57 -0
  14. zscaler/ratelimiter/__init__.py +0 -0
  15. zscaler/ratelimiter/ratelimiter.py +39 -0
  16. zscaler/user_agent.py +23 -0
  17. zscaler/utils.py +577 -0
  18. zscaler/zia/__init__.py +657 -0
  19. zscaler/zia/activate.py +52 -0
  20. zscaler/zia/admin_and_role_management.py +344 -0
  21. zscaler/zia/apptotal.py +71 -0
  22. zscaler/zia/audit_logs.py +95 -0
  23. zscaler/zia/authentication_settings.py +98 -0
  24. zscaler/zia/client.py +88 -0
  25. zscaler/zia/cloud_apps.py +406 -0
  26. zscaler/zia/device_management.py +90 -0
  27. zscaler/zia/dlp.py +784 -0
  28. zscaler/zia/errors.py +37 -0
  29. zscaler/zia/firewall.py +1104 -0
  30. zscaler/zia/forwarding_control.py +271 -0
  31. zscaler/zia/isolation_profile.py +83 -0
  32. zscaler/zia/labels.py +180 -0
  33. zscaler/zia/locations.py +661 -0
  34. zscaler/zia/sandbox.py +180 -0
  35. zscaler/zia/security.py +236 -0
  36. zscaler/zia/ssl_inspection.py +175 -0
  37. zscaler/zia/traffic.py +853 -0
  38. zscaler/zia/url_categories.py +442 -0
  39. zscaler/zia/url_filtering.py +310 -0
  40. zscaler/zia/users.py +386 -0
  41. zscaler/zia/web_dlp.py +295 -0
  42. zscaler/zia/workload_groups.py +58 -0
  43. zscaler/zia/zpa_gateway.py +187 -0
  44. zscaler/zpa/__init__.py +683 -0
  45. zscaler/zpa/app_segments.py +331 -0
  46. zscaler/zpa/app_segments_inspection.py +311 -0
  47. zscaler/zpa/app_segments_pra.py +310 -0
  48. zscaler/zpa/certificates.py +234 -0
  49. zscaler/zpa/client.py +113 -0
  50. zscaler/zpa/cloud_connector_groups.py +75 -0
  51. zscaler/zpa/connectors.py +518 -0
  52. zscaler/zpa/emergency_access.py +178 -0
  53. zscaler/zpa/errors.py +37 -0
  54. zscaler/zpa/idp.py +83 -0
  55. zscaler/zpa/inspection.py +1012 -0
  56. zscaler/zpa/isolation_profile.py +85 -0
  57. zscaler/zpa/lss.py +568 -0
  58. zscaler/zpa/machine_groups.py +79 -0
  59. zscaler/zpa/policies.py +848 -0
  60. zscaler/zpa/posture_profiles.py +122 -0
  61. zscaler/zpa/privileged_remote_access.py +862 -0
  62. zscaler/zpa/provisioning.py +271 -0
  63. zscaler/zpa/saml_attributes.py +100 -0
  64. zscaler/zpa/scim_attributes.py +117 -0
  65. zscaler/zpa/scim_groups.py +146 -0
  66. zscaler/zpa/segment_groups.py +191 -0
  67. zscaler/zpa/server_groups.py +217 -0
  68. zscaler/zpa/servers.py +202 -0
  69. zscaler/zpa/service_edges.py +404 -0
  70. zscaler/zpa/trusted_networks.py +127 -0
  71. zscaler_sdk_python-1.0.0.dist-info/LICENSE.md +21 -0
  72. zscaler_sdk_python-1.0.0.dist-info/METADATA +59 -0
  73. zscaler_sdk_python-1.0.0.dist-info/RECORD +75 -0
  74. zscaler_sdk_python-1.0.0.dist-info/WHEEL +6 -0
  75. zscaler_sdk_python-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1012 @@
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 box import Box, BoxList
19
+ from requests import Response
20
+ from requests.utils import quote
21
+
22
+ from zscaler.utils import convert_keys, snake_to_camel
23
+ from zscaler.zpa.client import ZPAClient
24
+
25
+
26
+ class InspectionControllerAPI:
27
+ def __init__(self, client: ZPAClient):
28
+ self.rest = client
29
+
30
+ @staticmethod
31
+ def _create_rule(rule: dict) -> dict:
32
+ rule_set = {
33
+ "type": rule["type"],
34
+ "conditions": [],
35
+ }
36
+ if "names" in rule:
37
+ rule_set["names"] = rule["names"]
38
+ for condition in rule["conditions"]:
39
+ rule_set["conditions"].append(
40
+ {
41
+ "lhs": condition["lhs"],
42
+ "op": condition["op"],
43
+ "rhs": condition["rhs"],
44
+ }
45
+ )
46
+ return rule_set
47
+
48
+ def add_custom_control(
49
+ self,
50
+ name: str,
51
+ default_action: str,
52
+ severity: str,
53
+ type: str,
54
+ rules: list,
55
+ **kwargs,
56
+ ) -> Box:
57
+ """
58
+ Adds a new ZPA Inspection Custom Control.
59
+
60
+ Args:
61
+ name (str): The name of the custom control.
62
+ default_action (str): The default action to take for matches against this custom control.
63
+ severity (str): The severity for events that match this custom control.
64
+ type (str): The type of HTTP message this control matches.
65
+ rules (list): A list of Inspection Control rule objects.
66
+ **kwargs: Optional keyword args.
67
+
68
+ Keyword Args:
69
+ description (str): Additional information about the custom control.
70
+ paranoia_level (int): The paranoia level for the custom control.
71
+
72
+ Returns:
73
+ :obj:`Box`: The newly created custom Inspection Control resource record.
74
+
75
+ """
76
+
77
+ payload = {
78
+ "name": name,
79
+ "defaultAction": default_action,
80
+ "severity": severity,
81
+ "rules": [],
82
+ "type": type,
83
+ }
84
+
85
+ # Handle default_action_value
86
+ if "default_action_value" in kwargs:
87
+ payload["defaultActionValue"] = kwargs["default_action_value"]
88
+
89
+ # Use the _create_rule method to restructure the Inspection Control rule and add to the payload.
90
+ for rule in rules:
91
+ payload["rules"].append(self._create_rule(rule))
92
+
93
+ # Add optional parameters to payload
94
+ for key, value in kwargs.items():
95
+ if key == "paranoia_level":
96
+ payload["paranoiaLevel"] = int(value)
97
+ elif key == "description":
98
+ payload["description"] = value
99
+ # Add other optional parameters if necessary
100
+
101
+ # Convert snake to camelcase
102
+ payload = convert_keys(payload)
103
+
104
+ response = self.rest.post("inspectionControls/custom", json=payload)
105
+ if isinstance(response, Response):
106
+ # this is only true when the creation failed (status code is not 2xx)
107
+ status_code = response.status_code
108
+ # Handle error response
109
+ raise Exception(
110
+ f"API call failed with status {status_code}: {response.json()}"
111
+ )
112
+ return response
113
+
114
+ # response = self.rest.post("/inspectionControls/custom", json=payload)
115
+ # if isinstance(response, Response):
116
+ # status_code = response.status_code
117
+ # if status_code > 299:
118
+ # return None
119
+ # return self.get_custom_control(response.get("id"))
120
+
121
+ def add_profile(
122
+ self, name: str, paranoia_level: int, predef_controls_version: str, **kwargs
123
+ ):
124
+ """
125
+ Adds a ZPA Inspection Profile.
126
+
127
+ Args:
128
+ name (str):
129
+ The name of the Inspection Profile.
130
+ paranoia_level (int):
131
+ The paranoia level for the Inspection Profile.
132
+ predef_controls_version (str):
133
+ The version of the predefined controls that will be added.
134
+ **kwargs:
135
+ Additional keyword args.
136
+
137
+ Keyword Args:
138
+ **description (str):
139
+ Additional information about the Inspection Profile.
140
+ **custom_controls (list):
141
+ A tuple list of custom controls to be added to the Inspection profile.
142
+
143
+ Custom control tuples must follow the convention below:
144
+
145
+ ``(control_id, action)``
146
+
147
+ e.g.
148
+
149
+ .. code-block:: python
150
+
151
+ custom_controls = [(99999, "BLOCK"), (88888, "PASS")]
152
+ **predef_controls (list):
153
+ A tuple list of predefined controls to be added to the Inspection profile.
154
+
155
+ Predefined control tuples must follow the convention below:
156
+
157
+ ``(control_id, action)``
158
+
159
+ e.g.
160
+
161
+ .. code-block:: python
162
+
163
+ predef_controls = [(77777, "BLOCK"), (66666, "PASS")]
164
+
165
+ Returns:
166
+ :obj:`Box`: The newly created Inspection Profile resource record.
167
+
168
+ Examples:
169
+ Add a new ZPA Inspection Profile with the minimum required parameters, printing the object to
170
+ console after creation:
171
+
172
+ .. code-block:: python
173
+
174
+ print(
175
+ zpa.inspection.add_profile(
176
+ name="predefined_controls",
177
+ paranoia_level=3,
178
+ predef_controls_version="OWASP_CRS/3.3.0",
179
+ )
180
+ )
181
+
182
+ Add a new ZPA Inspection Profile that uses additional predefined controls and custom controls, printing the
183
+ object to console after creation:
184
+
185
+ .. code-block:: python
186
+
187
+ print(
188
+ zpa.inspection.add_profile(
189
+ name="block_common_xss",
190
+ paranoia_level=2,
191
+ predefined_controls=[("99999", "BLOCK")],
192
+ predef_controls_version="OWASP_CRS/3.3.0",
193
+ custom_controls=[("88888", "BLOCK")],
194
+ )
195
+ )
196
+
197
+ """
198
+
199
+ # Inspection Profiles require the default predefined controls to be added. zscaler-sdk-python adds these in
200
+ # automatically for our users.
201
+
202
+ predef_controls = self.list_predef_controls("OWASP_CRS/3.3.0")
203
+ default_controls = []
204
+ for group in predef_controls:
205
+ if group.default_group:
206
+ default_controls = group.predefined_inspection_controls
207
+
208
+ payload = {
209
+ "name": name,
210
+ "paranoiaLevel": paranoia_level,
211
+ "predefinedControls": default_controls,
212
+ "predefinedControlsVersion": predef_controls_version,
213
+ }
214
+
215
+ # Extend existing list of default predefined controls if the user supplies more
216
+ if kwargs.get("predef_controls"):
217
+ controls = kwargs.pop("predef_controls")
218
+ for control in controls:
219
+ payload["predefinedControls"].append(
220
+ {"id": control[0], "action": control[1]}
221
+ )
222
+
223
+ # Add custom controls if provided
224
+ if kwargs.get("custom_controls"):
225
+ controls = kwargs.pop("custom_controls")
226
+ payload["customControls"] = [
227
+ {"id": control[0], "action": control[1]} for control in controls
228
+ ]
229
+
230
+ # Add optional parameters to payload
231
+ for key, value in kwargs.items():
232
+ payload[key] = value
233
+
234
+ payload = convert_keys(payload)
235
+
236
+ response = self.rest.post("inspectionProfile", json=payload)
237
+ if isinstance(response, Response):
238
+ # this is only true when the creation failed (status code is not 2xx)
239
+ status_code = response.status_code
240
+ # Handle error response
241
+ raise Exception(
242
+ f"API call failed with status {status_code}: {response.json()}"
243
+ )
244
+ return response
245
+
246
+ # response = self.rest.post("/inspectionProfile", json=payload)
247
+ # if isinstance(response, Response):
248
+ # status_code = response.status_code
249
+ # if status_code > 299:
250
+ # return None
251
+ # return self.get_profile(response.get("id"))
252
+
253
+ def delete_custom_control(self, control_id: str) -> int:
254
+ """
255
+ Deletes the specified custom ZPA Inspection Control.
256
+
257
+ Args:
258
+ control_id (str):
259
+ The unique id for the custom control that will be deleted.
260
+
261
+ Returns:
262
+ :obj:`int`: The status code for the operation.
263
+
264
+ Examples:
265
+ Delete a custom ZPA Inspection Control with an id of `99999`.
266
+
267
+ .. code-block:: python
268
+
269
+ zpa.inspection.delete_custom_control("99999")
270
+
271
+ """
272
+ return self.rest.delete(f"inspectionControls/custom/{control_id}").status_code
273
+
274
+ def delete_profile(self, profile_id: str):
275
+ """
276
+ Deletes the specified Inspection Profile.
277
+
278
+ Args:
279
+ profile_id (str):
280
+ The unique id for the Inspection Profile that will be deleted.
281
+
282
+ Returns:
283
+ :obj:`int`: The status code for the operation.
284
+
285
+ Examples:
286
+ Delete an Inspection Profile with an id of *999999*:
287
+
288
+ .. code-block:: python
289
+
290
+ zpa.inspection.delete_profile("999999")
291
+
292
+ """
293
+ return self.rest.delete(f"inspectionProfile/{profile_id}").status_code
294
+
295
+ def get_custom_control(self, control_id: str) -> Box:
296
+ """
297
+ Returns the specified custom ZPA Inspection Control.
298
+
299
+ Args:
300
+ control_id (str):
301
+ The unique id of the custom ZPA Inspection Control to be returned.
302
+
303
+ Returns:
304
+ :obj:`Box`: The custom ZPA Inspection Control resource record.
305
+
306
+ Examples:
307
+ Print the Custom Inspection Control with an id of `99999`:
308
+
309
+ .. code-block:: python
310
+
311
+ print(zpa.inspection.get_custom_control("99999"))
312
+
313
+ """
314
+ response = self.rest.get("/inspectionControls/custom/%s" % (control_id))
315
+ if isinstance(response, Response):
316
+ status_code = response.status_code
317
+ if status_code != 200:
318
+ return None
319
+ return response
320
+
321
+ def get_predef_control(self, control_id: str):
322
+ """
323
+ Returns the specified predefined ZPA Inspection Control.
324
+
325
+ Args:
326
+ control_id (str):
327
+ The unique id of the predefined ZPA Inspection Control to be returned.
328
+
329
+ Returns:
330
+ :obj:`Box`: The ZPA Inspection Predefined Control resource record.
331
+
332
+ Examples:
333
+ Print the ZPA Inspection Predefined Control with an id of `99999`:
334
+
335
+ .. code-block:: python
336
+
337
+ print(zpa.inspection.get_predef_control("99999"))
338
+
339
+ """
340
+ response = self.rest.get("/inspectionControls/predefined/%s" % (control_id))
341
+ if isinstance(response, Response):
342
+ status_code = response.status_code
343
+ if status_code != 200:
344
+ return None
345
+ return response
346
+
347
+ def get_profile(self, profile_id: str) -> Box:
348
+ """
349
+ Returns the specified ZPA Inspection Profile.
350
+
351
+ Args:
352
+ profile_id (str):
353
+ The unique id of the ZPA Inspection Profile
354
+
355
+ Returns:
356
+ :obj:`Box`: The specified ZPA Inspection Profile resource record.
357
+
358
+ Examples:
359
+ Print the ZPA Inspection Profile with an id of `99999`:
360
+
361
+ .. code-block:: python
362
+
363
+ print(zpa.inspection.get_profile("99999"))
364
+
365
+ """
366
+ response = self.rest.get("/inspectionProfile/%s" % (profile_id))
367
+ if isinstance(response, Response):
368
+ status_code = response.status_code
369
+ if status_code != 200:
370
+ return None
371
+ return response
372
+
373
+ def list_control_action_types(self) -> Box:
374
+ """
375
+ Returns a list of ZPA Inspection Control Action Types.
376
+
377
+ Returns:
378
+ :obj:`BoxList`: A list containing the ZPA Inspection Control Action Types.
379
+
380
+ Examples:
381
+ Iterate over the ZPA Inspection Control Action Types and print each one:
382
+
383
+ .. code-block:: python
384
+
385
+ for action_type in zpa.inspection.list_control_action_types():
386
+ print(action_type)
387
+
388
+ """
389
+ return self.rest.get("inspectionControls/actionTypes")
390
+
391
+ def list_control_severity_types(self) -> BoxList:
392
+ """
393
+ Returns a list of Inspection Control Severity Types.
394
+
395
+ Returns:
396
+ :obj:`BoxList`: A list containing all valid Inspection Control Severity Types.
397
+
398
+ Examples:
399
+ Print all Inspection Control Severity Types
400
+
401
+ .. code-block:: python
402
+
403
+ for severity in zpa.inspection.list_control_severity_types():
404
+ print(severity)
405
+
406
+ """
407
+ return self.rest.get("inspectionControls/severityTypes")
408
+
409
+ def list_control_types(self) -> BoxList:
410
+ """
411
+ Returns a list of ZPA Inspection Control Types.
412
+
413
+ Returns:
414
+ :obj:`BoxList`: A list containing ZPA Inspection Control Types.
415
+
416
+ Examples:
417
+ Print all ZPA Inspection Control Types:
418
+
419
+ .. code-block:: python
420
+
421
+ for control_type in zpa.inspection.list_control_types():
422
+ print(control_type)
423
+
424
+ """
425
+ return self.rest.get("inspectionControls/controlTypes")
426
+
427
+ def list_custom_control_types(self) -> BoxList:
428
+ """
429
+ Returns a list of custom ZPA Inspection Control Types.
430
+
431
+ Returns:
432
+ :obj:`BoxList`: A list containing custom ZPA Inspection Control Types.
433
+
434
+ Examples:
435
+
436
+ Print all custom ZPA Inspection Control Types
437
+
438
+ .. code-block:: python
439
+
440
+ for control_type in zpa.inspection.list_custom_control_types():
441
+ print(control_type)
442
+
443
+ """
444
+ return self.rest.get(
445
+ "https://config.private.zscaler.com/mgmtconfig/v1/admin/inspectionControls/customControlTypes"
446
+ )
447
+
448
+ def list_custom_controls(self, **kwargs) -> BoxList:
449
+ """
450
+ Returns a list of all custom ZPA Inspection Controls.
451
+
452
+ Args:
453
+ **kwargs: Optional keyword arguments.
454
+
455
+ Keyword Args:
456
+ **search (str):
457
+ The string used to search for a custom control by features and fields.
458
+ **sortdir (str):
459
+ Specifies the sorting order for the search results.
460
+
461
+ Accepted values are:
462
+
463
+ - ``ASC`` - ascending order
464
+ - ``DESC`` - descending order
465
+
466
+ Returns:
467
+ :obj:`BoxList`: A list containing all custom ZPA Inspection Controls.
468
+
469
+ Examples:
470
+ Print a list of all custom ZPA Inspection Controls:
471
+
472
+ .. code-block:: python
473
+
474
+ for control in zpa.inspection.list_custom_controls():
475
+ print(control)
476
+
477
+ """
478
+ list, _ = self.rest.get_paginated_data(
479
+ path="/inspectionControls/custom",
480
+ )
481
+ return list
482
+
483
+ def list_custom_http_methods(self) -> BoxList:
484
+ """
485
+ Returns a list of custom ZPA Inspection Control HTTP Methods.
486
+
487
+ Returns:
488
+ :obj:`BoxList`: A list containing custom ZPA Inspection Control HTTP Methods.
489
+
490
+ Examples:
491
+
492
+ Print all custom ZPA Inspection Control HTTP Methods:
493
+
494
+ .. code-block:: python
495
+
496
+ for method in zpa.inspection.list_custom_http_methods():
497
+ print(method)
498
+
499
+ """
500
+ return self.rest.get("inspectionControls/custom/httpMethods")
501
+
502
+ def list_predef_control_versions(self) -> BoxList:
503
+ """
504
+ Returns a list of predefined ZPA Inspection Control versions.
505
+
506
+ Returns:
507
+ :obj:`BoxList`: A list containing all predefined ZPA Inspection Control versions.
508
+
509
+ Examples:
510
+ Print all predefined ZPA Inspection Control versions::
511
+
512
+ for version in zpa.inspection.list_predef_control_versions():
513
+ print(version)
514
+
515
+ """
516
+ return self.rest.get("inspectionControls/predefined/versions")
517
+
518
+ def list_predef_controls(self, version: str, **kwargs) -> BoxList:
519
+ """
520
+ Returns a list of predefined ZPA Inspection Controls.
521
+
522
+ Args:
523
+ version (str):
524
+ The version of the predefined controls to return.
525
+ **kwargs:
526
+ Optional keyword args.
527
+
528
+ Keyword Args:
529
+ **search (str):
530
+ The string used to search for predefined inspection controls by features and fields.
531
+
532
+ Returns:
533
+ :obj:`BoxList`: A list containing all predefined ZPA Inspection Controls that match the Version and Search
534
+ string.
535
+
536
+ Examples:
537
+ Return all predefined ZPA Inspection Controls for the given version:
538
+
539
+ .. code-block:: python
540
+
541
+ for control in zpa.inspection.list_predef_controls(version="OWASP_CRS/3.3.0"):
542
+ print(control)
543
+
544
+ Return predefined ZPA Inspection Controls matching a search string:
545
+
546
+ .. code-block:: python
547
+
548
+ for control in zpa.inspection.list_predef_controls(search="new_control", version="OWASP_CRS/3.3.0"):
549
+ print(control)
550
+
551
+ """
552
+ # Encode the version parameter to be URL safe
553
+ encoded_version = quote(version, safe="")
554
+
555
+ # Construct the full URL with version query param
556
+ url = f"/inspectionControls/predefined?version={encoded_version}"
557
+
558
+ # If you have additional query parameters, add them to the URL
559
+ if kwargs:
560
+ additional_params = "&".join(
561
+ f"{key}={quote(str(value))}" for key, value in kwargs.items()
562
+ )
563
+ url += f"&{additional_params}"
564
+
565
+ # Make the GET request
566
+ response = self.rest.get(url)
567
+ if isinstance(response, Response):
568
+ status_code = response.status_code
569
+ if status_code != 200:
570
+ # Handle error or return None based on your API handling
571
+ return None
572
+ return response
573
+
574
+ def get_predef_control_by_name(
575
+ self, name: str, version: str = "OWASP_CRS/3.3.0"
576
+ ) -> Box:
577
+ """
578
+ Returns the specified predefined ZPA Inspection Control by its name.
579
+
580
+ Args:
581
+ name (str):
582
+ The name of the predefined ZPA Inspection Control to be returned.
583
+ version (str):
584
+ The version of the predefined control to return.
585
+
586
+ Returns:
587
+ :obj:`Box`: The ZPA Inspection Predefined Control resource record.
588
+
589
+ Examples:
590
+ Print the ZPA Inspection Predefined Control with the name `Failed to parse request body`:
591
+
592
+ .. code-block:: python
593
+
594
+ print(zpa.inspection.get_predef_control_by_name("Failed to parse request body", "OWASP_CRS/3.3.0"))
595
+
596
+ """
597
+ # Use the list_predef_controls method to get all controls
598
+ all_controls = self.list_predef_controls(version)
599
+
600
+ # Iterate through the controls to find the one with the desired name
601
+ for control_group in all_controls:
602
+ for control in control_group.get("predefined_inspection_controls", []):
603
+ if control["name"] == name:
604
+ return control
605
+
606
+ # If we reach here, the control was not found
607
+ raise ValueError(f"No predefined control named '{name}' found")
608
+
609
+ def get_predef_control_group_by_name(
610
+ self, group_name: str, version: str = "OWASP_CRS/3.3.0"
611
+ ) -> Box:
612
+ """
613
+ Returns the specified predefined ZPA Inspection Control Group by its name.
614
+
615
+ Args:
616
+ group_name (str):
617
+ The name of the predefined ZPA Inspection Control Group to be returned.
618
+ version (str):
619
+ The version of the predefined control to return.
620
+
621
+ Returns:
622
+ :obj:`Box`: The ZPA Inspection Predefined Control Group resource record.
623
+
624
+ Examples:
625
+ Print the ZPA Inspection Predefined Control Group with the name `Preprocessors`:
626
+
627
+ .. code-block:: python
628
+
629
+ print(zpa.inspection.get_predef_control_group_by_name("Preprocessors", "OWASP_CRS/3.3.0"))
630
+
631
+ """
632
+ # Use the list_predef_controls method to get all control groups
633
+ all_control_groups = self.list_predef_controls(version)
634
+
635
+ # Iterate through the control groups to find the one with the desired name
636
+ for control_group in all_control_groups:
637
+ if control_group["control_group"] == group_name:
638
+ return control_group
639
+
640
+ # If we reach here, the control group was not found
641
+ raise ValueError(f"No predefined control group named '{group_name}' found")
642
+
643
+ def list_profiles(self, **kwargs) -> BoxList:
644
+ """
645
+ Returns the list of ZPA Inspection Profiles.
646
+
647
+ Args:
648
+ **kwargs:
649
+ Optional keyword args.
650
+
651
+ Keyword Args:
652
+ **pagesize (int):
653
+ Specifies the page size. The default size is 20 and the maximum size is 500.
654
+ **search (str, optional):
655
+ The search string used to match against features and fields.
656
+
657
+ Returns:
658
+ :obj:`BoxList`: The list of ZPA Inspection Profile resource records.
659
+
660
+ Examples:
661
+ Iterate over all ZPA Inspection Profiles and print them:
662
+
663
+ .. code-block:: python
664
+
665
+ for profile in zpa.inspection.list_profiles():
666
+ print(profile)
667
+
668
+ """
669
+ list, _ = self.rest.get_paginated_data(
670
+ path="/inspectionProfile",
671
+ )
672
+ return list
673
+
674
+ def profile_control_attach(self, profile_id: str, action: str, **kwargs) -> Box:
675
+ """
676
+ Attaches or detaches all predefined ZPA Inspection Controls to a ZPA Inspection Profile.
677
+
678
+ Args:
679
+ profile_id (str):
680
+ The unique id for the ZPA Inspection Profile that will be modified.
681
+ action (str):
682
+ The association action that will be taken, accepted values are:
683
+
684
+ * ``attach``: Attaches all predefined controls to the Inspection Profile with the specified version.
685
+ * ``detach``: Detaches all predefined controls from the Inspection Profile.
686
+ **kwargs:
687
+ Additional keyword arguments.
688
+
689
+ Keyword Args:
690
+ profile_version (str):
691
+ The version of the Predefined Controls to attach. Only required when using the
692
+ attach action. Defaults to ``OWASP_CRS/3.3.0``.
693
+
694
+ Returns:
695
+ :obj:`Box`: The updated ZPA Inspection Profile resource record.
696
+
697
+ Examples:
698
+ Attach all predefined controls to a ZPA Inspection Profile with an id of 99999:
699
+
700
+ .. code-block:: python
701
+
702
+ updated_profile = zpa.inspection.profile_control_attach("99999", action="attach")
703
+
704
+ Attach all predefined controls to a ZPA Inspection Profile with an id of 99999 and specified version:
705
+
706
+ .. code-block:: python
707
+
708
+ updated_profile = zpa.inspection.profile_control_attach(
709
+ "99999",
710
+ action="attach",
711
+ profile_version="OWASP_CRS/3.2.0",
712
+ )
713
+
714
+ Detach all predefined controls from a ZPA Inspection Profile with an id of 99999:
715
+
716
+ .. code-block:: python
717
+
718
+ updated_profile = zpa.inspection.profile_control_attach(
719
+ "99999",
720
+ action="detach",
721
+ )
722
+
723
+ Raises:
724
+ ValueError: If an incorrect value is supplied for `action`.
725
+
726
+ """
727
+ if action == "attach":
728
+ payload = {"version": kwargs.pop("profile_version", "OWASP_CRS/3.3.0")}
729
+ resp = self.rest.put(
730
+ f"inspectionProfile/{profile_id}/associateAllPredefinedControls",
731
+ params=payload,
732
+ )
733
+ return (
734
+ self.get_profile(profile_id)
735
+ if resp.status_code == 204
736
+ else resp.status_code
737
+ )
738
+ elif action == "detach":
739
+ resp = self.rest.put(
740
+ f"inspectionProfile/{profile_id}/deAssociateAllPredefinedControls"
741
+ )
742
+ return (
743
+ self.get_profile(profile_id)
744
+ if resp.status_code == 204
745
+ else resp.status_code
746
+ )
747
+ else:
748
+ raise ValueError(
749
+ "Unknown action provided. Valid actions are 'attach' or 'detach'."
750
+ )
751
+
752
+ def update_custom_control(self, control_id: str, **kwargs) -> Box:
753
+ """
754
+ Updates the specified custom ZPA Inspection Control.
755
+
756
+ Args:
757
+ control_id (str):
758
+ The unique id for the custom control that will be updated.
759
+ **kwargs:
760
+ Optional keyword args.
761
+
762
+ Keyword Args:
763
+ **description (str):
764
+ Additional information about the custom control.
765
+ **default_action (str):
766
+ The default action to take for matches against this custom control. Valid options are:
767
+
768
+ - ``PASS``
769
+ - ``BLOCK``
770
+ - ``REDIRECT``
771
+ **name (str):
772
+ The name of the custom control.
773
+ **paranoia_level (int):
774
+ The paranoia level for the custom control.
775
+ **rules (list):
776
+ A list of Inspection Control rule objects, with each object using the format::
777
+
778
+ {
779
+ "names": ["name1", "name2"],
780
+ "type": "rule_type",
781
+ "conditions": [
782
+ ("LHS", "OP", "RHS"),
783
+ ("LHS", "OP", "RHS"),
784
+ ],
785
+ }
786
+ **severity (str):
787
+ The severity for events that match this custom control. Valid options are:
788
+
789
+ - ``CRITICAL``
790
+ - ``ERROR``
791
+ - ``WARNING``
792
+ - ``INFO``
793
+ **type (str):
794
+ The type of HTTP message this control matches. Valid options are:
795
+
796
+ - ``REQUEST``
797
+ - ``RESPONSE``
798
+
799
+ Returns:
800
+ :obj:`Box`: The updated custom ZPA Inspection Control resource record.
801
+
802
+ Examples:
803
+ Update the description of a custom ZPA Inspection Control with an id of 99999:
804
+
805
+ .. code-block:: python
806
+
807
+ print(
808
+ zpa.inspection.update_custom_control(
809
+ "99999",
810
+ description="Updated description",
811
+ )
812
+ )
813
+
814
+ Update the rules of a custom ZPA Inspection Control with an id of 88888:
815
+
816
+ .. code-block:: python
817
+
818
+ print(
819
+ zpa.inspection.update_custom_control(
820
+ "88888",
821
+ rules=[
822
+ {
823
+ "names": ["xforwardedfor_ge_20"],
824
+ "type": "REQUEST_HEADERS",
825
+ "conditions": [
826
+ ("SIZE", "GE", "20"),
827
+ ("VALUE", "CONTAINS", "X-Forwarded-For"),
828
+ ],
829
+ }
830
+ ],
831
+ )
832
+ )
833
+
834
+
835
+ """
836
+
837
+ # Set payload to value of existing record and recursively convert nested dict keys from snake_case
838
+ # to camelCase.
839
+ payload = convert_keys(self.get_custom_control(control_id))
840
+
841
+ # If the user provides rules for an update, clear the current rules then use the create_rule method to
842
+ # restructure the Inspection Control rule and add to the payload.
843
+ if kwargs.get("rules"):
844
+ payload["rules"] = []
845
+ for rule in kwargs.pop("rules"):
846
+ payload["rules"].append(self._create_rule(rule))
847
+
848
+ # Add optional parameters to payload
849
+ for key, value in kwargs.items():
850
+ payload_key = snake_to_camel(key)
851
+ payload[payload_key] = value
852
+
853
+ # Special handling for default_action_value
854
+ if key == "default_action_value":
855
+ payload["defaultActionValue"] = value
856
+
857
+ resp = self.rest.put(
858
+ f"inspectionControls/custom/{control_id}", json=payload
859
+ ).status_code
860
+
861
+ # Return the object if it was updated successfully
862
+ if not isinstance(resp, Response):
863
+ return self.get_custom_control(control_id)
864
+
865
+ def update_profile(self, profile_id: str, **kwargs):
866
+ """
867
+ Updates the specified ZPA Inspection Profile.
868
+
869
+ Args:
870
+ profile_id (str):
871
+ The unique id for the ZPA Inspection Profile that will be updated.
872
+ predef_controls_version (str):
873
+ The predefined controls version for the ZPA Inspection Profile. Defaults to `OWASP_CRS/3.3.0`.
874
+ **kwargs:
875
+ Optional keyword args.
876
+
877
+ Keyword Args:
878
+ **custom_controls (list):
879
+ A tuple list of custom controls to be added to the Inspection profile.
880
+
881
+ Custom control tuples must follow the convention below:
882
+
883
+ ``(control_id, action)``
884
+
885
+ e.g.
886
+
887
+ .. code-block:: python
888
+
889
+ custom_controls = [(99999, "BLOCK"), (88888, "PASS")]
890
+ **description (str):
891
+ Additional information about the Inspection Profile.
892
+ **name (str):
893
+ The name of the Inspection Profile.
894
+ **paranoia_level (int):
895
+ The paranoia level for the Inspection Profile.
896
+ **predef_controls (list):
897
+ A tuple list of predefined controls to be added to the Inspection profile.
898
+
899
+ Predefined control tuples must follow the convention below:
900
+
901
+ ``(control_id, action)``
902
+
903
+ e.g.
904
+
905
+ .. code-block:: python
906
+
907
+ predef_controls = [(77777, "BLOCK"), (66666, "PASS")]
908
+ **predef_controls_version (str):
909
+ The version of the predefined controls that will be added.
910
+
911
+ Returns:
912
+ :obj:`Box`: The updated ZPA Inspection Profile resource record.
913
+
914
+ Examples:
915
+ Update the name and description of a ZPA Inspection Profile with the id 99999:
916
+
917
+ .. code-block:: python
918
+
919
+ print(
920
+ zpa.inspection.update_profile(
921
+ "99999",
922
+ name="inspect_common_predef_controls",
923
+ description="Inspects common controls from the Predefined set.",
924
+ )
925
+ )
926
+
927
+ Add a custom control to the ZPA Inspection Profile with the id 88888:
928
+
929
+ .. code-block:: python
930
+
931
+ print(
932
+ zpa.inspection.update_profile(
933
+ "88888",
934
+ custom_controls=[("2", "BLOCK")],
935
+ )
936
+ )
937
+
938
+ """
939
+ # Set payload to value of existing record
940
+ payload = self.get_profile(profile_id)
941
+ payload["predefinedControlsVersion"] = kwargs.get(
942
+ "predef_controls_version", "OWASP_CRS/3.3.0"
943
+ )
944
+
945
+ # Extend existing list of default predefined controls if the user supplies more
946
+ if kwargs.get("predef_controls"):
947
+ controls = kwargs.pop("predef_controls")
948
+ for control in controls:
949
+ payload["predefined_controls"] = [
950
+ {"id": control[0], "action": control[1]} for control in controls
951
+ ]
952
+
953
+ # Add custom controls if provided
954
+ if kwargs.get("custom_controls"):
955
+ controls = kwargs.pop("custom_controls")
956
+ payload["custom_controls"] = [
957
+ {"id": control[0], "action": control[1]} for control in controls
958
+ ]
959
+
960
+ # Add optional parameters to payload
961
+ for key, value in kwargs.items():
962
+ payload[key] = value
963
+
964
+ # Convert from snake case to camel case
965
+ payload = convert_keys(payload)
966
+
967
+ resp = self.rest.put(
968
+ f"inspectionProfile/{profile_id}", json=payload
969
+ ).status_code
970
+
971
+ # Return the object if it was updated successfully
972
+ if not isinstance(resp, Response):
973
+ return self.get_profile(profile_id)
974
+
975
+ def update_profile_and_controls(
976
+ self, profile_id: str, inspection_profile: dict, **kwargs
977
+ ):
978
+ """
979
+ Updates the inspection profile and controls for the specified ID.
980
+
981
+ Note:
982
+ This method has not been fully implemented and will not be maintained. There seems to be functionality
983
+ duplication with the default Inspection Profile update API call. `**kwargs` has been provided as a parameter
984
+ for you to be able to add any additional args that Zscaler may add.
985
+
986
+ If you feel that this is in error and that this functionality should be correctly implemented by zscaler-sdk-python
987
+ `raise an issue <https://github.com/zscaler/zscaler-sdk-python/issues>` in the zscaler-sdk-python Github repo
988
+
989
+ Args:
990
+ profile_id (str):
991
+ The unique id of the inspection profile.
992
+ inspection_profile (dict):
993
+ The new inspection profile object.
994
+ **kwargs:
995
+ Additional keyword args.
996
+
997
+ """
998
+
999
+ payload = {
1000
+ "inspection_profile_id": profile_id,
1001
+ "inspection_profile": inspection_profile,
1002
+ }
1003
+
1004
+ payload = convert_keys(payload)
1005
+
1006
+ resp = self.rest.put(
1007
+ "inspectionProfile/{profile_id}/patch", json=payload
1008
+ ).status_code
1009
+
1010
+ # Return the object if it was updated successfully
1011
+ if not isinstance(resp, Response):
1012
+ return self.get_profile(profile_id)