Skip to content

Commit

Permalink
Merge branch 'release/1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchos committed May 14, 2022
2 parents 77cf2ff + 48dc787 commit 4197cf8
Show file tree
Hide file tree
Showing 26 changed files with 902 additions and 12 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ by Steve McGrath.
## Products
- Zscaler Private Access (ZPA)
- Zscaler Internet Access (ZIA)
- Zscaler Mobile Admin Portal for Zscaler Client Connector (ZCC)
- Cloud Security Posture Management (CSPM) - (work in progress)


Expand Down Expand Up @@ -72,6 +73,18 @@ for app_segment in zpa.app_segments.list_segments():
pprint(app_segment)
```

### Quick ZCC Example

```python
from pyzscaler import ZCC
from pprint import pprint

zcc = ZCC(client_id='CLIENT_ID', client_secret='CLIENT_SECRET', company_id='COMPANY_ID')
for device in zcc.devices.list_devices():
pprint(device)
```


## Documentation
### API Documentation
pyZscaler's API is fully 100% documented and is hosted at [ReadTheDocs](https://pyzscaler.readthedocs.io).
Expand Down
4 changes: 2 additions & 2 deletions docsrc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
html_title = ""

# The short X.Y version
version = '1.1'
version = '1.2'
# The full version, including alpha/beta/rc tags
release = '1.1.1'
release = '1.2.0'

# -- General configuration ---------------------------------------------------

Expand Down
16 changes: 16 additions & 0 deletions docsrc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

zs/zia/index
zs/zpa/index
zs/zcc/index

pyZscaler SDK - Library Reference
=====================================================================
Expand Down Expand Up @@ -39,6 +40,7 @@ Products
---------
- :doc:`Zscaler Private Access (ZPA) <zs/zpa/index>`
- :doc:`Zscaler Internet Access (ZIA) <zs/zia/index>`
- :doc:`Zscaler Mobile Admin Portal <zs/zcc/index>`
- Cloud Security Posture Management (CSPM) - (work in progress)

Installation
Expand Down Expand Up @@ -83,6 +85,20 @@ Quick ZPA Example
for app_segment in zpa.app_segments.list_segments():
pprint(app_segment)
Quick ZCC Example
^^^^^^^^^^^^^^^^^^^

.. code-block:: python
from pyzscaler import ZCC
from pprint import pprint
zcc = ZCC(client_id='CLIENT_ID', client_secret='CLIENT_SECRET', company_id='COMPANY_ID)
for device in zcc.devices.list_devices():
pprint(device)
.. automodule:: pyzscaler
:members:
Expand Down
12 changes: 12 additions & 0 deletions docsrc/zs/zcc/devices.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
devices
--------------

The following methods allow for interaction with the ZCC
Devices API endpoints.

Methods are accessible via ``zcc.devices``

.. _zcc-devices:

.. automodule:: pyzscaler.zcc.devices
:members:
26 changes: 26 additions & 0 deletions docsrc/zs/zcc/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ZCC
==========
This package covers the ZCC interface.

Retrieving the ZCC Company ID.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ZCC Company ID can be obtained by following these instructions:
1. Navigate to the Zscaler Mobile Admin Portal in a web browser.
2. Open the Browser console (typically ``F12``) and click on **Network**.
3. From the top navigation, click on **Enrolled Devices**.
4. Look for the API call ``mobileadmin.zscaler.net/webservice/api/web/usersByCompany`` in the 'Networks' tab
of the Browser Console. Click on this entry.
5. Click on either **Preview** or **Response** to see the data that was returned by the Mobile Admin Portal.
6. The Company ID is represented as an ``int`` and can be found under the ``companyId`` key in the object returned
for each user.

.. toctree::
:maxdepth: 1
:glob:
:hidden:

*

.. automodule:: pyzscaler.zcc
:members:
11 changes: 11 additions & 0 deletions docsrc/zs/zcc/secrets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
secrets
--------------

The following methods allow for interaction with the ZCC API endpoints for managing secrets.

Methods are accessible via ``zcc.secrets``

.. _zcc-secrets:

.. automodule:: pyzscaler.zcc.secrets
:members:
12 changes: 12 additions & 0 deletions docsrc/zs/zcc/session.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
session
--------------

The following methods allow for interaction with the ZCC
Session API endpoints.

Methods are accessible via ``zcc.session``

.. _zcc-session:

.. automodule:: pyzscaler.zcc.session
:members:
1 change: 1 addition & 0 deletions docsrc/zs/zia/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This package covers the ZIA interface.
dlp
firewall
locations
rule_labels
sandbox
security
session
Expand Down
12 changes: 12 additions & 0 deletions docsrc/zs/zia/rule_labels.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
labels
-------------

The following methods allow for interaction with the ZIA
Rule Labels API endpoints.

Methods are accessible via ``zia.labels``

.. _zia-labels:

.. automodule:: pyzscaler.zia.labels
:members:
10 changes: 10 additions & 0 deletions docsrc/zs/zia/url_categories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ The following methods allow for interaction with the ZIA URL Categories API endp

Methods are accessible via ``zia.url_categories``


**Guidelines for adding / updating URLs**


- The URL must use a standard URI format.
- The URL length cannot exceed 1024 characters.
- The URL cannot contain non-ASCII characters.
- The domain name before the colon (:) cannot exceed 255 characters.
- The domain name between periods (.) cannot exceed 63 characters.

.. _zia-url_categories:

.. automodule:: pyzscaler.zia.url_categories
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyzscaler"
version = "1.1.1"
version = "1.2.0"
description = "A python SDK for the Zscaler API."
authors = ["Mitch Kelly <me@mkelly.dev>"]
license = "MIT"
Expand Down
3 changes: 2 additions & 1 deletion pyzscaler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"Dax Mickelson",
"Jacob Gårder",
]
__version__ = "1.1.1"
__version__ = "1.2.0"

from pyzscaler.zcc import ZCC # noqa
from pyzscaler.zia import ZIA # noqa
from pyzscaler.zpa import ZPA # noqa
6 changes: 6 additions & 0 deletions pyzscaler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ def snake_to_camel(name: str):
return ret


def chunker(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i : i + n]


# Recursive function to convert all keys and nested keys from snake case
# to camel case.
def convert_keys(data):
Expand Down
64 changes: 64 additions & 0 deletions pyzscaler/zcc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import os

from box import Box
from restfly.session import APISession

from pyzscaler import __version__

from .devices import DevicesAPI
from .secrets import SecretsAPI
from .session import AuthenticatedSessionAPI


class ZCC(APISession):
"""
A Controller to access Endpoints in the Zscaler Mobile Admin Portal API.
The ZCC object stores the session token and simplifies access to CRUD options within the ZCC Portal.
Attributes:
client_id (str): The ZCC Client ID generated from the ZCC Portal.
client_secret (str): The ZCC Client Secret generated from the ZCC Portal.
company_id (str):
The ZCC Company ID. There seems to be no easy way to obtain this at present. See the note
at the top of this page for information on how to retrieve the Company ID.
"""

_vendor = "Zscaler"
_product = "Zscaler Mobile Admin Portal"
_backoff = 3
_build = __version__
_box = True
_box_attrs = {"camel_killer_box": True}
_env_base = "ZCC"
_env_cloud = "zscaler"
_url = "https://api-mobile.zscaler.net/papi"

def __init__(self, **kw):
self._client_id = kw.get("client_id", os.getenv(f"{self._env_base}_CLIENT_ID"))
self._client_secret = kw.get("client_secret", os.getenv(f"{self._env_base}_CLIENT_SECRET"))
self.company_id = kw.get("company_id", os.getenv(f"{self._env_base}_COMPANY_ID"))
self.conv_box = True
super(ZCC, self).__init__(**kw)

def _build_session(self, **kwargs) -> Box:
"""Creates a ZCC API session."""
super(ZCC, self)._build_session(**kwargs)
self._auth_token = self.session.create_token(client_id=self._client_id, client_secret=self._client_secret)
return self._session.headers.update({"auth-token": f"{self._auth_token}"})

@property
def devices(self):
"""The interface object for the :ref:`ZCC Devices interface <zcc-devices>`."""
return DevicesAPI(self)

@property
def secrets(self):
"""The interface object for the :ref:`ZCC Secrets interface <zcc-secrets>`."""
return SecretsAPI(self)

@property
def session(self):
"""The interface object for the :ref:`ZCC Authenticated Session interface <zcc-session>`."""
return AuthenticatedSessionAPI(self)
27 changes: 27 additions & 0 deletions pyzscaler/zcc/devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from box import BoxList
from restfly import APISession
from restfly.endpoint import APIEndpoint


class DevicesAPI(APIEndpoint):
def __init__(self, api: APISession):
super().__init__(api)
self.company_id = api.company_id

def list_devices(self) -> BoxList:
"""
Returns the list of devices enrolled in the Mobile Admin Portal.
Returns:
:obj:`BoxList`: A list containing devices using ZCC enrolled in the Mobile Admin Portal.
Examples:
Prints all devices in the Mobile Admin Portal to the console:
>>> for device in zcc.devices.list_devices():
... print(device)
"""
payload = {"companyId": self.company_id}

return self._get("public/v1/getDevices", json=payload)
79 changes: 79 additions & 0 deletions pyzscaler/zcc/secrets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from restfly import APISession
from restfly.endpoint import APIEndpoint


class SecretsAPI(APIEndpoint):
os_map = {
"ios": 1,
"android": 2,
"windows": 3,
"macos": 4,
"linux": 5,
}

def __init__(self, api: APISession):
super().__init__(api)
self.company_id = api.company_id

def get_otp(self, device_id: str):
"""
Returns the OTP code for the specified device id.
Args:
device_id (str): The unique id for the enrolled device that the OTP will be obtained for.
Returns:
:obj:`Box`: A dictionary containing the requested OTP code for the specified device id.
Examples:
Obtain the OTP code for a device and print it to console:
>>> otp_code = zcc.secrets.get_otp('System-Serial-Number:1234ABCDEF')
... print(otp_code.otp)
"""

payload = {"udid": device_id}

return self._get("public/v1/getOtp", params=payload)

def get_passwords(self, username: str, os_type: str = "windows"):
"""
Return passwords for the specified username and device OS type.
Args:
username (str): The username that the device belongs to.
os_type (str): The OS Type for the device, defaults to `windows`. Valid options are:
- ios
- android
- windows
- macos
- linux
Returns:
:obj:`Box`: Dictionary containing passwords for the specified username's device.
Examples:
Print macos device passwords for username test@example.com:
>>> print(zcc.secrets.get_passwords(username='test@example.com',
... os_type='macos'))
"""

payload = {
"companyId": self.company_id,
}

# Simplify the os_type argument, raise an error if the user supplies the wrong one.
os_type = self.os_map.get(os_type, None)
if not os_type:
raise ValueError("Invalid os_type specified. Check the pyZscaler documentation for valid os_type options.")

params = {
"username": username,
"osType": os_type,
}

return self._get("public/v1/getPasswords", data=payload, params=params)
Loading

0 comments on commit 4197cf8

Please sign in to comment.