zscaler-sdk-python 0.9.0__tar.gz → 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/PKG-INFO +3 -3
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/pyproject.toml +4 -4
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/__init__.py +2 -1
- zscaler_sdk_python-0.9.1/zscaler/zcc/__init__.py +265 -0
- zscaler_sdk_python-0.9.1/zscaler/zcc/client.py +65 -0
- zscaler_sdk_python-0.9.1/zscaler/zcc/devices.py +220 -0
- zscaler_sdk_python-0.9.1/zscaler/zcc/secrets.py +66 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/__init__.py +1 -1
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/LICENSE.md +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/README.md +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/cache/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/cache/cache.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/cache/no_op_cache.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/cache/zscaler_cache.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/constants.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/errors/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/errors/error.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/errors/http_error.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/errors/zscaler_api_error.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/exceptions/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/exceptions/exceptions.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/logger.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/ratelimiter/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/ratelimiter/ratelimiter.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/user_agent.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/utils.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/activation.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/client.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/ecgroups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/locations.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/provisioning.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/admin.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/alerts.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/apps.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/devices.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/filters.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/inventory.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/troubleshooting.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/users.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zdx/zdx_client.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/activate.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/admin_and_role_management.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/apptotal.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/audit_logs.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/authentication_settings.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/client.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/cloud_apps.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/cloudappcontrol.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/device_management.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/dlp.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/errors.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/firewall.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/forwarding_control.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/isolation_profile.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/labels.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/locations.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/sandbox.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/security.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/ssl_inspection.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/traffic.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/url_categories.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/url_filtering.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/users.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/web_dlp.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/workload_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/zpa_gateway.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/README.md +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/__init__.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/app_segments.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/app_segments_inspection.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/app_segments_pra.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/authdomains.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/certificates.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/client.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/cloud_connector_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/connectors.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/emergency_access.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/errors.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/idp.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/inspection.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/isolation.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/lss.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/machine_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/microtenants.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/policies.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/posture_profiles.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/privileged_remote_access.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/provisioning.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/saml_attributes.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/scim_attributes.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/scim_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/segment_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/server_groups.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/servers.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/service_edges.py +0 -0
- {zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/trusted_networks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zscaler-sdk-python
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Official Python SDK for the Zscaler Products (Beta)
|
|
5
5
|
Home-page: https://github.com/zscaler/zscaler-sdk-python
|
|
6
6
|
License: MIT
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Security
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: aenum ; extra == "dev"
|
|
25
|
-
Requires-Dist: aiohttp (>=3.10.
|
|
25
|
+
Requires-Dist: aiohttp (>=3.10.2)
|
|
26
26
|
Requires-Dist: arrow
|
|
27
27
|
Requires-Dist: black (>=24.3.0) ; extra == "dev"
|
|
28
28
|
Requires-Dist: certifi (>=2024.2.2)
|
|
@@ -30,7 +30,7 @@ Requires-Dist: charset-normalizer
|
|
|
30
30
|
Requires-Dist: cryptography (>=43.0.0)
|
|
31
31
|
Requires-Dist: flake8
|
|
32
32
|
Requires-Dist: flatdict
|
|
33
|
-
Requires-Dist: idna
|
|
33
|
+
Requires-Dist: idna
|
|
34
34
|
Requires-Dist: okta (>=2.9.7)
|
|
35
35
|
Requires-Dist: pycryptodomex
|
|
36
36
|
Requires-Dist: pydash (>=8.0.3) ; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "zscaler-sdk-python"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.1"
|
|
4
4
|
description = "Official Python SDK for the Zscaler Products (Beta)"
|
|
5
5
|
authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -32,7 +32,7 @@ python = ">=3.9,<4.0"
|
|
|
32
32
|
arrow = "*"
|
|
33
33
|
certifi = ">=2024.2.2"
|
|
34
34
|
charset-normalizer = "*"
|
|
35
|
-
idna = "
|
|
35
|
+
idna = "*"
|
|
36
36
|
python-box = ">=7.2.0"
|
|
37
37
|
python-dateutil = "*"
|
|
38
38
|
requests = ">=2.32.3"
|
|
@@ -52,12 +52,12 @@ black = ">=24.3.0"
|
|
|
52
52
|
# cryptography = ">=3.4,<43.0"
|
|
53
53
|
cryptography = ">=43.0.0"
|
|
54
54
|
okta = ">=2.9.7"
|
|
55
|
-
aiohttp = ">=3.10.
|
|
55
|
+
aiohttp = ">=3.10.2"
|
|
56
56
|
|
|
57
57
|
[tool.poetry.dev-dependencies]
|
|
58
58
|
black = ">=24.3.0"
|
|
59
59
|
pytest = ">=8.3.1"
|
|
60
|
-
pytest-asyncio = "
|
|
60
|
+
pytest-asyncio = "^0.23.8"
|
|
61
61
|
pytest-mock = "*"
|
|
62
62
|
pytest-recording = "^0.13.2"
|
|
63
63
|
pytest-cov = "*"
|
|
@@ -29,9 +29,10 @@ __license__ = "MIT"
|
|
|
29
29
|
__contributors__ = [
|
|
30
30
|
"William Guilherme",
|
|
31
31
|
]
|
|
32
|
-
__version__ = "0.9.
|
|
32
|
+
__version__ = "0.9.1"
|
|
33
33
|
|
|
34
34
|
from zscaler.zdx import ZDXClientHelper # noqa
|
|
35
35
|
from zscaler.zia import ZIAClientHelper # noqa
|
|
36
36
|
from zscaler.zpa import ZPAClientHelper # noqa
|
|
37
37
|
from zscaler.zcon import ZCONClientHelper # noqa
|
|
38
|
+
from zscaler.zcc import ZCCClientHelper # noqa
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import urllib.parse
|
|
4
|
+
import uuid
|
|
5
|
+
import time
|
|
6
|
+
import requests
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
|
|
9
|
+
from zscaler import __version__
|
|
10
|
+
from zscaler.user_agent import UserAgent
|
|
11
|
+
from zscaler.utils import (
|
|
12
|
+
dump_request,
|
|
13
|
+
dump_response,
|
|
14
|
+
format_json_response,
|
|
15
|
+
is_token_expired,
|
|
16
|
+
retry_with_backoff,
|
|
17
|
+
)
|
|
18
|
+
from zscaler.logger import setup_logging
|
|
19
|
+
from .devices import DevicesAPI
|
|
20
|
+
from .secrets import SecretsAPI
|
|
21
|
+
|
|
22
|
+
from zscaler.zcc.client import ZCCClient
|
|
23
|
+
|
|
24
|
+
# Setup the logger
|
|
25
|
+
setup_logging(logger_name="zscaler-sdk-python")
|
|
26
|
+
logger = logging.getLogger("zscaler-sdk-python")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ZCCClientHelper(ZCCClient):
|
|
30
|
+
"""
|
|
31
|
+
A Controller to access Endpoints in the Zscaler Mobile Admin Portal API.
|
|
32
|
+
|
|
33
|
+
The ZCC object stores the session token and simplifies access to CRUD options within the ZCC Portal.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
client_id (str): The ZCC Client ID generated from the ZCC Portal.
|
|
37
|
+
client_secret (str): The ZCC Client Secret generated from the ZCC Portal.
|
|
38
|
+
cloud (str): The Zscaler cloud for your tenancy, accepted values are:
|
|
39
|
+
|
|
40
|
+
* ``zscaler``
|
|
41
|
+
* ``zscalerone``
|
|
42
|
+
* ``zscalertwo``
|
|
43
|
+
* ``zscalerthree``
|
|
44
|
+
* ``zscloud``
|
|
45
|
+
* ``zscalerbeta``
|
|
46
|
+
override_url (str):
|
|
47
|
+
If supplied, this attribute can be used to override the production URL that is derived
|
|
48
|
+
from supplying the `cloud` attribute. Use this attribute if you have a non-standard tenant URL
|
|
49
|
+
(e.g. internal test instance etc). When using this attribute, there is no need to supply the `cloud`
|
|
50
|
+
attribute. The override URL will be prepended to the API endpoint suffixes. The protocol must be included
|
|
51
|
+
i.e. http:// or https://.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_vendor = "Zscaler"
|
|
56
|
+
_product = "Zscaler Mobile Admin Portal"
|
|
57
|
+
_backoff = 3
|
|
58
|
+
_build = __version__
|
|
59
|
+
_env_base = "ZCC"
|
|
60
|
+
_env_cloud = "zscaler"
|
|
61
|
+
|
|
62
|
+
RATE_LIMIT = 100 # 100 API calls per hour
|
|
63
|
+
DOWNLOAD_DEVICES_LIMIT = 3 # 3 calls per day
|
|
64
|
+
RATE_LIMIT_RESET_TIME = timedelta(hours=1)
|
|
65
|
+
DOWNLOAD_DEVICES_RESET_TIME = timedelta(days=1)
|
|
66
|
+
|
|
67
|
+
def __init__(self, **kw):
|
|
68
|
+
self._apikey = kw.get("apikey", os.getenv(f"{self._env_base}_CLIENT_ID"))
|
|
69
|
+
self._secret_key = kw.get("secret_key", os.getenv(f"{self._env_base}_CLIENT_SECRET"))
|
|
70
|
+
self._env_cloud = (
|
|
71
|
+
os.getenv(f"{self._env_base}_CLOUD") if os.getenv(f"{self._env_base}_CLOUD") is not None else kw.get("cloud")
|
|
72
|
+
)
|
|
73
|
+
self.login_url = f"https://api-mobile.{self._env_cloud}.net/papi/auth/v1/login"
|
|
74
|
+
self.url = f"https://api-mobile.{self._env_cloud}.net/papi/public/v1"
|
|
75
|
+
|
|
76
|
+
self.user_agent = UserAgent().get_user_agent_string() # Ensure this returns a string
|
|
77
|
+
self.auth_token = None
|
|
78
|
+
self.headers = {}
|
|
79
|
+
self.refreshToken()
|
|
80
|
+
|
|
81
|
+
# Initialize rate limit tracking
|
|
82
|
+
self.last_request_time = datetime.utcnow()
|
|
83
|
+
self.request_count = 0
|
|
84
|
+
|
|
85
|
+
# Track specific download devices endpoint usage
|
|
86
|
+
self.download_devices_count = 0
|
|
87
|
+
self.download_devices_last_reset = datetime.utcnow()
|
|
88
|
+
|
|
89
|
+
def __enter__(self):
|
|
90
|
+
self.refreshToken()
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
94
|
+
logger.debug("Deauthenticating...")
|
|
95
|
+
|
|
96
|
+
def refreshToken(self):
|
|
97
|
+
if not self.auth_token or is_token_expired(self.auth_token):
|
|
98
|
+
response = self.login()
|
|
99
|
+
if response is None or response.status_code > 299 or not response.json():
|
|
100
|
+
logger.error("Failed to login using provided credentials, response: %s", response)
|
|
101
|
+
raise Exception("Failed to login using provided credentials.")
|
|
102
|
+
self.auth_token = response.json().get("jwtToken")
|
|
103
|
+
self.headers = {
|
|
104
|
+
"Content-Type": "application/json",
|
|
105
|
+
"Accept": "*/*",
|
|
106
|
+
"auth-token": f"{self.auth_token}",
|
|
107
|
+
"User-Agent": self.user_agent,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@retry_with_backoff(retries=5)
|
|
111
|
+
def login(self):
|
|
112
|
+
data = {"apiKey": self._apikey, "secretKey": self._secret_key}
|
|
113
|
+
headers = {
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
"Accept": "*/*",
|
|
116
|
+
"User-Agent": self.user_agent,
|
|
117
|
+
}
|
|
118
|
+
try:
|
|
119
|
+
url = self.login_url
|
|
120
|
+
resp = requests.post(url, json=data, headers=headers)
|
|
121
|
+
logger.info("Login attempt with status: %d", resp.status_code)
|
|
122
|
+
return resp
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.error("Login failed due to an exception: %s", str(e))
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def check_rate_limit(self, path):
|
|
128
|
+
"""
|
|
129
|
+
Checks the rate limit and adjusts the request timing accordingly.
|
|
130
|
+
"""
|
|
131
|
+
current_time = datetime.utcnow()
|
|
132
|
+
time_since_last_request = current_time - self.last_request_time
|
|
133
|
+
|
|
134
|
+
# Reset the rate limit counter if the reset time has passed
|
|
135
|
+
if time_since_last_request >= self.RATE_LIMIT_RESET_TIME:
|
|
136
|
+
self.request_count = 0
|
|
137
|
+
self.last_request_time = current_time
|
|
138
|
+
|
|
139
|
+
if "/downloadDevices" in path:
|
|
140
|
+
time_since_last_reset = current_time - self.download_devices_last_reset
|
|
141
|
+
if time_since_last_reset >= self.DOWNLOAD_DEVICES_RESET_TIME:
|
|
142
|
+
self.download_devices_count = 0
|
|
143
|
+
self.download_devices_last_reset = current_time
|
|
144
|
+
if self.download_devices_count >= self.DOWNLOAD_DEVICES_LIMIT:
|
|
145
|
+
logger.warning("Rate limit exceeded for /downloadDevices endpoint. Backing off...")
|
|
146
|
+
time.sleep(24 * 60 * 60) # Back off for a day
|
|
147
|
+
self.download_devices_count = 0
|
|
148
|
+
self.download_devices_last_reset = datetime.utcnow()
|
|
149
|
+
self.download_devices_count += 1
|
|
150
|
+
else:
|
|
151
|
+
if self.request_count >= self.RATE_LIMIT:
|
|
152
|
+
logger.warning("Rate limit exceeded. Backing off...")
|
|
153
|
+
time.sleep(3600) # Back off for an hour
|
|
154
|
+
self.request_count = 0
|
|
155
|
+
self.last_request_time = datetime.utcnow()
|
|
156
|
+
self.request_count += 1
|
|
157
|
+
|
|
158
|
+
def send(self, method, path, json=None, params=None, stream=False):
|
|
159
|
+
api = self.url
|
|
160
|
+
if params is None:
|
|
161
|
+
params = {}
|
|
162
|
+
url = f"{api}/{path.lstrip('/')}"
|
|
163
|
+
if params:
|
|
164
|
+
url = f"{url}?{urllib.parse.urlencode(params)}"
|
|
165
|
+
|
|
166
|
+
start_time = time.time()
|
|
167
|
+
headers_with_user_agent = self.headers.copy()
|
|
168
|
+
headers_with_user_agent["User-Agent"] = self.user_agent
|
|
169
|
+
request_uuid = str(uuid.uuid4())
|
|
170
|
+
dump_request(logger, url, method, json, None, headers_with_user_agent, request_uuid)
|
|
171
|
+
|
|
172
|
+
# Check rate limits
|
|
173
|
+
self.check_rate_limit(path)
|
|
174
|
+
|
|
175
|
+
attempts = 0
|
|
176
|
+
while attempts < 5:
|
|
177
|
+
try:
|
|
178
|
+
self.refreshToken()
|
|
179
|
+
resp = requests.request(
|
|
180
|
+
method,
|
|
181
|
+
url,
|
|
182
|
+
json=json,
|
|
183
|
+
params=None,
|
|
184
|
+
headers=headers_with_user_agent,
|
|
185
|
+
stream=stream,
|
|
186
|
+
)
|
|
187
|
+
dump_response(
|
|
188
|
+
logger=logger,
|
|
189
|
+
url=url,
|
|
190
|
+
params=None,
|
|
191
|
+
method=method,
|
|
192
|
+
resp=resp,
|
|
193
|
+
request_uuid=request_uuid,
|
|
194
|
+
start_time=start_time,
|
|
195
|
+
)
|
|
196
|
+
if resp.status_code == 429:
|
|
197
|
+
retry_after = resp.headers.get("Retry-After")
|
|
198
|
+
if retry_after:
|
|
199
|
+
try:
|
|
200
|
+
sleep_time = int(retry_after)
|
|
201
|
+
except ValueError:
|
|
202
|
+
sleep_time = int(retry_after[:-1])
|
|
203
|
+
logger.warning(f"Rate limit exceeded. Retrying in {sleep_time} seconds.")
|
|
204
|
+
time.sleep(sleep_time)
|
|
205
|
+
else:
|
|
206
|
+
time.sleep(60)
|
|
207
|
+
attempts += 1
|
|
208
|
+
continue
|
|
209
|
+
else:
|
|
210
|
+
break
|
|
211
|
+
except requests.RequestException as e:
|
|
212
|
+
if attempts == 4:
|
|
213
|
+
logger.error(f"Failed to send {method} request to {url} after 5 attempts. Error: {str(e)}")
|
|
214
|
+
raise e
|
|
215
|
+
else:
|
|
216
|
+
logger.warning(f"Failed to send {method} request to {url}. Retrying... Error: {str(e)}")
|
|
217
|
+
attempts += 1
|
|
218
|
+
time.sleep(5)
|
|
219
|
+
|
|
220
|
+
return resp
|
|
221
|
+
|
|
222
|
+
def get(self, path, json=None, params=None, stream=False):
|
|
223
|
+
"""
|
|
224
|
+
Send a GET request to the ZCC API.
|
|
225
|
+
|
|
226
|
+
Parameters:
|
|
227
|
+
path (str): API endpoint path.
|
|
228
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
229
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
230
|
+
stream (bool, optional): Whether to stream the response content. Defaults to False.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Response: The response object from the API request.
|
|
234
|
+
"""
|
|
235
|
+
resp = self.send("GET", path, json, params, stream=stream)
|
|
236
|
+
if stream:
|
|
237
|
+
return resp
|
|
238
|
+
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
239
|
+
return formatted_resp
|
|
240
|
+
|
|
241
|
+
def post(self, path, json=None, params=None):
|
|
242
|
+
"""
|
|
243
|
+
Send a POST request to the ZCC API.
|
|
244
|
+
|
|
245
|
+
Parameters:
|
|
246
|
+
path (str): API endpoint path.
|
|
247
|
+
json (dict, optional): Request payload. Defaults to None.
|
|
248
|
+
params (dict, optional): Query parameters. Defaults to None.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
dict: Formatted JSON response from the API.
|
|
252
|
+
"""
|
|
253
|
+
resp = self.send("POST", path, json, params)
|
|
254
|
+
formatted_resp = format_json_response(resp, box_attrs=dict())
|
|
255
|
+
return formatted_resp
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def devices(self):
|
|
259
|
+
"""The interface object for the :ref:`ZCC Devices interface <zcc-devices>`."""
|
|
260
|
+
return DevicesAPI(self)
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def secrets(self):
|
|
264
|
+
"""The interface object for the :ref:`ZCC Secrets interface <zcc-secrets>`."""
|
|
265
|
+
return SecretsAPI(self)
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
class ZCCClient:
|
|
19
|
+
def __init__():
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def get(
|
|
23
|
+
self,
|
|
24
|
+
path: str,
|
|
25
|
+
json=None,
|
|
26
|
+
params=None,
|
|
27
|
+
stream=False,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Send a GET request to the ZCC API.
|
|
31
|
+
Parameters:
|
|
32
|
+
- path (str): API endpoint path.
|
|
33
|
+
- json (str): the request body.
|
|
34
|
+
- params (dict): the query params
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def get_paginated_data(
|
|
39
|
+
self,
|
|
40
|
+
path=None,
|
|
41
|
+
params=None,
|
|
42
|
+
search=None,
|
|
43
|
+
search_field="name",
|
|
44
|
+
page=None,
|
|
45
|
+
pagesize=20,
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Send a GET request to the ZCC API to fetch all pages of a resources.
|
|
49
|
+
Parameters:
|
|
50
|
+
- path (str): API endpoint path.
|
|
51
|
+
- data_key_name (str): list field key.
|
|
52
|
+
- data_per_page: the page size
|
|
53
|
+
- params (dict): the query params
|
|
54
|
+
"""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def post(self, path: str, json=None, params=None):
|
|
58
|
+
"""
|
|
59
|
+
Send a POST request to the ZCC API.
|
|
60
|
+
Parameters:
|
|
61
|
+
- path (str): API endpoint path.
|
|
62
|
+
- json (str): the request body.
|
|
63
|
+
- params (dict): the query params
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from box import BoxList
|
|
4
|
+
from zscaler.zcc.client import ZCCClient
|
|
5
|
+
from zscaler.utils import convert_keys, zcc_param_map
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DevicesAPI:
|
|
9
|
+
|
|
10
|
+
def __init__(self, client: ZCCClient):
|
|
11
|
+
self.rest = client
|
|
12
|
+
|
|
13
|
+
def download_devices(
|
|
14
|
+
self,
|
|
15
|
+
filename: str = None,
|
|
16
|
+
os_types: list = None,
|
|
17
|
+
registration_types: list = None,
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Downloads the list of devices in the Client Connector Portal as a CSV file.
|
|
21
|
+
|
|
22
|
+
By default, this method will create a file named `zcc-devices-YYmmDD-HH_MM_SS.csv`. This can be overridden by
|
|
23
|
+
specifying the ``filename`` argument.
|
|
24
|
+
|
|
25
|
+
Notes:
|
|
26
|
+
This API endpoint is heavily rate-limited by Zscaler and as of NOV 2022 only 3 calls per-day are allowed.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
filename (str):
|
|
30
|
+
The name of the file that you want to save to disk.
|
|
31
|
+
os_types (list):
|
|
32
|
+
A list of OS Types to filter the device list. Omitting this argument will result in all OS types being
|
|
33
|
+
matched.
|
|
34
|
+
Valid options are:
|
|
35
|
+
|
|
36
|
+
- ios
|
|
37
|
+
- android
|
|
38
|
+
- windows
|
|
39
|
+
- macos
|
|
40
|
+
- linux
|
|
41
|
+
registration_types (list):
|
|
42
|
+
A list of device registration states to filter the device list.
|
|
43
|
+
Valid options are:
|
|
44
|
+
|
|
45
|
+
- all (provides all states except for 'removed')
|
|
46
|
+
- registered
|
|
47
|
+
- removal_pending
|
|
48
|
+
- unregistered
|
|
49
|
+
- removed
|
|
50
|
+
- quarantined
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
:obj:`str`: The local filename for the CSV file that was downloaded.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
Create a CSV with all OS types and all registration types:
|
|
57
|
+
|
|
58
|
+
>>> zcc.devices.download_devices(registration_types=["all", "removed"])
|
|
59
|
+
|
|
60
|
+
Create a CSV for Windows and macOS devices that are in the `registered` state:
|
|
61
|
+
|
|
62
|
+
>>> zcc.devices.download_devices(os_types=["windows", "macos"],
|
|
63
|
+
... registration_types=["registered"])
|
|
64
|
+
|
|
65
|
+
Create a CSV with filename `unregistered.csv` for devices in the unregistered state:
|
|
66
|
+
|
|
67
|
+
>>> zcc.devices.download_devices(filename="unregistered.csv",
|
|
68
|
+
... registration_types=["unregistered"])
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
if not filename:
|
|
73
|
+
filename = f"zcc-devices-{datetime.now().strftime('%Y%m%d-%H_%M_%S')}.csv"
|
|
74
|
+
|
|
75
|
+
params = {}
|
|
76
|
+
|
|
77
|
+
# Simplify the os_type argument, raise an error if the user supplies the wrong one.
|
|
78
|
+
if os_types:
|
|
79
|
+
for item in os_types:
|
|
80
|
+
os_type = zcc_param_map["os"].get(item, None)
|
|
81
|
+
if os_type:
|
|
82
|
+
if "osTypes" not in params:
|
|
83
|
+
params["osTypes"] = str(os_type)
|
|
84
|
+
else:
|
|
85
|
+
params["osTypes"] += "," + str(os_type)
|
|
86
|
+
else:
|
|
87
|
+
raise ValueError("Invalid os_type specified. Check the pyZscaler documentation for valid os_type options.")
|
|
88
|
+
|
|
89
|
+
# Simplify the registration_type argument, raise an error if the user supplies the wrong one.
|
|
90
|
+
if registration_types:
|
|
91
|
+
for item in registration_types:
|
|
92
|
+
reg_type = zcc_param_map["reg_type"].get(item, None)
|
|
93
|
+
if reg_type:
|
|
94
|
+
if "registrationTypes" not in params:
|
|
95
|
+
params["registrationTypes"] = str(reg_type)
|
|
96
|
+
else:
|
|
97
|
+
params["registrationTypes"] += "," + str(reg_type)
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError(
|
|
100
|
+
"Invalid registration_type specified. Check the pyZscaler documentation for valid "
|
|
101
|
+
"registration_type options."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Create the local file and stream the device list csv to it
|
|
105
|
+
with self.rest.get("downloadDevices", params=params, stream=True) as r:
|
|
106
|
+
with open(filename, "wb") as f:
|
|
107
|
+
f.write(r.content)
|
|
108
|
+
|
|
109
|
+
return filename
|
|
110
|
+
|
|
111
|
+
def list_devices(self, **kwargs) -> BoxList:
|
|
112
|
+
"""
|
|
113
|
+
Returns the list of devices enrolled in the Client Connector Portal.
|
|
114
|
+
|
|
115
|
+
Keyword Args:
|
|
116
|
+
os_type (str):
|
|
117
|
+
Filter by device operating system. Valid options are:
|
|
118
|
+
|
|
119
|
+
- ios
|
|
120
|
+
- android
|
|
121
|
+
- windows
|
|
122
|
+
- macos
|
|
123
|
+
- linux
|
|
124
|
+
page (int):
|
|
125
|
+
Return a specific page number.
|
|
126
|
+
page_size (int):
|
|
127
|
+
Specify the number of devices per page, defaults to ``30``.
|
|
128
|
+
user_name (str):
|
|
129
|
+
Filter by the enrolled user for the device.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
:obj:`BoxList`: A list containing devices using ZCC enrolled in the Client Connector Portal.
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
Prints all devices in the Client Connector Portal to the console:
|
|
136
|
+
|
|
137
|
+
>>> for device in zcc.devices.list_devices():
|
|
138
|
+
... print(device)
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
payload = convert_keys(dict(kwargs))
|
|
142
|
+
|
|
143
|
+
# Simplify the os_type argument, raise an error if the user supplies the wrong one.
|
|
144
|
+
if kwargs.get("os_type"):
|
|
145
|
+
os_type = zcc_param_map["os"].get(payload["osType"], None)
|
|
146
|
+
if os_type:
|
|
147
|
+
payload["osType"] = os_type
|
|
148
|
+
else:
|
|
149
|
+
raise ValueError("Invalid os_type specified. Check the Zscaler documentation for valid os_type options.")
|
|
150
|
+
|
|
151
|
+
return self.rest.get("getDevices", **payload)
|
|
152
|
+
|
|
153
|
+
def remove_devices(self, force: bool = False, **kwargs):
|
|
154
|
+
"""
|
|
155
|
+
Removes the specified devices from the Zscaler Client Connector Portal.
|
|
156
|
+
|
|
157
|
+
Notes:
|
|
158
|
+
You must be using API credentials with the `Write` role.
|
|
159
|
+
You must specify at least one criterion from `Keyword Args` to remove devices.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
force (bool):
|
|
163
|
+
Setting force to ``True`` removes the enrolled device from the portal. You can only remove devices that
|
|
164
|
+
are in the `registered` or `device removal pending` state.
|
|
165
|
+
**kwargs:
|
|
166
|
+
Optional keyword args.
|
|
167
|
+
|
|
168
|
+
Keyword Args:
|
|
169
|
+
client_connector_version (list):
|
|
170
|
+
A list of client connector versions that will be removed. You must supply the exact version number, i.e.
|
|
171
|
+
if the Client Connector version is `3.2.0.18` you must specify `3.2.0.18` and not `3.2`.
|
|
172
|
+
os_type (str):
|
|
173
|
+
The OS Type for the devices to be removed. Valid options are:
|
|
174
|
+
|
|
175
|
+
- ios
|
|
176
|
+
- android
|
|
177
|
+
- windows
|
|
178
|
+
- macos
|
|
179
|
+
- linux
|
|
180
|
+
udids (list):
|
|
181
|
+
A list of Unique Device IDs.
|
|
182
|
+
user_name (str):
|
|
183
|
+
The username of the user whose devices will be removed.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
:obj:`Box`: Server response containing the total number of devices removed.
|
|
187
|
+
|
|
188
|
+
Examples:
|
|
189
|
+
Soft-remove devices using ZCC version 3.7.1.44 from the Client Connector Portal:
|
|
190
|
+
|
|
191
|
+
>>> zcc.devices.remove_devices(client_connector_version=["3.7.1.44"])
|
|
192
|
+
|
|
193
|
+
Soft-remove Android devices from the Client Connector Portal:
|
|
194
|
+
|
|
195
|
+
>>> zcc.devices.remove_devices(os_type="android")
|
|
196
|
+
|
|
197
|
+
Hard-remove devices from the Client Connector Portal by UDID:
|
|
198
|
+
|
|
199
|
+
>>> zcc.devices.remove_devices(force=True, udids=["99999", "88888", "77777"])
|
|
200
|
+
|
|
201
|
+
Hard-remove Android devices for johnno@widgets.co from the Client Connector Portal:
|
|
202
|
+
|
|
203
|
+
>>> zcc.devices.remove_devices(force=True, os_type="android",
|
|
204
|
+
... user_name="johnno@widgets.co")
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
payload = convert_keys(dict(kwargs))
|
|
208
|
+
|
|
209
|
+
# Simplify the os_type argument, raise an error if the user supplies the wrong one.
|
|
210
|
+
if kwargs.get("os_type"):
|
|
211
|
+
os_type = zcc_param_map["os"].get(payload["osType"], None)
|
|
212
|
+
if os_type:
|
|
213
|
+
payload["osType"] = os_type
|
|
214
|
+
else:
|
|
215
|
+
raise ValueError("Invalid os_type specified. Check the pyZscaler documentation for valid os_type options.")
|
|
216
|
+
|
|
217
|
+
if force:
|
|
218
|
+
return self.rest.post("forceRemoveDevices", json=payload)
|
|
219
|
+
else:
|
|
220
|
+
return self.rest.post("removeDevices", json=payload)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from zscaler.zcc.client import ZCCClient
|
|
2
|
+
from zscaler.utils import zcc_param_map
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SecretsAPI:
|
|
6
|
+
def __init__(self, client: ZCCClient):
|
|
7
|
+
self.rest = client
|
|
8
|
+
|
|
9
|
+
def get_otp(self, device_id: str):
|
|
10
|
+
"""
|
|
11
|
+
Returns the OTP code for the specified device id.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
device_id (str): The unique id for the enrolled device that the OTP will be obtained for.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
:obj:`Box`: A dictionary containing the requested OTP code for the specified device id.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
Obtain the OTP code for a device and print it to console:
|
|
21
|
+
|
|
22
|
+
>>> otp_code = zcc.secrets.get_otp('System-Serial-Number:1234ABCDEF')
|
|
23
|
+
... print(otp_code.otp)
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
payload = {"udid": device_id}
|
|
28
|
+
|
|
29
|
+
return self.rest.get("getOtp", params=payload)
|
|
30
|
+
|
|
31
|
+
def get_passwords(self, username: str, os_type: str = "windows"):
|
|
32
|
+
"""
|
|
33
|
+
Return passwords for the specified username and device OS type.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
username (str): The username that the device belongs to.
|
|
37
|
+
os_type (str): The OS Type for the device, defaults to `windows`. Valid options are:
|
|
38
|
+
|
|
39
|
+
- ios
|
|
40
|
+
- android
|
|
41
|
+
- windows
|
|
42
|
+
- macos
|
|
43
|
+
- linux
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
:obj:`Box`: Dictionary containing passwords for the specified username's device.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
Print macOS device passwords for username test@example.com:
|
|
50
|
+
|
|
51
|
+
>>> print(zcc.secrets.get_passwords(username='test@example.com',
|
|
52
|
+
... os_type='macos'))
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# Simplify the os_type argument, raise an error if the user supplies the wrong one.
|
|
57
|
+
os_type = zcc_param_map["os"].get(os_type, None)
|
|
58
|
+
if not os_type:
|
|
59
|
+
raise ValueError("Invalid os_type specified. Check the pyZscaler documentation for valid os_type options.")
|
|
60
|
+
|
|
61
|
+
params = {
|
|
62
|
+
"username": username,
|
|
63
|
+
"osType": os_type,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return self.rest.get("getPasswords", params=params)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zcon/admin_and_role_management.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/admin_and_role_management.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zia/authentication_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/app_segments_inspection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{zscaler_sdk_python-0.9.0 → zscaler_sdk_python-0.9.1}/zscaler/zpa/privileged_remote_access.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|