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