├── .flake8
├── .github
├── ISSUE_TEMPLATE
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── bug_report.md
└── workflows
│ └── build.yml
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── dev_requirements.txt
├── docs
├── .buildinfo
├── .nojekyll
├── _sources
│ ├── index.rst.txt
│ └── zs
│ │ ├── zia
│ │ ├── admins.rst.txt
│ │ ├── audit_logs.rst.txt
│ │ ├── config.rst.txt
│ │ ├── index.rst.txt
│ │ ├── locations.rst.txt
│ │ ├── traffic.rst.txt
│ │ └── users.rst.txt
│ │ └── zpa
│ │ ├── app_segments.rst.txt
│ │ ├── certificates.rst.txt
│ │ ├── cloud_connector_groups.rst.txt
│ │ ├── connector_groups.rst.txt
│ │ ├── idp.rst.txt
│ │ ├── index.rst.txt
│ │ ├── machine_groups.rst.txt
│ │ ├── policies.rst.txt
│ │ ├── posture_profiles.rst.txt
│ │ ├── saml_attributes.rst.txt
│ │ ├── scim_attributes.rst.txt
│ │ ├── scim_groups.rst.txt
│ │ ├── segment_groups.rst.txt
│ │ ├── server_groups.rst.txt
│ │ ├── servers.rst.txt
│ │ └── trusted_networks.rst.txt
├── _static
│ ├── alabaster.css
│ ├── basic.css
│ ├── css
│ │ ├── badge_only.css
│ │ ├── fonts
│ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ ├── fontawesome-webfont.woff2
│ │ │ ├── lato-bold-italic.woff
│ │ │ ├── lato-bold-italic.woff2
│ │ │ ├── lato-bold.woff
│ │ │ ├── lato-bold.woff2
│ │ │ ├── lato-normal-italic.woff
│ │ │ ├── lato-normal-italic.woff2
│ │ │ ├── lato-normal.woff
│ │ │ └── lato-normal.woff2
│ │ └── theme.css
│ ├── custom.css
│ ├── doctools.js
│ ├── documentation_options.js
│ ├── file.png
│ ├── fonts
│ │ ├── Inconsolata-Bold.ttf
│ │ ├── Inconsolata-Regular.ttf
│ │ ├── Inconsolata.ttf
│ │ ├── Lato-Bold.ttf
│ │ ├── Lato-Regular.ttf
│ │ ├── Lato
│ │ │ ├── lato-bold.eot
│ │ │ ├── lato-bold.ttf
│ │ │ ├── lato-bold.woff
│ │ │ ├── lato-bold.woff2
│ │ │ ├── lato-bolditalic.eot
│ │ │ ├── lato-bolditalic.ttf
│ │ │ ├── lato-bolditalic.woff
│ │ │ ├── lato-bolditalic.woff2
│ │ │ ├── lato-italic.eot
│ │ │ ├── lato-italic.ttf
│ │ │ ├── lato-italic.woff
│ │ │ ├── lato-italic.woff2
│ │ │ ├── lato-regular.eot
│ │ │ ├── lato-regular.ttf
│ │ │ ├── lato-regular.woff
│ │ │ └── lato-regular.woff2
│ │ ├── RobotoSlab-Bold.ttf
│ │ ├── RobotoSlab-Regular.ttf
│ │ ├── RobotoSlab
│ │ │ ├── roboto-slab-v7-bold.eot
│ │ │ ├── roboto-slab-v7-bold.ttf
│ │ │ ├── roboto-slab-v7-bold.woff
│ │ │ ├── roboto-slab-v7-bold.woff2
│ │ │ ├── roboto-slab-v7-regular.eot
│ │ │ ├── roboto-slab-v7-regular.ttf
│ │ │ ├── roboto-slab-v7-regular.woff
│ │ │ └── roboto-slab-v7-regular.woff2
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ ├── jquery-3.5.1.js
│ ├── jquery.js
│ ├── js
│ │ ├── badge_only.js
│ │ ├── html5shiv-printshiv.min.js
│ │ ├── html5shiv.min.js
│ │ ├── modernizr.min.js
│ │ └── theme.js
│ ├── language_data.js
│ ├── minus.png
│ ├── plus.png
│ ├── pygments.css
│ ├── searchtools.js
│ ├── underscore-1.13.1.js
│ └── underscore.js
├── genindex.html
├── google38a519c1999beafe.html
├── index.html
├── objects.inv
├── py-modindex.html
├── search.html
├── searchindex.js
└── zs
│ ├── zia
│ ├── admins.html
│ ├── audit_logs.html
│ ├── config.html
│ ├── index.html
│ ├── locations.html
│ ├── traffic.html
│ └── users.html
│ └── zpa
│ ├── app_segments.html
│ ├── certificates.html
│ ├── cloud_connector_groups.html
│ ├── connector_groups.html
│ ├── idp.html
│ ├── index.html
│ ├── machine_groups.html
│ ├── policies.html
│ ├── posture_profiles.html
│ ├── saml_attributes.html
│ ├── scim_attributes.html
│ ├── scim_groups.html
│ ├── segment_groups.html
│ ├── server_groups.html
│ ├── servers.html
│ └── trusted_networks.html
├── docsrc
├── Makefile
├── conf.py
├── index.rst
├── logo.svg
└── zs
│ ├── zcc
│ ├── devices.rst
│ ├── index.rst
│ ├── secrets.rst
│ └── session.rst
│ ├── zcon
│ ├── admin.rst
│ ├── config.rst
│ ├── connectors.rst
│ ├── index.rst
│ ├── locations.rst
│ └── session.rst
│ ├── zdx
│ ├── admin.rst
│ ├── apps.rst
│ ├── devices.rst
│ ├── index.rst
│ ├── session.rst
│ └── users.rst
│ ├── zia
│ ├── admin_and_role_management.rst
│ ├── apptotal.rst
│ ├── audit_logs.rst
│ ├── cloud_apps.rst
│ ├── config.rst
│ ├── dlp.rst
│ ├── firewall.rst
│ ├── index.rst
│ ├── locations.rst
│ ├── rule_labels.rst
│ ├── sandbox.rst
│ ├── security.rst
│ ├── session.rst
│ ├── ssl_inspection.rst
│ ├── traffic.rst
│ ├── url_categories.rst
│ ├── url_filters.rst
│ ├── users.rst
│ ├── vips.rst
│ └── web_dlp.rst
│ └── zpa
│ ├── app_segments.rst
│ ├── certificates.rst
│ ├── cloud_connector_groups.rst
│ ├── connectors.rst
│ ├── idp.rst
│ ├── index.rst
│ ├── inspection.rst
│ ├── lss.rst
│ ├── machine_groups.rst
│ ├── policies.rst
│ ├── posture_profiles.rst
│ ├── provisioning.rst
│ ├── saml_attributes.rst
│ ├── scim_attributes.rst
│ ├── scim_groups.rst
│ ├── segment_groups.rst
│ ├── server_groups.rst
│ ├── servers.rst
│ ├── service_edges.rst
│ ├── session.rst
│ └── trusted_networks.rst
├── examples
└── zia
│ └── download_audit_logs
│ └── zia_download_audit_logs.py
├── pyproject.toml
├── pyzscaler
├── __init__.py
├── utils.py
├── zcc
│ ├── __init__.py
│ ├── devices.py
│ ├── secrets.py
│ └── session.py
├── zcon
│ ├── __init__.py
│ ├── admin.py
│ ├── config.py
│ ├── connectors.py
│ ├── locations.py
│ └── session.py
├── zdx
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── devices.py
│ ├── session.py
│ └── users.py
├── zia
│ ├── __init__.py
│ ├── admin_and_role_management.py
│ ├── apptotal.py
│ ├── audit_logs.py
│ ├── cloud_apps.py
│ ├── config.py
│ ├── dlp.py
│ ├── firewall.py
│ ├── labels.py
│ ├── locations.py
│ ├── sandbox.py
│ ├── security.py
│ ├── session.py
│ ├── ssl_inspection.py
│ ├── traffic.py
│ ├── url_categories.py
│ ├── url_filters.py
│ ├── users.py
│ ├── vips.py
│ └── web_dlp.py
└── zpa
│ ├── __init__.py
│ ├── app_segments.py
│ ├── certificates.py
│ ├── cloud_connector_groups.py
│ ├── connectors.py
│ ├── idp.py
│ ├── inspection.py
│ ├── lss.py
│ ├── machine_groups.py
│ ├── policies.py
│ ├── posture_profiles.py
│ ├── provisioning.py
│ ├── saml_attributes.py
│ ├── scim_attributes.py
│ ├── scim_groups.py
│ ├── segment_groups.py
│ ├── server_groups.py
│ ├── servers.py
│ ├── service_edges.py
│ ├── session.py
│ └── trusted_networks.py
├── requirements.txt
└── tests
├── __init__.py
├── conftest.py
├── test_pyzscaler.py
├── test_utils.py
├── zcc
├── conftest.py
├── test_zcc_devices.py
├── test_zcc_secrets.py
└── test_zcc_session.py
├── zcon
├── conftest.py
├── test_zcon_admin.py
├── test_zcon_config.py
├── test_zcon_connectors.py
├── test_zcon_locations.py
└── test_zcon_session.py
├── zdx
├── conftest.py
├── test_zdx_admin.py
├── test_zdx_apps.py
├── test_zdx_devices.py
├── test_zdx_session.py
└── test_zdx_users.py
├── zia
├── conftest.py
├── test_admin_and_role_management.py
├── test_apptotal.py
├── test_audit_logs.py
├── test_cloud_apps.py
├── test_config.py
├── test_dlp.py
├── test_firewall.py
├── test_init.py
├── test_locations.py
├── test_rule_labels.py
├── test_sandbox.py
├── test_security.py
├── test_session.py
├── test_ssl_inspection.py
├── test_traffic.py
├── test_url_categories.py
├── test_url_filters.py
├── test_users.py
├── test_vips.py
└── test_web_dlp.py
└── zpa
├── conftest.py
├── test_zpa_app_segments.py
├── test_zpa_certificates.py
├── test_zpa_cloud_connector_groups.py
├── test_zpa_connectors.py
├── test_zpa_idp.py
├── test_zpa_inspection.py
├── test_zpa_lss.py
├── test_zpa_machine_groups.py
├── test_zpa_policies.py
├── test_zpa_posture_profiles.py
├── test_zpa_provisioning.py
├── test_zpa_saml_attributes.py
├── test_zpa_scim_attributes.py
├── test_zpa_scim_groups.py
├── test_zpa_segment_groups.py
├── test_zpa_server_groups.py
├── test_zpa_servers.py
├── test_zpa_service_edges.py
├── test_zpa_session.py
└── test_zpa_trusted_networks.py
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 127
3 | max-doc-length = 127
4 | extend_ignore = E203, W503, W605, F841
5 | exclude = build,dist,.git,docs,docsrc,examples,.venv,venv
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Proposed changes
2 |
3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
4 | If it fixes a bug or resolves a feature request, be sure to link to that issue.
5 |
6 | ## Types of changes
7 |
8 | What types of changes does your code introduce to pyZscaler?
9 | _Put an `x` in the boxes that apply_
10 |
11 | - [ ] Bugfix (non-breaking change which fixes an issue)
12 | - [ ] New feature (non-breaking change which adds functionality)
13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
14 | - [ ] Documentation Update (if none of the other choices apply)
15 |
16 | ## Checklist
17 |
18 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of
19 | them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before
20 | merging your code._
21 |
22 | - [ ] I have read the [CONTRIBUTING](https://github.com/pyZscaler/CONTRIBUTING.md) doc
23 | - [ ] Lint and unit tests pass locally with my changes
24 | - [ ] I have added tests that prove my fix is effective or that my feature works
25 | - [ ] I have added necessary documentation (if appropriate)
26 | - [ ] Any dependent changes have been merged and published in downstream modules
27 |
28 | ## Further comments
29 |
30 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you
31 | did and what alternatives you considered, etc...
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve pyZscaler
4 | title: "[BUG]: Short title of the bug"
5 | labels: bug
6 | assignees: mitchos
7 |
8 | ---
9 |
10 | Describe the bug
11 | ----------------------
12 | A clear and concise description of what the bug is.
13 |
14 | **To Reproduce**
15 | Steps to reproduce the behavior:
16 | 1. Go to '...'
17 | 2. Click on '....'
18 | 3. Scroll down to '....'
19 | 4. See error
20 |
21 | Expected behavior
22 | ------------------------
23 | A clear and concise description of what you expected to happen.
24 |
25 | Screenshots
26 | ----------------
27 | If applicable, add screenshots to help explain your problem.
28 |
29 | **Desktop (please complete the following information):**
30 | - OS: [e.g. iOS]
31 | - Browser [e.g. chrome, safari]
32 | - Version [e.g. 22]
33 |
34 | Additional context
35 | -------------------------
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches: [ main, develop ]
6 | paths-ignore:
7 | - '.github/**'
8 | - 'docs/**'
9 | - 'docsrc/**'
10 | - 'examples/**'
11 | pull_request:
12 | branches: [ main, develop ]
13 | paths-ignore:
14 | - '.github/**'
15 | - 'docs/**'
16 | - 'docsrc/**'
17 | - 'examples/**'
18 | workflow_dispatch:
19 |
20 | jobs:
21 | build:
22 |
23 | environment:
24 | name: test
25 | runs-on: ubuntu-latest
26 | strategy:
27 | fail-fast: false
28 | matrix:
29 | python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
30 |
31 | steps:
32 | - uses: actions/checkout@v2
33 | - name: Set up Python ${{ matrix.python-version }}
34 | uses: actions/setup-python@v2
35 | with:
36 | python-version: ${{ matrix.python-version }}
37 | - name: Install dependencies
38 | run: |
39 | python -m pip install --upgrade pip
40 | pip install flake8 pytest
41 | pip install pytest-cov
42 | pip install -r dev_requirements.txt
43 |
44 | - name: Lint with flake8
45 | run: |
46 | # stop the build if there are Python syntax errors or undefined names
47 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
48 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
49 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
50 | - name: Test with pytest
51 | run: |
52 | pytest
53 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | exclude: ^(docsrc/|docs/|examples/)
2 | repos:
3 | - repo: https://github.com/psf/black
4 | rev: 23.3.0
5 | hooks:
6 | - id: black
7 | language_version: python3.12
8 |
9 | - repo: https://github.com/PyCQA/flake8
10 | rev: 6.0.0
11 | hooks:
12 | - id: flake8
13 |
14 | - repo: https://github.com/PyCQA/isort
15 | rev: 5.12.0
16 | hooks:
17 | - id: isort
18 | args: [--profile, black, --skip, docsrc, --skip, examples, --filter-files]
19 | types: [python]
20 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 |
2 | # Read the Docs configuration file for Sphinx projects
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS and Python version
8 | build:
9 | os: ubuntu-22.04
10 | tools:
11 | python: "3.12"
12 |
13 | # Build documentation in the "docsrc/" directory with Sphinx
14 | sphinx:
15 | configuration: docsrc/conf.py
16 |
17 | # pyZscaler doc build requirements
18 | python:
19 | install:
20 | - requirements: dev_requirements.txt
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to pyZscaler
2 |
3 | I'd love your input! I want to make contributing to this project as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 | - Becoming a maintainer
10 |
11 | ## I Develop with Github
12 |
13 | I use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
14 |
15 | ## I Use GitFlow for Releases, So All Code Changes Happen Through Pull Requests in Develop
16 |
17 | Pull requests are the best way to propose changes to the codebase. Pull requests are welcome:
18 |
19 | 1. Fork the repo and create your branch from `develop`.
20 | 2. If you've added code that should be tested, add tests.
21 | 3. If you've changed APIs, update the documentation.
22 | 4. Ensure the test suite passes.
23 | 5. Make sure your code lints.
24 | 6. Issue that pull request!
25 |
26 | Once your change has been merged into `develop`, it will be bundled with the next batch of changes into the next release,
27 | which is merged into main.
28 |
29 | ## Keep Pull Requests Short and Specific
30 |
31 | It takes a lot of work to review a PR that covers multiple issues or features. Breaking
32 | your PR up into smaller requests is greatly appreciated. Doing this ensures quick review
33 | and PRs that require additional work won't hold up other work.
34 |
35 | If there is one thing you do, please do this.
36 |
37 | ## I recommend Conventional Commits
38 |
39 | No system is perfect, but I've found [Conventional Commits](https://www.conventionalcommits.org/) to work pretty well for myself. At the very
40 | least check it out and see if it works for you too.
41 |
42 | ## Any contributions you make will be under the MIT Software License
43 |
44 | In short, when you submit code changes, your submissions are understood to be under the
45 | same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the
46 | maintainers if that's a concern.
47 |
48 | ## Report bugs using Github's [issues](https://github.com/mitchos/pyZscaler/issues)
49 |
50 | I use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/mitchos/pyzscaler/issues/new); it's that easy!
51 |
52 | ## Write bug reports with detail, background, and sample code
53 |
54 | **Great Bug Reports** tend to have:
55 |
56 | - A quick summary and/or background
57 | - Steps to reproduce
58 | - Be specific!
59 | - Give sample code if you can.
60 | - What you expected would happen
61 | - What actually happens
62 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
63 |
64 | People *love* thorough bug reports.
65 |
66 | ## Use a Consistent Coding Style
67 |
68 | - [Black](https://github.com/psf/black) is used for formatting in this project with line-length 127.
69 |
70 | - [Flake8](https://flake8.pycqa.org) is used for linting.
71 |
72 | ## License
73 |
74 | By contributing, you agree that your contributions will be licensed under the MIT License.
75 |
76 | ## References
77 |
78 | This document was adapted from the open-source contribution guidelines
79 | for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
80 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mitch Kelly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/mitchos/pyZscaler)
2 | # pyZscaler - An unofficial SDK for the Zscaler API
3 |
4 | [](https://github.com/mitchos/pyZscaler/actions/workflows/build.yml)
5 | [](https://pyzscaler.readthedocs.io/?badge=latest)
6 | [](https://github.com/mitchos/pyZscaler)
7 | [](https://www.codacy.com/gh/mitchos/pyZscaler/dashboard?utm_source=github.com&utm_medium=referral&utm_content=mitchos/pyZscaler&utm_campaign=Badge_Grade)
8 | [](https://pypi.org/project/pyZscaler)
9 | [](https://pypi.python.org/pypi/pyzscaler/)
10 | [](https://github.com/mitchos/pyZscaler/releases/)
11 |
12 | # [DEPRECATED] Unofficial SDK
13 |
14 | **This SDK is no longer maintained.**
15 | Its features and contributions have been merged into the [Official Zscaler Python SDK](https://github.com/zscaler/zscaler-sdk-python). Please refer to the official repository for all future development, documentation, and support.
16 |
17 | Please see [the pyZscaler deprecation notice](https://github.com/mitchos/pyZscaler/discussions/306) for further information on this.
18 |
19 | *Thank you to everyone who contributed, tested, and supported this project. Your efforts helped create a stronger, more unified solution now available in the official repository.*
20 |
21 | ---
22 |
23 | ## License
24 | MIT License
25 |
26 | Copyright (c) 2021 Mitch Kelly
27 |
28 | Permission is hereby granted, free of charge, to any person obtaining a copy
29 | of this software and associated documentation files (the "Software"), to deal
30 | in the Software without restriction, including without limitation the rights
31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32 | copies of the Software, and to permit persons to whom the Software is
33 | furnished to do so, subject to the following conditions:
34 |
35 | The above copyright notice and this permission notice shall be included in all
36 | copies or substantial portions of the Software.
37 |
38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44 | SOFTWARE.
45 |
--------------------------------------------------------------------------------
/dev_requirements.txt:
--------------------------------------------------------------------------------
1 | furo==2023.5.20
2 | pre-commit==3.3.3
3 | pytest==7.3.2
4 | python-box==7.0.1
5 | restfly==1.4.7
6 | requests==2.31.0
7 | responses==0.23.1
8 | sphinx==7.0.1
9 | toml==0.10.2
10 | urllib3<2
--------------------------------------------------------------------------------
/docs/.buildinfo:
--------------------------------------------------------------------------------
1 | # Sphinx build info version 1
2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3 | config: cc2b3d2de8fe545361b448fa1cd5d1c4
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/admins.rst.txt:
--------------------------------------------------------------------------------
1 | admins
2 | --------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Admin Management API endpoints.
6 |
7 | .. _zia-admins:
8 |
9 | .. automodule:: pyzscaler.zia.admins
10 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/audit_logs.rst.txt:
--------------------------------------------------------------------------------
1 | audit_logs
2 | ============
3 |
4 | The following methods allow for interaction with the ZIA Audit Logs API endpoints.
5 |
6 | Methods are accessible via ``zia.audit_logs``
7 |
8 | .. _zia-audit_logs:
9 |
10 | .. automodule:: pyzscaler.zia.audit_logs
11 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/config.rst.txt:
--------------------------------------------------------------------------------
1 | config
2 | ========
3 |
4 | The following methods allow for interaction with the ZIA
5 | Activation Management API endpoints.
6 |
7 | Methods are accessible via ``zia.config``
8 |
9 | .. _zia-config:
10 |
11 | .. automodule:: pyzscaler.zia.config
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/index.rst.txt:
--------------------------------------------------------------------------------
1 | ZIA
2 | ==========
3 | This package covers the ZIA interface.
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 |
8 | audit_logs
9 | config
10 | firewall
11 | locations
12 | sandbox
13 | security
14 | session
15 | ssl
16 | traffic
17 | url_categories
18 | url_filters
19 | users
20 |
21 | .. automodule:: pyzscaler.zia
22 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/locations.rst.txt:
--------------------------------------------------------------------------------
1 | locations
2 | ----------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Location Management API endpoints.
6 |
7 | Methods are accessible via ``zia.locations``
8 |
9 | .. _zia-locations:
10 |
11 | .. automodule:: pyzscaler.zia.locations
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/traffic.rst.txt:
--------------------------------------------------------------------------------
1 | traffic
2 | ---------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Traffic Management API endpoints.
6 |
7 | Methods are accessible via ``zia.traffic``
8 |
9 | .. _zia-traffic:
10 |
11 | .. automodule:: pyzscaler.zia.traffic
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zia/users.rst.txt:
--------------------------------------------------------------------------------
1 | users
2 | ------
3 |
4 | The following methods allow for interaction with the ZIA
5 | User Management API endpoints.
6 |
7 | Methods are accessible via ``zia.users``
8 |
9 | .. _zia-users:
10 |
11 | .. automodule:: pyzscaler.zia.users
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/app_segments.rst.txt:
--------------------------------------------------------------------------------
1 | app_segments
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Application Segments API endpoints.
6 |
7 | Methods are accessible via ``zpa.app_segments``
8 |
9 | .. _zpa-app_segments:
10 |
11 | .. automodule:: pyzscaler.zpa.app_segments
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/certificates.rst.txt:
--------------------------------------------------------------------------------
1 | certificates
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Browser Access Certificate API endpoints.
6 |
7 | Methods are accessible via ``zpa.certificates``
8 |
9 | Browser Access is the only ZPA API endpoint for certificates provided by Zscaler at the time of publishing. The
10 | ``zpa.certificates`` namespace has been chosen to accommodate any potential future certificate API endpoints.
11 |
12 | .. _zpa-certificates:
13 |
14 | .. automodule:: pyzscaler.zpa.certificates
15 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/cloud_connector_groups.rst.txt:
--------------------------------------------------------------------------------
1 | cloud_connector_groups
2 | ------------------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Cloud Connector Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.cloud_connector_groups``
8 |
9 | .. _zpa-cloud_connector_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.cloud_connector_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/connector_groups.rst.txt:
--------------------------------------------------------------------------------
1 | connector_groups
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Connector Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.connector_groups``
8 |
9 | .. _zpa-connector_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.connector_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/idp.rst.txt:
--------------------------------------------------------------------------------
1 | idp
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | IDP Controller API endpoints.
6 |
7 | Methods are accessible via ``zpa.idp``
8 |
9 | .. _zpa-idp:
10 |
11 | .. automodule:: pyzscaler.zpa.idp
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/index.rst.txt:
--------------------------------------------------------------------------------
1 | ZPA
2 | ==========
3 | This package covers the ZPA interface.
4 |
5 | .. toctree::
6 | :maxdepth: 1
7 | :glob:
8 | :hidden:
9 |
10 | *
11 |
12 | .. automodule:: pyzscaler.zpa
13 | :members:
14 |
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/machine_groups.rst.txt:
--------------------------------------------------------------------------------
1 | machine_groups
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Machine Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.machine_groups``
8 |
9 | .. _zpa-machine_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.machine_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/policies.rst.txt:
--------------------------------------------------------------------------------
1 | policies
2 | ------------------
3 |
4 | The following methods allow for interaction with the ZPA Policy Sets API endpoints.
5 |
6 | Methods are accessible via ``zpa.policies``
7 |
8 | .. _zpa-policies:
9 |
10 | .. automodule:: pyzscaler.zpa.policies
11 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/posture_profiles.rst.txt:
--------------------------------------------------------------------------------
1 | posture_profiles
2 | ------------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Posture Profiles API endpoints.
6 |
7 | Methods are accessible via ``zpa.posture_profiles``
8 |
9 | .. _zpa-posture_profiles:
10 |
11 | .. automodule:: pyzscaler.zpa.posture_profiles
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/saml_attributes.rst.txt:
--------------------------------------------------------------------------------
1 | saml_attributes
2 | ----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SAML Attributes API endpoints.
6 |
7 | Methods are accessible via ``zpa.saml_attributes``
8 |
9 | .. _zpa-saml_attributes:
10 |
11 | .. automodule:: pyzscaler.zpa.saml_attributes
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/scim_attributes.rst.txt:
--------------------------------------------------------------------------------
1 | scim_attributes
2 | ----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SCIM Attributes API endpoints.
6 |
7 | Methods are accessible via ``zpa.scim_attributes``
8 |
9 | .. _zpa-scim_attributes:
10 |
11 | .. automodule:: pyzscaler.zpa.scim_attributes
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/scim_groups.rst.txt:
--------------------------------------------------------------------------------
1 | scim_groups
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SCIM Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.scim_groups``
8 |
9 | .. _zpa-scim_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.scim_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/segment_groups.rst.txt:
--------------------------------------------------------------------------------
1 | segment_groups
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Segment Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.segment_groups``
8 |
9 | .. _zpa-segment_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.segment_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/server_groups.rst.txt:
--------------------------------------------------------------------------------
1 | server_groups
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Server Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.server_groups``
8 |
9 | .. _zpa-server_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.server_groups
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/servers.rst.txt:
--------------------------------------------------------------------------------
1 | servers
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Application Servers API endpoints.
6 |
7 | Methods are accessible via ``zpa.servers``
8 |
9 | .. _zpa-app_servers:
10 |
11 | .. automodule:: pyzscaler.zpa.servers
12 | :members:
--------------------------------------------------------------------------------
/docs/_sources/zs/zpa/trusted_networks.rst.txt:
--------------------------------------------------------------------------------
1 | trusted_networks
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Trusted Networks API endpoints.
6 |
7 | Methods are accessible via ``zpa.trusted_networks``
8 |
9 | .. _zpa-trusted_networks:
10 |
11 | .. automodule:: pyzscaler.zpa.trusted_networks
12 | :members:
--------------------------------------------------------------------------------
/docs/_static/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/_static/custom.css:
--------------------------------------------------------------------------------
1 | /* This file intentionally left blank. */
2 |
--------------------------------------------------------------------------------
/docs/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '0.7.0',
4 | LANGUAGE: 'None',
5 | COLLAPSE_INDEX: false,
6 | BUILDER: 'html',
7 | FILE_SUFFIX: '.html',
8 | LINK_SUFFIX: '.html',
9 | HAS_SOURCE: true,
10 | SOURCELINK_SUFFIX: '.txt',
11 | NAVIGATION_WITH_KEYS: false
12 | };
--------------------------------------------------------------------------------
/docs/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/file.png
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Inconsolata-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Inconsolata-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Inconsolata.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bold.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bold.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bolditalic.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bolditalic.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bolditalic.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-bolditalic.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-italic.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-italic.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-italic.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-regular.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-regular.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/Lato/lato-regular.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/docs/_static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/_static/js/badge_only.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
--------------------------------------------------------------------------------
/docs/_static/js/html5shiv-printshiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/_static/js/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/_static/js/theme.js:
--------------------------------------------------------------------------------
1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap(""),n("table.docutils.citation").wrap(""),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}t.length>0&&($(".wy-menu-vertical .current").removeClass("current"),t.addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l1").parent().addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l2").addClass("current"),t.closest("li.toctree-l3").addClass("current"),t.closest("li.toctree-l4").addClass("current"),t.closest("li.toctree-l5").addClass("current"),t[0].scrollIntoView())}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t
--------------------------------------------------------------------------------
/docsrc/zs/zcc/devices.rst:
--------------------------------------------------------------------------------
1 | devices
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCC
5 | Devices API endpoints.
6 |
7 | Methods are accessible via ``zcc.devices``
8 |
9 | .. _zcc-devices:
10 |
11 | .. automodule:: pyzscaler.zcc.devices
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcc/index.rst:
--------------------------------------------------------------------------------
1 | ZCC
2 | ==========
3 | This package covers the ZCC interface.
4 |
5 | .. toctree::
6 | :maxdepth: 1
7 | :glob:
8 | :hidden:
9 |
10 | *
11 |
12 | .. automodule:: pyzscaler.zcc
13 | :members:
14 |
--------------------------------------------------------------------------------
/docsrc/zs/zcc/secrets.rst:
--------------------------------------------------------------------------------
1 | secrets
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCC API endpoints for managing secrets.
5 |
6 | Methods are accessible via ``zcc.secrets``
7 |
8 | .. _zcc-secrets:
9 |
10 | .. automodule:: pyzscaler.zcc.secrets
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcc/session.rst:
--------------------------------------------------------------------------------
1 | session
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCC
5 | Session API endpoints.
6 |
7 | Methods are accessible via ``zcc.session``
8 |
9 | .. _zcc-session:
10 |
11 | .. automodule:: pyzscaler.zcc.session
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcon/admin.rst:
--------------------------------------------------------------------------------
1 | admin
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCON
5 | Admin API endpoints.
6 |
7 | Methods are accessible via ``zcon.admin``
8 |
9 | .. _zcon-admin:
10 |
11 | .. automodule:: pyzscaler.zcon.admin
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcon/config.rst:
--------------------------------------------------------------------------------
1 | config
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCON
5 | Config API endpoints.
6 |
7 | Methods are accessible via ``zcon.config``
8 |
9 | .. _zcon-config:
10 |
11 | .. automodule:: pyzscaler.zcon.config
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcon/connectors.rst:
--------------------------------------------------------------------------------
1 | connectors
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCON
5 | Connectors API endpoints.
6 |
7 | Methods are accessible via ``zcon.connectors``
8 |
9 | .. _zcon-connectors:
10 |
11 | .. automodule:: pyzscaler.zcon.connectors
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcon/index.rst:
--------------------------------------------------------------------------------
1 | ZCON
2 | ==========
3 | This package covers the ZCON interface.
4 |
5 | .. toctree::
6 | :maxdepth: 1
7 | :glob:
8 | :hidden:
9 |
10 | *
11 |
12 | .. automodule:: pyzscaler.zcon
13 | :members:
14 |
--------------------------------------------------------------------------------
/docsrc/zs/zcon/locations.rst:
--------------------------------------------------------------------------------
1 | locations
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCON
5 | Locations API endpoints.
6 |
7 | Methods are accessible via ``zcon.locations``
8 |
9 | .. _zcon-locations:
10 |
11 | .. automodule:: pyzscaler.zcon.locations
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zcon/session.rst:
--------------------------------------------------------------------------------
1 | session
2 | --------------
3 |
4 | The following methods allow for interaction with the ZCON
5 | Session API endpoints.
6 |
7 | Methods are accessible via ``zcon.session``
8 |
9 | .. _zcon-session:
10 |
11 | .. automodule:: pyzscaler.zcon.session
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zdx/admin.rst:
--------------------------------------------------------------------------------
1 | admin
2 | ------
3 |
4 | The following methods allow for interaction with the ZDX
5 | Admin API endpoints.
6 |
7 | Methods are accessible via ``zdx.admin``
8 |
9 | .. _zdx-admin:
10 |
11 | .. automodule:: pyzscaler.zdx.admin
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zdx/apps.rst:
--------------------------------------------------------------------------------
1 | apps
2 | ------
3 |
4 | The following methods allow for interaction with the ZDX
5 | Application API endpoints.
6 |
7 | Methods are accessible via ``zdx.apps``
8 |
9 | .. _zdx-apps:
10 |
11 | .. automodule:: pyzscaler.zdx.apps
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zdx/devices.rst:
--------------------------------------------------------------------------------
1 | devices
2 | -------
3 |
4 | The following methods allow for interaction with the ZDX
5 | Devices API endpoints.
6 |
7 | Methods are accessible via ``zdx.devices``
8 |
9 | .. _zdx-devices:
10 |
11 | .. automodule:: pyzscaler.zdx.devices
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zdx/index.rst:
--------------------------------------------------------------------------------
1 | ZDX
2 | ==========
3 | This package covers the ZDX interface.
4 |
5 | .. toctree::
6 | :maxdepth: 1
7 | :glob:
8 | :hidden:
9 |
10 | *
11 |
12 | .. automodule:: pyzscaler.zdx
13 | :members:
14 |
--------------------------------------------------------------------------------
/docsrc/zs/zdx/session.rst:
--------------------------------------------------------------------------------
1 | session
2 | -------
3 |
4 | The following methods allow for interaction with the ZDX
5 | Session API endpoints.
6 |
7 | Methods are accessible via ``zdx.session``
8 |
9 | .. _zdx-session:
10 |
11 | .. automodule:: pyzscaler.zdx.session
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zdx/users.rst:
--------------------------------------------------------------------------------
1 | users
2 | -------
3 |
4 | The following methods allow for interaction with the ZDX
5 | Users API endpoints.
6 |
7 | Methods are accessible via ``zdx.users``
8 |
9 | .. _zdx-users:
10 |
11 | .. automodule:: pyzscaler.zdx.users
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/admin_and_role_management.rst:
--------------------------------------------------------------------------------
1 | admin_and_role_management
2 | ==========================
3 |
4 | The following methods allow for interaction with the ZIA Admin and Role Management API endpoints.
5 |
6 | Methods are accessible via ``zia.admin_and_role_management``
7 |
8 | .. _zia-admin_and_role_management:
9 |
10 | .. automodule:: pyzscaler.zia.admin_and_role_management
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/apptotal.rst:
--------------------------------------------------------------------------------
1 | apptotal
2 | ============
3 |
4 | The following methods allow for interaction with the ZIA AppTotal API endpoints.
5 |
6 | Methods are accessible via ``zia.apptotal``
7 |
8 | .. _zia-apptotal:
9 |
10 | .. automodule:: pyzscaler.zia.apptotal
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/audit_logs.rst:
--------------------------------------------------------------------------------
1 | audit_logs
2 | ============
3 |
4 | The following methods allow for interaction with the ZIA Audit Logs API endpoints.
5 |
6 | Methods are accessible via ``zia.audit_logs``
7 |
8 | .. _zia-audit_logs:
9 |
10 | .. automodule:: pyzscaler.zia.audit_logs
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/cloud_apps.rst:
--------------------------------------------------------------------------------
1 | cloud_apps
2 | -------------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Cloud Applications API endpoints.
6 |
7 | Methods are accessible via ``zia.cloud_apps``
8 |
9 | .. _zia-cloud_apps:
10 |
11 | .. automodule:: pyzscaler.zia.cloud_apps
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/config.rst:
--------------------------------------------------------------------------------
1 | config
2 | ========
3 |
4 | The following methods allow for interaction with the ZIA
5 | Activation Management API endpoints.
6 |
7 | Methods are accessible via ``zia.config``
8 |
9 | .. _zia-config:
10 |
11 | .. automodule:: pyzscaler.zia.config
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/dlp.rst:
--------------------------------------------------------------------------------
1 | dlp
2 | ========
3 |
4 | The following methods allow for interaction with the ZIA
5 | DLP Dictionary API endpoints.
6 |
7 | Methods are accessible via ``zia.dlp``
8 |
9 | .. _zia-dlp:
10 |
11 | .. automodule:: pyzscaler.zia.dlp
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/firewall.rst:
--------------------------------------------------------------------------------
1 | firewall
2 | =========
3 |
4 | The following methods allow for interaction with the ZIA Firewall Policies API endpoints.
5 |
6 | Methods are accessible via ``zia.firewall``
7 |
8 | .. _zia-firewall:
9 |
10 | .. automodule:: pyzscaler.zia.firewall
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/index.rst:
--------------------------------------------------------------------------------
1 | ZIA
2 | ==========
3 | This package covers the ZIA interface.
4 |
5 | .. toctree::
6 | :glob:
7 | :hidden:
8 |
9 | *
10 |
11 | .. automodule:: pyzscaler.zia
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/locations.rst:
--------------------------------------------------------------------------------
1 | locations
2 | ----------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Location Management API endpoints.
6 |
7 | Methods are accessible via ``zia.locations``
8 |
9 | .. _zia-locations:
10 |
11 | .. automodule:: pyzscaler.zia.locations
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/rule_labels.rst:
--------------------------------------------------------------------------------
1 | labels
2 | -------------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Rule Labels API endpoints.
6 |
7 | Methods are accessible via ``zia.labels``
8 |
9 | .. _zia-labels:
10 |
11 | .. automodule:: pyzscaler.zia.labels
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/sandbox.rst:
--------------------------------------------------------------------------------
1 | sandbox
2 | ----------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Cloud Sandbox API endpoints.
6 |
7 | Methods are accessible via ``zia.sandbox``
8 |
9 | .. _zia-sandbox:
10 |
11 | .. automodule:: pyzscaler.zia.sandbox
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/security.rst:
--------------------------------------------------------------------------------
1 | security
2 | ----------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Security Policy Settings API endpoints.
6 |
7 | Methods are accessible via ``zia.security``
8 |
9 | .. _zia-security:
10 |
11 | .. automodule:: pyzscaler.zia.security
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/session.rst:
--------------------------------------------------------------------------------
1 | session
2 | ========
3 |
4 | The following methods allow for interaction with the ZIA Authentication Session API endpoints.
5 |
6 | Methods are accessible via ``zia.session``
7 |
8 | There is no need to manually create or delete a pyZscaler session, especially if you are using a
9 | context handler such as ``with``. The example below shows correct usage of pyZscaler to print
10 | the GRE tunnels configured in ZIA. The authenticated session will be automatically torn down by
11 | pyZscaler after all the tunnels have been printed.
12 |
13 |
14 | .. code-block:: python
15 |
16 | from pyzscaler.zia import ZIA
17 |
18 | with ZIA() as zia:
19 | for tunnel in zia.traffic.list_gre_tunnels():
20 | print(tunnel)
21 |
22 | .. _zia-session:
23 |
24 | .. automodule:: pyzscaler.zia.session
25 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/ssl_inspection.rst:
--------------------------------------------------------------------------------
1 | ssl_inspection
2 | ----------------
3 |
4 | The following methods allow for interaction with the ZIA SSL Inspection Settings API endpoints.
5 |
6 | Methods are accessible via ``zia.ssl_inspection``
7 |
8 | .. _zia-ssl_inspection:
9 |
10 | .. automodule:: pyzscaler.zia.ssl_inspection
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/traffic.rst:
--------------------------------------------------------------------------------
1 | traffic
2 | ---------
3 |
4 | The following methods allow for interaction with the ZIA
5 | Traffic Management API endpoints.
6 |
7 | Methods are accessible via ``zia.traffic``
8 |
9 | .. _zia-traffic:
10 |
11 | .. automodule:: pyzscaler.zia.traffic
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/url_categories.rst:
--------------------------------------------------------------------------------
1 | url_categories
2 | ===============
3 |
4 | The following methods allow for interaction with the ZIA URL Categories API endpoints.
5 |
6 | Methods are accessible via ``zia.url_categories``
7 |
8 |
9 | **Guidelines for adding / updating URLs**
10 |
11 |
12 | - The URL must use a standard URI format.
13 | - The URL length cannot exceed 1024 characters.
14 | - The URL cannot contain non-ASCII characters.
15 | - The domain name before the colon (:) cannot exceed 255 characters.
16 | - The domain name between periods (.) cannot exceed 63 characters.
17 |
18 | .. _zia-url_categories:
19 |
20 | .. automodule:: pyzscaler.zia.url_categories
21 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/url_filters.rst:
--------------------------------------------------------------------------------
1 | url_filters
2 | ===============
3 |
4 | The following methods allow for interaction with the ZIA URL Filtering Policy API endpoints.
5 |
6 | Methods are accessible via ``zia.url_filters``
7 |
8 | .. _zia-url_filters:
9 |
10 | .. automodule:: pyzscaler.zia.url_filters
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/users.rst:
--------------------------------------------------------------------------------
1 | users
2 | ------
3 |
4 | The following methods allow for interaction with the ZIA
5 | User Management API endpoints.
6 |
7 | Methods are accessible via ``zia.users``
8 |
9 | .. _zia-users:
10 |
11 | .. automodule:: pyzscaler.zia.users
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/vips.rst:
--------------------------------------------------------------------------------
1 | vips
2 | ========
3 |
4 | The following methods allow for interaction with the ZIA Data Center VIPs API endpoints.
5 |
6 | Methods are accessible via ``zia.vips``
7 |
8 | .. _zia-vips:
9 |
10 | .. automodule:: pyzscaler.zia.vips
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zia/web_dlp.rst:
--------------------------------------------------------------------------------
1 | web_dlp
2 | -------
3 |
4 | The following methods allow for interaction with the ZIA
5 | User Management API endpoints.
6 |
7 | Methods are accessible via ``zia.web_dlp``
8 |
9 | .. _zia-web_dlp:
10 |
11 | .. automodule:: pyzscaler.zia.web_dlp
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/app_segments.rst:
--------------------------------------------------------------------------------
1 | app_segments
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Application Segments API endpoints.
6 |
7 | Methods are accessible via ``zpa.app_segments``
8 |
9 | .. _zpa-app_segments:
10 |
11 | .. automodule:: pyzscaler.zpa.app_segments
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/certificates.rst:
--------------------------------------------------------------------------------
1 | certificates
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA Certificate API endpoints.
5 |
6 | Methods are accessible via ``zpa.certificates``
7 |
8 | .. _zpa-certificates:
9 |
10 | .. automodule:: pyzscaler.zpa.certificates
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/cloud_connector_groups.rst:
--------------------------------------------------------------------------------
1 | cloud_connector_groups
2 | ------------------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Cloud Connector Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.cloud_connector_groups``
8 |
9 | .. _zpa-cloud_connector_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.cloud_connector_groups
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/connectors.rst:
--------------------------------------------------------------------------------
1 | connectors
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | ZPA Connector API endpoints.
6 |
7 | Methods are accessible via ``zpa.connectors``
8 |
9 | .. _zpa-connectors:
10 |
11 | .. automodule:: pyzscaler.zpa.connectors
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/idp.rst:
--------------------------------------------------------------------------------
1 | idp
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | IDP Controller API endpoints.
6 |
7 | Methods are accessible via ``zpa.idp``
8 |
9 | .. _zpa-idp:
10 |
11 | .. automodule:: pyzscaler.zpa.idp
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/index.rst:
--------------------------------------------------------------------------------
1 | ZPA
2 | ==========
3 | This package covers the ZPA interface.
4 |
5 | .. toctree::
6 | :maxdepth: 1
7 | :glob:
8 | :hidden:
9 |
10 | *
11 |
12 | .. automodule:: pyzscaler.zpa
13 | :members:
14 |
--------------------------------------------------------------------------------
/docsrc/zs/zpa/inspection.rst:
--------------------------------------------------------------------------------
1 | inspection
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Inspection Controller API endpoints.
6 |
7 | Methods are accessible via ``zpa.inspection``
8 |
9 | .. _zpa-inspection:
10 |
11 | .. automodule:: pyzscaler.zpa.inspection
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/lss.rst:
--------------------------------------------------------------------------------
1 | lss
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Log Streaming Service Controller API endpoints.
6 |
7 | Methods are accessible via ``zpa.lss``
8 |
9 | .. _zpa-lss:
10 |
11 | .. automodule:: pyzscaler.zpa.lss
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/machine_groups.rst:
--------------------------------------------------------------------------------
1 | machine_groups
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Machine Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.machine_groups``
8 |
9 | .. _zpa-machine_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.machine_groups
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/policies.rst:
--------------------------------------------------------------------------------
1 | policies
2 | ------------------
3 |
4 | The following methods allow for interaction with the ZPA Policy Sets API endpoints.
5 |
6 | Methods are accessible via ``zpa.policies``
7 |
8 | .. _zpa-policies:
9 |
10 | .. automodule:: pyzscaler.zpa.policies
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/posture_profiles.rst:
--------------------------------------------------------------------------------
1 | posture_profiles
2 | ------------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Posture Profiles API endpoints.
6 |
7 | Methods are accessible via ``zpa.posture_profiles``
8 |
9 | .. _zpa-posture_profiles:
10 |
11 | .. automodule:: pyzscaler.zpa.posture_profiles
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/provisioning.rst:
--------------------------------------------------------------------------------
1 | provisioning
2 | ------------------
3 |
4 | The following methods allow for interaction with the ZPA Provisioning API endpoints.
5 |
6 | Methods are accessible via ``zpa.provisioning``
7 |
8 | .. _zpa-provisioning:
9 |
10 | .. automodule:: pyzscaler.zpa.provisioning
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/saml_attributes.rst:
--------------------------------------------------------------------------------
1 | saml_attributes
2 | ----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SAML Attributes API endpoints.
6 |
7 | Methods are accessible via ``zpa.saml_attributes``
8 |
9 | .. _zpa-saml_attributes:
10 |
11 | .. automodule:: pyzscaler.zpa.saml_attributes
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/scim_attributes.rst:
--------------------------------------------------------------------------------
1 | scim_attributes
2 | ----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SCIM Attributes API endpoints.
6 |
7 | Methods are accessible via ``zpa.scim_attributes``
8 |
9 | .. _zpa-scim_attributes:
10 |
11 | .. automodule:: pyzscaler.zpa.scim_attributes
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/scim_groups.rst:
--------------------------------------------------------------------------------
1 | scim_groups
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | SCIM Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.scim_groups``
8 |
9 | .. _zpa-scim_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.scim_groups
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/segment_groups.rst:
--------------------------------------------------------------------------------
1 | segment_groups
2 | ---------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Segment Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.segment_groups``
8 |
9 | .. _zpa-segment_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.segment_groups
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/server_groups.rst:
--------------------------------------------------------------------------------
1 | server_groups
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Server Groups API endpoints.
6 |
7 | Methods are accessible via ``zpa.server_groups``
8 |
9 | .. _zpa-server_groups:
10 |
11 | .. automodule:: pyzscaler.zpa.server_groups
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/servers.rst:
--------------------------------------------------------------------------------
1 | servers
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Application Servers API endpoints.
6 |
7 | Methods are accessible via ``zpa.servers``
8 |
9 | .. _zpa-app_servers:
10 |
11 | .. automodule:: pyzscaler.zpa.servers
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/service_edges.rst:
--------------------------------------------------------------------------------
1 | service_edges
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Service Edge API endpoints.
6 |
7 | Methods are accessible via ``zpa.service_edges``
8 |
9 | .. _zpa-service_edges:
10 |
11 | .. automodule:: pyzscaler.zpa.service_edges
12 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/session.rst:
--------------------------------------------------------------------------------
1 | session
2 | --------------
3 |
4 | The following methods allow for interaction with the ZPA Session creation / deletion endpoints.
5 |
6 | Methods are accessible via ``zpa.session``
7 |
8 | .. _zpa-session:
9 |
10 | .. automodule:: pyzscaler.zpa.session
11 | :members:
--------------------------------------------------------------------------------
/docsrc/zs/zpa/trusted_networks.rst:
--------------------------------------------------------------------------------
1 | trusted_networks
2 | -----------------
3 |
4 | The following methods allow for interaction with the ZPA
5 | Trusted Networks API endpoints.
6 |
7 | Methods are accessible via ``zpa.trusted_networks``
8 |
9 | .. _zpa-trusted_networks:
10 |
11 | .. automodule:: pyzscaler.zpa.trusted_networks
12 | :members:
--------------------------------------------------------------------------------
/examples/zia/download_audit_logs/zia_download_audit_logs.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from pyzscaler.zia import ZIA
4 |
5 | with ZIA() as zia:
6 | complete = False
7 | zia.audit_logs.create(start_time="1627221600000", end_time="1627271676622")
8 |
9 | while not complete:
10 | if zia.audit_logs.status().status == "COMPLETE":
11 | complete = True
12 | time.sleep(2) # 2 seconds is enough to avoid API limits
13 |
14 | with open("audit_log.csv", "w+") as fh:
15 | fh.write(zia.audit_logs.get_report())
16 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "pyzscaler"
3 | version = "1.6.0"
4 | description = "A python SDK for the Zscaler API."
5 | authors = ["Mitch Kelly "]
6 | license = "MIT"
7 | readme = "README.md"
8 | documentation = "https://pyzscaler.readthedocs.io/"
9 | homepage = "https://pyzscaler.packet.tech/"
10 | repository = "https://github.com/mitchos/pyZscaler"
11 | classifiers = [
12 | "Environment :: Console",
13 | "Intended Audience :: Developers",
14 | "Intended Audience :: Information Technology",
15 | "Intended Audience :: System Administrators",
16 | "License :: OSI Approved :: MIT License",
17 | "Natural Language :: English",
18 | "Programming Language :: Python :: 3",
19 | "Programming Language :: Python :: 3.8",
20 | "Programming Language :: Python :: 3.9",
21 | "Programming Language :: Python :: 3.10",
22 | "Programming Language :: Python :: 3.11",
23 | "Programming Language :: Python :: 3.12",
24 | "Topic :: Security",
25 | "Topic :: Software Development :: Libraries :: Python Modules", ]
26 | include = [
27 | "LICENSE",
28 | ]
29 |
30 | [tool.poetry.urls]
31 | "Bug Tracker" = "https://github.com/mitchos/pyZscaler/issues"
32 |
33 | [tool.poetry.dependencies]
34 | python = "^3.8"
35 | restfly = "1.4.7"
36 | python-box = "7.0.1"
37 |
38 | [tool.poetry.dev-dependencies]
39 | python = "^3.8"
40 | restfly = "1.4.7"
41 | python-box = "7.0.1"
42 | sphinx = "7.0.1"
43 | furo = "2023.5.20"
44 | pytest = "7.3.2"
45 | requests = "2.29.0"
46 | pre-commit = "3.3.3"
47 | responses = "0.23.1"
48 | toml = "0.10.2"
49 | urllib3 = "1.26.16"
50 |
51 | [build-system]
52 | requires = ["poetry-core>=1.0.0"]
53 | build-backend = "poetry.core.masonry.api"
54 |
55 | [tool.black]
56 | line-length = 127
57 |
58 | [tool.pylint.'MESSAGES CONTROL']
59 | disable=[
60 | "format",
61 | "missing-module-docstring",
62 | "missing-class-docstring",
63 | "missing-function-docstring",
64 | "too-many-public-methods",
65 | "anomalous-backslash-in-string",
66 | "import-error",
67 | "redefined-outer-name",
68 | ]
69 |
70 | [tool.pylint.'FORMAT']
71 | max-line-length = 127
72 |
73 | [tool.isort]
74 | profile = "black"
75 |
--------------------------------------------------------------------------------
/pyzscaler/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "Mitch Kelly"
2 | __license__ = "MIT"
3 | __contributors__ = [
4 | "Dax Mickelson",
5 | "Jacob Gårder",
6 | ]
7 | __version__ = "1.6.0"
8 |
9 | from pyzscaler.zcc import ZCC # noqa
10 | from pyzscaler.zcon import ZCON # noqa
11 | from pyzscaler.zdx import ZDX # noqa
12 | from pyzscaler.zia import ZIA # noqa
13 | from pyzscaler.zpa import ZPA # noqa
14 |
--------------------------------------------------------------------------------
/pyzscaler/zcc/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from box import Box
4 | from restfly.session import APISession
5 |
6 | from pyzscaler import __version__
7 |
8 | from .devices import DevicesAPI
9 | from .secrets import SecretsAPI
10 | from .session import AuthenticatedSessionAPI
11 |
12 |
13 | class ZCC(APISession):
14 | """
15 | A Controller to access Endpoints in the Zscaler Mobile Admin Portal API.
16 |
17 | The ZCC object stores the session token and simplifies access to CRUD options within the ZCC Portal.
18 |
19 | Attributes:
20 | client_id (str): The ZCC Client ID generated from the ZCC Portal.
21 | client_secret (str): The ZCC Client Secret generated from the ZCC Portal.
22 | cloud (str): The Zscaler cloud for your tenancy, accepted values are:
23 |
24 | * ``zscaler``
25 | * ``zscalerone``
26 | * ``zscalertwo``
27 | * ``zscalerthree``
28 | * ``zscloud``
29 | * ``zscalerbeta``
30 | override_url (str):
31 | If supplied, this attribute can be used to override the production URL that is derived
32 | from supplying the `cloud` attribute. Use this attribute if you have a non-standard tenant URL
33 | (e.g. internal test instance etc). When using this attribute, there is no need to supply the `cloud`
34 | attribute. The override URL will be prepended to the API endpoint suffixes. The protocol must be included
35 | i.e. http:// or https://.
36 |
37 | """
38 |
39 | _vendor = "Zscaler"
40 | _product = "Zscaler Mobile Admin Portal"
41 | _backoff = 3
42 | _build = __version__
43 | _box = True
44 | _box_attrs = {"camel_killer_box": True}
45 | _env_base = "ZCC"
46 | _env_cloud = "zscaler"
47 | _url = "https://api-mobile.zscaler.net/papi"
48 |
49 | def __init__(self, **kw):
50 | self._client_id = kw.get("client_id", os.getenv(f"{self._env_base}_CLIENT_ID"))
51 | self._client_secret = kw.get("client_secret", os.getenv(f"{self._env_base}_CLIENT_SECRET"))
52 | self._env_cloud = kw.get("cloud", os.getenv(f"{self._env_base}_CLOUD"))
53 | self._url = (
54 | kw.get("override_url", os.getenv(f"{self._env_base}_OVERRIDE_URL"))
55 | or f"https://api-mobile.{self._env_cloud}.net/papi"
56 | )
57 | self.conv_box = True
58 | super(ZCC, self).__init__(**kw)
59 |
60 | def _build_session(self, **kwargs) -> Box:
61 | """Creates a ZCC API session."""
62 | super(ZCC, self)._build_session(**kwargs)
63 | self._auth_token = self.session.create_token(client_id=self._client_id, client_secret=self._client_secret)
64 | return self._session.headers.update({"auth-token": f"{self._auth_token}"})
65 |
66 | @property
67 | def devices(self):
68 | """The interface object for the :ref:`ZCC Devices interface `."""
69 | return DevicesAPI(self)
70 |
71 | @property
72 | def secrets(self):
73 | """The interface object for the :ref:`ZCC Secrets interface `."""
74 | return SecretsAPI(self)
75 |
76 | @property
77 | def session(self):
78 | """The interface object for the :ref:`ZCC Authenticated Session interface `."""
79 | return AuthenticatedSessionAPI(self)
80 |
--------------------------------------------------------------------------------
/pyzscaler/zcc/secrets.py:
--------------------------------------------------------------------------------
1 | from restfly.endpoint import APIEndpoint
2 |
3 | from pyzscaler.utils import zcc_param_map
4 |
5 |
6 | class SecretsAPI(APIEndpoint):
7 | def get_otp(self, device_id: str):
8 | """
9 | Returns the OTP code for the specified device id.
10 |
11 | Args:
12 | device_id (str): The unique id for the enrolled device that the OTP will be obtained for.
13 |
14 | Returns:
15 | :obj:`Box`: A dictionary containing the requested OTP code for the specified device id.
16 |
17 | Examples:
18 | Obtain the OTP code for a device and print it to console:
19 |
20 | >>> otp_code = zcc.secrets.get_otp('System-Serial-Number:1234ABCDEF')
21 | ... print(otp_code.otp)
22 |
23 | """
24 |
25 | payload = {"udid": device_id}
26 |
27 | return self._get("public/v1/getOtp", params=payload)
28 |
29 | def get_passwords(self, username: str, os_type: str = "windows"):
30 | """
31 | Return passwords for the specified username and device OS type.
32 |
33 | Args:
34 | username (str): The username that the device belongs to.
35 | os_type (str): The OS Type for the device, defaults to `windows`. Valid options are:
36 |
37 | - ios
38 | - android
39 | - windows
40 | - macos
41 | - linux
42 |
43 | Returns:
44 | :obj:`Box`: Dictionary containing passwords for the specified username's device.
45 |
46 | Examples:
47 | Print macOS device passwords for username test@example.com:
48 |
49 | >>> print(zcc.secrets.get_passwords(username='test@example.com',
50 | ... os_type='macos'))
51 |
52 | """
53 |
54 | # Simplify the os_type argument, raise an error if the user supplies the wrong one.
55 | os_type = zcc_param_map["os"].get(os_type, None)
56 | if not os_type:
57 | raise ValueError("Invalid os_type specified. Check the pyZscaler documentation for valid os_type options.")
58 |
59 | params = {
60 | "username": username,
61 | "osType": os_type,
62 | }
63 |
64 | return self._get("public/v1/getPasswords", params=params)
65 |
--------------------------------------------------------------------------------
/pyzscaler/zcc/session.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly.endpoint import APIEndpoint
3 |
4 |
5 | class AuthenticatedSessionAPI(APIEndpoint):
6 | def create_token(self, client_id: str, client_secret: str) -> Box:
7 | """
8 | Creates a ZCC authentication token.
9 |
10 | Args:
11 | client_id (str): The ZCC Portal Client ID.
12 | client_secret (str): The ZCC Portal Client Secret.
13 |
14 | Returns:
15 | :obj:`Box`: The authenticated session information.
16 |
17 | Examples:
18 | >>> zia.session.create(api_key='999999999',
19 | ... username='admin@example.com',
20 | ... password='MyInsecurePassword')
21 |
22 |
23 | """
24 |
25 | payload = {
26 | "apiKey": client_id,
27 | "secretKey": client_secret,
28 | }
29 |
30 | return self._post("auth/v1/login", json=payload).jwt_token
31 |
--------------------------------------------------------------------------------
/pyzscaler/zcon/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from box import Box
4 | from restfly import APISession
5 |
6 | from pyzscaler import __version__
7 |
8 | from .admin import ZCONAdminAPI
9 | from .config import ZCONConfigAPI
10 | from .connectors import ZCONConnectorsAPI
11 | from .locations import ZCONLocationsAPI
12 | from .session import ZCONSessionAPI
13 |
14 |
15 | class ZCON(APISession):
16 | """
17 | A Controller to access Endpoints in the Zscaler Cloud and Branch Connector API.
18 |
19 | The ZCON object stores the session token and simplifies access to CRUD options within the ZCON Portal.
20 |
21 | """
22 |
23 | _vendor = "Zscaler"
24 | _product = "Zscaler Cloud and Branch Connector"
25 | _backoff = 3
26 | _build = __version__
27 | _box = True
28 | _box_attrs = {"camel_killer_box": True}
29 | _env_base = "ZCON"
30 | _url = "https://connector.zscaler.net/api/v1"
31 | _env_cloud = "zscaler"
32 |
33 | def __init__(self, **kw):
34 | self._api_key = kw.get("api_key", os.getenv(f"{self._env_base}_API_KEY"))
35 | self._username = kw.get("username", os.getenv(f"{self._env_base}_USERNAME"))
36 | self._password = kw.get("password", os.getenv(f"{self._env_base}_PASSWORD"))
37 | self.env_cloud = kw.get("cloud", os.getenv(f"{self._env_base}_CLOUD"))
38 | self._url = (
39 | kw.get("override_url", os.getenv(f"{self._env_base}_OVERRIDE_URL"))
40 | or f"https://connector.{self.env_cloud}.net/api/v1"
41 | )
42 | self.conv_box = True
43 | super(ZCON, self).__init__(**kw)
44 |
45 | def _build_session(self, **kwargs) -> Box:
46 | """
47 | Build the APISession object.
48 |
49 | This method is called automatically when instantiating the ZCON object.
50 |
51 | Returns:
52 | Box: The Box object representing the ZCON API.
53 |
54 | """
55 | super(ZCON, self)._build_session(**kwargs)
56 | return self.session.create(api_key=self._api_key, username=self._username, password=self._password)
57 |
58 | def _deauthenticate(self):
59 | """
60 | End the authentication session.
61 |
62 | Returns:
63 | Box: The Box object representing the ZCON API.
64 |
65 | """
66 | return self.session.delete()
67 |
68 | @property
69 | def admin(self) -> ZCONAdminAPI:
70 | """
71 | The interface object for the :ref:`ZCON Admin interface `.
72 |
73 | Returns:
74 | ZCONAdminAPI: The AdminAPI object.
75 |
76 | """
77 | return ZCONAdminAPI(self)
78 |
79 | @property
80 | def connectors(self) -> ZCONConnectorsAPI:
81 | """
82 | The interface object for the :ref:`ZCON Connectors interface `.
83 |
84 | Returns:
85 | ZCONConnectorsAPI: The ConnectorsAPI object.
86 |
87 | """
88 | return ZCONConnectorsAPI(self)
89 |
90 | @property
91 | def config(self) -> ZCONConfigAPI:
92 | """
93 | The interface object for the :ref:`ZCON Config interface `.
94 |
95 | Returns:
96 | ZCONConfigAPI: The ConfigAPI object.
97 |
98 | """
99 | return ZCONConfigAPI(self)
100 |
101 | @property
102 | def locations(self) -> ZCONLocationsAPI:
103 | """
104 | The interface object for the :ref:`ZCON Locations interface `.
105 |
106 | Returns:
107 | ZCONLocationsAPI: The LocationsAPI object.
108 |
109 | """
110 | return ZCONLocationsAPI(self)
111 |
112 | @property
113 | def session(self) -> ZCONSessionAPI:
114 | """
115 | The interface object for the :ref:`ZCON Authentication interface `.
116 |
117 | Returns:
118 | ZCONSessionAPI: The SessionAPI object.
119 |
120 | """
121 | return ZCONSessionAPI(self)
122 |
--------------------------------------------------------------------------------
/pyzscaler/zcon/config.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly import APIEndpoint
3 |
4 |
5 | class ZCONConfigAPI(APIEndpoint):
6 | def activate(self, force: bool = False) -> Box:
7 | """
8 | Activate the configuration.
9 |
10 | Args:
11 | force (bool): If set to True, forces the activation. Default is False.
12 |
13 | Returns:
14 | :obj:`Box`: The status code of the operation.
15 |
16 | Examples:
17 | Activate the configuration without forcing::
18 |
19 | zcon.config.activate()
20 |
21 | Forcefully activate the configuration::
22 |
23 | zcon.config.activate(force=True)
24 |
25 | """
26 | if force:
27 | return self._post("ecAdminActivateStatus/forcedActivate")
28 | else:
29 | return self._post("ecAdminActivateStatus/activate")
30 |
31 | def get_status(self):
32 | """
33 | Get the status of the configuration.
34 |
35 | Returns:
36 | :obj:`Box`: The status of the configuration.
37 |
38 | Examples:
39 | Get the status of the configuration::
40 |
41 | print(zcon.config.get_status())
42 |
43 | """
44 | return self._get("ecAdminActivateStatus")
45 |
--------------------------------------------------------------------------------
/pyzscaler/zcon/connectors.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly import APIEndpoint
3 |
4 | from pyzscaler.utils import convert_keys
5 |
6 |
7 | class ZCONConnectorsAPI(APIEndpoint):
8 | def list_groups(self, **kwargs) -> BoxList:
9 | """
10 | List all existing connector groups.
11 |
12 | Keyword Args:
13 | page (int): The page number to return.
14 | page_size (int): The number of items to return per page.
15 |
16 | Returns:
17 | :obj:`BoxList`: The list of cloud and branch connector groups.
18 |
19 | Examples:
20 | List all connector groups::
21 |
22 | for group in zcon.connectors.list_groups():
23 | print(group)
24 |
25 | List first page of connector groups with 10 items per page::
26 |
27 | for group in zcon.connectors.list_groups(page=1, page_size=10):
28 | print(group)
29 |
30 | """
31 | # There is an edge case in the camelcase to snake conversion that we're going to handle here. We'll revert
32 | # the default camel_killer_box and run it through our conversion function in utils that handles edge-cases.
33 | self._box_attrs = {"camel_killer_box": False}
34 |
35 | return convert_keys(self._get("ecgroup", params=kwargs), "to_snake")
36 |
37 | def get_group(self, group_id: str) -> Box:
38 | """
39 | Get details for a specific connector group.
40 |
41 | Args:
42 | group_id (str): The ID of the connector group.
43 |
44 | Returns:
45 | :obj:`Box`: The connector group details.
46 |
47 | Examples:
48 | Get details of a specific connector group::
49 |
50 | print(zcon.connectors.get_group("123456789"))
51 | """
52 | # There is an edge case in the camelcase to snake conversion that we're going to handle here. We'll revert
53 | # the default camel_killer_box and run it through our conversion function in utils that handles edge-cases.
54 | self._box_attrs = {"camel_killer_box": False}
55 |
56 | return convert_keys(self._get(f"ecgroup/{group_id}"), "to_snake")
57 |
58 | def get_vm(self, group_id: str, vm_id: str) -> Box:
59 | """
60 | Get details for a specific connector VM.
61 |
62 | Args:
63 | group_id (str): The ID of the connector group.
64 | vm_id (str): The ID of the connector VM.
65 |
66 | Returns:
67 | :obj:`Box`: The connector VM details.
68 |
69 | Examples:
70 | Get details of a specific connector VM::
71 |
72 | print(zcon.connectors.get_vm("123456789", "987654321"))
73 | """
74 | return self._get(f"ecgroup/{group_id}/vm/{vm_id}")
75 |
76 | def delete_vm(self, group_id: str, vm_id: str) -> int:
77 | """
78 | Delete the specified connector VM.
79 |
80 | Args:
81 | group_id (str): The ID of the connector group.
82 | vm_id (str): The ID of the connector VM.
83 |
84 | Returns:
85 | :obj:`Box`: The status of the operation.
86 |
87 | Examples:
88 | Delete a specific connector VM::
89 |
90 | zcon.connectors.delete_vm("123456789", "987654321")
91 | """
92 | return self._delete(f"ecgroup/{group_id}/vm/{vm_id}").status_code
93 |
--------------------------------------------------------------------------------
/pyzscaler/zcon/session.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly import APIEndpoint
3 |
4 | from pyzscaler.utils import obfuscate_api_key
5 |
6 |
7 | class ZCONSessionAPI(APIEndpoint):
8 | def status(self) -> Box:
9 | """
10 | Returns the status of the authentication session if it exists.
11 |
12 | Returns:
13 | :obj:`Box`: Session authentication information.
14 |
15 | Examples:
16 | Check the status of the authentication session::
17 |
18 | print(zcon.session.status())
19 | """
20 | return self._get("auth")
21 |
22 | def create(self, api_key: str, username: str, password: str) -> Box:
23 | """
24 | Create a new ZCON authentication session.
25 |
26 | Args:
27 | api_key (str): The ZCON API Key.
28 | username (str): Username of admin user for the authentication session.
29 | password (str): Password of the admin user for the authentication session.
30 |
31 | Returns:
32 | :obj:`Box`: The authenticated session information.
33 |
34 | Examples:
35 | Create a new authentication session::
36 |
37 | zcon.session.create(
38 | api_key='123456789',
39 | username='admin@example.com',
40 | password='MyInsecurePassword'
41 | )
42 | """
43 | api_obf = obfuscate_api_key(api_key)
44 |
45 | payload = {
46 | "apiKey": api_obf["key"],
47 | "username": username,
48 | "password": password,
49 | "timestamp": api_obf["timestamp"],
50 | }
51 | return self._post("auth", json=payload)
52 |
53 | def delete(self) -> int:
54 | """
55 | End the authentication session.
56 |
57 | Returns:
58 | :obj:`int`: The status code of the operation.
59 |
60 | Examples:
61 | End the authentication session::
62 |
63 | print(zcon.session.delete())
64 | """
65 | return self._delete("auth").status
66 |
--------------------------------------------------------------------------------
/pyzscaler/zdx/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from box import Box
4 | from restfly.session import APISession
5 |
6 | from pyzscaler import __version__
7 | from pyzscaler.zdx.admin import AdminAPI
8 | from pyzscaler.zdx.apps import AppsAPI
9 | from pyzscaler.zdx.devices import DevicesAPI
10 | from pyzscaler.zdx.session import SessionAPI
11 | from pyzscaler.zdx.users import UsersAPI
12 |
13 |
14 | class ZDX(APISession):
15 | """
16 | A Controller to access Endpoints in the Zscaler Digital Experience (ZDX) API.
17 |
18 | The ZDX object stores the session token and simplifies access to CRUD options within the ZDX Portal.
19 |
20 | Attributes:
21 | client_id (str): The ZDX Client ID generated from the ZDX Portal.
22 | client_secret (str): The ZDX Client Secret generated from the ZDX Portal.
23 | cloud (str): The Zscaler cloud for your tenancy, accepted values are below. Defaults to ``zdxcloud``.
24 |
25 | * ``zdxcloud``
26 | * ``zdxbeta``
27 |
28 | override_url (str):
29 | If supplied, this attribute can be used to override the production URL that is derived
30 | from supplying the `cloud` attribute. Use this attribute if you have a non-standard tenant URL
31 | (e.g. internal test instance etc). When using this attribute, there is no need to supply the `cloud`
32 | attribute. The override URL will be prepended to the API endpoint suffixes. The protocol must be included
33 | i.e. http:// or https://.
34 |
35 | """
36 |
37 | _vendor = "Zscaler"
38 | _product = "pyZscaler"
39 | _backoff = 3
40 | _build = __version__
41 | _box = True
42 | _box_attrs = {"camel_killer_box": True}
43 | _env_base = "ZDX"
44 | _env_cloud = "zdxcloud"
45 | _url = "https://api.zdxcloud.net/v1"
46 |
47 | def __init__(self, **kw):
48 | self._client_id = kw.get("client_id", os.getenv(f"{self._env_base}_CLIENT_ID"))
49 | self._client_secret = kw.get("client_secret", os.getenv(f"{self._env_base}_CLIENT_SECRET"))
50 | self._cloud = kw.get("cloud", os.getenv(f"{self._env_base}_CLOUD", self._env_cloud))
51 | self._url = kw.get("override_url", os.getenv(f"{self._env_base}_OVERRIDE_URL")) or f"https://api.{self._cloud}.net/v1"
52 | self.conv_box = True
53 | super(ZDX, self).__init__(**kw)
54 |
55 | def _build_session(self, **kwargs) -> Box:
56 | """Creates a ZCC API session."""
57 | super(ZDX, self)._build_session(**kwargs)
58 | self._auth_token = self.session.create_token(client_id=self._client_id, client_secret=self._client_secret).token
59 | return self._session.headers.update({"Authorization": f"Bearer {self._auth_token}"})
60 |
61 | @property
62 | def session(self):
63 | """The interface object for the :ref:`ZDX Session interface `."""
64 | return SessionAPI(self)
65 |
66 | @property
67 | def admin(self):
68 | """The interface object for the :ref:`ZDX Admin interface `."""
69 | return AdminAPI(self)
70 |
71 | @property
72 | def apps(self):
73 | """The interface object for the :ref:`ZDX Apps interface `."""
74 | return AppsAPI(self)
75 |
76 | @property
77 | def devices(self):
78 | """The interface object for the :ref:`ZDX Devices interface `."""
79 | return DevicesAPI(self)
80 |
81 | @property
82 | def users(self):
83 | """The interface object for the :ref:`ZDX Users interface `."""
84 | return UsersAPI(self)
85 |
--------------------------------------------------------------------------------
/pyzscaler/zdx/admin.py:
--------------------------------------------------------------------------------
1 | from box import BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import zdx_params
5 |
6 |
7 | class AdminAPI(APIEndpoint):
8 | @zdx_params
9 | def list_departments(self, **kwargs) -> BoxList:
10 | """
11 | Returns a list of departments that are configured within ZDX.
12 |
13 | Keyword Args:
14 | since (int): The number of hours to look back for devices.
15 | search (str): The search string to filter by name or department ID.
16 |
17 | Returns:
18 | :obj:`BoxList`: The list of departments in ZDX.
19 |
20 | Examples:
21 | List all departments in ZDX for the past 2 hours:
22 |
23 | >>> for department in zdx.admin.list_departments():
24 | ... print(department)
25 |
26 | """
27 |
28 | return self._get("administration/departments", params=kwargs)
29 |
30 | @zdx_params
31 | def list_locations(self, **kwargs) -> BoxList:
32 | """
33 | Returns a list of locations that are configured within ZDX.
34 |
35 | Keyword Args:
36 | since (int): The number of hours to look back for devices.
37 | search (str): The search string to filter by name or location ID.
38 |
39 | Returns:
40 | :obj:`BoxList`: The list of locations in ZDX.
41 |
42 | Examples:
43 | List all locations in ZDX for the past 2 hours:
44 |
45 | >>> for location in zdx.admin.list_locations():
46 | ... print(location)
47 |
48 | """
49 | return self._get("administration/locations", params=kwargs)
50 |
51 | @zdx_params
52 | def list_geolocations(self, **kwargs) -> BoxList:
53 | """
54 | Returns a list of all active geolocations configured within the ZDX tenant.
55 |
56 | Keyword Args:
57 | since (int): The number of hours to look back for devices.
58 | location_id (str): The unique ID for the location.
59 | parent_geo_id (str): The unique ID for the parent geolocation.
60 | search (str): The search string to filter by name.
61 |
62 | Returns:
63 | :obj:`BoxList`: The list of geolocations in ZDX.
64 |
65 | Examples:
66 | List all geolocations in ZDX for the past 2 hours:
67 |
68 | >>> for geolocation in zdx.admin.list_geolocations():
69 | ... print(geolocation)
70 |
71 | """
72 | return self._get("active_geo", params=kwargs)
73 |
--------------------------------------------------------------------------------
/pyzscaler/zdx/session.py:
--------------------------------------------------------------------------------
1 | import time
2 | from hashlib import sha256
3 |
4 | from box import Box
5 | from restfly.endpoint import APIEndpoint
6 |
7 |
8 | class SessionAPI(APIEndpoint):
9 | def create_token(self, client_id: str, client_secret: str) -> Box:
10 | """
11 | Creates a ZDX authentication token.
12 |
13 | Args:
14 | client_id (str): The ZDX API Key ID.
15 | client_secret (str): The ZDX API Key Secret.
16 |
17 | Returns:
18 | :obj:`Box`: The authenticated session information.
19 |
20 | Examples:
21 | >>> zia.session.create(client_id='999999999',
22 | ... client_secret='admin@example.com')
23 |
24 | """
25 | epoch_time = int(time.time())
26 |
27 | # Zscaler requires the API Secret Key to be appended with the epoch timestamp, separated by a colon. We then
28 | # need to take the SHA256 hash of this string and pass that as the API Secret Key.
29 | api_secret_format = f"{client_secret}:{epoch_time}"
30 | api_secret_hash = sha256(api_secret_format.encode("utf-8")).hexdigest()
31 |
32 | payload = {"key_id": client_id, "key_secret": api_secret_hash, "timestamp": epoch_time}
33 |
34 | return self._post("oauth/token", json=payload)
35 |
36 | def validate_token(self):
37 | """
38 | Validates the current ZDX JWT token.
39 |
40 | Returns:
41 | :obj:`Box`: The validated session information.
42 |
43 | Examples:
44 | >>> validation = zdx.session.validate()
45 |
46 | """
47 | return self._get("oauth/validate")
48 |
49 | def get_jwks(self):
50 | """
51 | Returns a JSON Web Key Set (JWKS) that contains the public keys that can be used to verify the JWT tokens.
52 |
53 | Returns:
54 | :obj:`Box`: The JSON Web Key Set (JWKS).
55 |
56 | Examples:
57 | >>> jwks = zdx.session.get_jwks()
58 |
59 | """
60 | return self._get("oauth/jwks")
61 |
--------------------------------------------------------------------------------
/pyzscaler/zdx/users.py:
--------------------------------------------------------------------------------
1 | from box import BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import ZDXIterator, zdx_params
5 |
6 |
7 | class UsersAPI(APIEndpoint):
8 | @zdx_params
9 | def list_users(self, **kwargs) -> BoxList:
10 | """
11 | Returns a list of all active users configured within the ZDX tenant.
12 |
13 | Keyword Args:
14 | since (int): The number of hours to look back for devices.
15 | location_id (str): The unique ID for the location.
16 | department_id (str): The unique ID for the department.
17 | geo_id (str): The unique ID for the geolocation.
18 |
19 | Returns:
20 | :obj:`BoxList`: The list of users in ZDX.
21 |
22 | Examples:
23 | List all users in ZDX for the past 2 hours:
24 |
25 | >>> for user in zdx.users.list_users():
26 | ... print(user)
27 |
28 | """
29 | return BoxList(ZDXIterator(self._api, "users", **kwargs))
30 |
31 | @zdx_params
32 | def get_user(self, user_id: str, **kwargs):
33 | """
34 | Returns information on the specified user configured within the ZDX tenant.
35 |
36 | Args:
37 | user_id (str): The unique ID for the ZDX user.
38 |
39 | Keyword Args:
40 | since (int): The number of hours to look back for devices.
41 | location_id (str): The unique ID for the location.
42 | department_id (str): The unique ID for the department.
43 | geo_id (str): The unique ID for the geolocation.
44 |
45 | Returns:
46 | :obj:`Box`: The user information.
47 |
48 | Examples:
49 | Return information on the user with the ID of 999999999:
50 |
51 | >>> zia.users.get_user(user_id='999999999')
52 |
53 | """
54 | return self._get(f"users/{user_id}")
55 |
--------------------------------------------------------------------------------
/pyzscaler/zia/apptotal.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly.endpoint import APIEndpoint
3 |
4 |
5 | class AppTotalAPI(APIEndpoint):
6 | def get_app(self, app_id: str, verbose: bool = False) -> Box:
7 | """
8 | Searches the AppTotal App Catalog by app ID. If the app exists in the catalog, the app's information is
9 | returned. If not, the app is submitted for analysis. After analysis is complete, a subsequent GET request is
10 | required to fetch the app's information.
11 |
12 | Args:
13 | app_id (str): The app ID to search for.
14 | verbose (bool, optional): Defaults to False.
15 |
16 | Returns:
17 | :obj:`Box`: The response object.
18 |
19 | Examples:
20 | Return verbose information on an app with ID 12345::
21 |
22 | zia.apptotal.get_app(app_id="12345", verbose=True)
23 |
24 | """
25 | params = {
26 | "app_id": app_id,
27 | "verbose": verbose,
28 | }
29 | return self._get("apps/app", params=params)
30 |
31 | def scan_app(self, app_id: str) -> Box:
32 | """
33 | Submits an app for analysis in the AppTotal Sandbox. After analysis is complete, a subsequent GET request is
34 | required to fetch the app's information.
35 |
36 | Args:
37 | app_id (str): The app ID to scan.
38 |
39 | Returns:
40 | :obj:`Box`: The response object.
41 |
42 | Examples:
43 | Scan an app with ID 12345::
44 |
45 | zia.apptotal.scan_app(app_id="12345")
46 |
47 | """
48 | payload = {
49 | "appId": app_id,
50 | }
51 | return self._post("apps/app", json=payload)
52 |
--------------------------------------------------------------------------------
/pyzscaler/zia/audit_logs.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly.endpoint import APIEndpoint
3 |
4 |
5 | class AuditLogsAPI(APIEndpoint):
6 | def status(self) -> Box:
7 | """
8 | Get the status of a request for an audit log report.
9 |
10 | Returns:
11 | :obj:`Box`: Audit log report request status.
12 |
13 | Examples:
14 | >>> print(zia.audit_logs.status())
15 |
16 | """
17 | return self._get("auditlogEntryReport")
18 |
19 | def create(self, start_time: str, end_time: str) -> int:
20 | """
21 | Creates an audit log report for the specified time period and saves it as a CSV file. The report
22 | includes audit information for every call made to the cloud service API during the specified time period.
23 | Creating a new audit log report will overwrite a previously-generated report.
24 |
25 | Args:
26 | start_time (str):
27 | The timestamp, in epoch, of the admin's last login.
28 | end_time (str):
29 | The timestamp, in epoch, of the admin's last logout.
30 |
31 | Returns:
32 | :obj:`int`: The status code for the operation.
33 |
34 | Examples:
35 | >>> zia.audit_logs.create(start_time='1627221600000',
36 | ... end_time='1627271676622')
37 |
38 | """
39 | payload = {
40 | "startTime": start_time,
41 | "endTime": end_time,
42 | }
43 | return self._post("auditlogEntryReport", json=payload, box=False).status_code
44 |
45 | def cancel(self) -> int:
46 | """
47 | Cancels the request to create an audit log report.
48 |
49 | Returns:
50 | :obj:`int`: The operation response code.
51 |
52 | Examples:
53 | >>> zia.audit_logs.cancel()
54 |
55 | """
56 | return self._delete("auditlogEntryReport", box=False).status_code
57 |
58 | def get_report(self) -> str:
59 | """
60 | Returns the most recently created audit log report.
61 |
62 | Returns:
63 | :obj:`str`: String representation of CSV file.
64 |
65 | Examples:
66 | Write report to CSV file:
67 |
68 | >>> with open("audit_log.csv", "w+") as fh:
69 | ... fh.write(zia.audit_logs.get_report())
70 |
71 | """
72 | return self._get("auditlogEntryReport/download").text
73 |
--------------------------------------------------------------------------------
/pyzscaler/zia/config.py:
--------------------------------------------------------------------------------
1 | from restfly.endpoint import APIEndpoint
2 |
3 |
4 | class ActivationAPI(APIEndpoint):
5 | def status(self) -> str:
6 | """
7 | Returns the activation status for a configuration change.
8 |
9 | Returns:
10 | :obj:`str`
11 | Configuration status.
12 |
13 | Examples:
14 | >>> config_status = zia.config.status()
15 |
16 | """
17 | return self._get("status").status
18 |
19 | def activate(self) -> str:
20 | """
21 | Activates configuration changes.
22 |
23 | Returns:
24 | :obj:`str`
25 | Configuration status.
26 |
27 | Examples:
28 | >>> config_activate = zia.config.activate()
29 |
30 | """
31 | return self._post("status/activate").status
32 |
--------------------------------------------------------------------------------
/pyzscaler/zia/labels.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import Iterator, convert_keys, snake_to_camel
5 |
6 |
7 | class RuleLabelsAPI(APIEndpoint):
8 | def list_labels(self, **kwargs) -> BoxList:
9 | """
10 | Returns the list of ZIA Rule Labels.
11 |
12 | Keyword Args:
13 | **max_items (int, optional):
14 | The maximum number of items to request before stopping iteration.
15 | **max_pages (int, optional):
16 | The maximum number of pages to request before stopping iteration.
17 | **page_size (int, optional):
18 | Specifies the page size. The default size is 100, but the maximum size is 1000.
19 |
20 | Returns:
21 | :obj:`BoxList`: The list of Rule Labels configured in ZIA.
22 |
23 | Examples:
24 | List Rule Labels using default settings:
25 |
26 | >>> for label in zia.labels.list_labels():
27 | ... print(label)
28 |
29 | List labels, limiting to a maximum of 10 items:
30 |
31 | >>> for label in zia.labels.list_labels(max_items=10):
32 | ... print(label)
33 |
34 | List labels, returning 200 items per page for a maximum of 2 pages:
35 |
36 | >>> for label in zia.labels.list_labels(page_size=200, max_pages=2):
37 | ... print(label)
38 |
39 | """
40 | return BoxList(Iterator(self._api, "ruleLabels", **kwargs))
41 |
42 | def get_label(self, label_id: str) -> Box:
43 | """
44 | Returns the label details for a given Rule Label.
45 |
46 | Args:
47 | label_id (str): The unique identifier for the Rule Label.
48 |
49 | Returns:
50 | :obj:`Box`: The Rule Label resource record.
51 |
52 | Examples:
53 | >>> label = zia.labels.get_label('99999')
54 |
55 | """
56 | return self._get(f"ruleLabels/{label_id}")
57 |
58 | def add_label(self, name: str, **kwargs) -> Box:
59 | """
60 | Creates a new ZIA Rule Label.
61 |
62 | Args:
63 | name (str):
64 | The name of the Rule Label.
65 |
66 | Keyword Args:
67 | description (str):
68 | Additional information about the Rule Label.
69 |
70 | Returns:
71 | :obj:`Box`: The newly added Rule Label resource record.
72 |
73 | Examples:
74 | Add a label with default parameters:
75 |
76 | >>> label = zia.labels.add_label("My New Label")
77 |
78 | Add a label with description:
79 |
80 | >>> label = zia.labels.add_label("My Second Label":
81 | ... description="My second label description")
82 |
83 | """
84 | payload = {"name": name}
85 |
86 | # Add optional parameters to payload
87 | for key, value in kwargs.items():
88 | payload[snake_to_camel(key)] = value
89 |
90 | return self._post("ruleLabels", json=payload)
91 |
92 | def update_label(self, label_id: str, **kwargs):
93 | """
94 | Updates information for the specified ZIA Rule Label.
95 |
96 | Args:
97 | label_id (str): The unique id for the Rule Label that will be updated.
98 |
99 | Keyword Args:
100 | name (str): The name of the Rule Label.
101 | description (str): Additional information for the Rule Label.
102 |
103 | Returns:
104 | :obj:`Box`: The updated Rule Label resource record.
105 |
106 | Examples:
107 | Update the name of a Rule Label:
108 |
109 | >>> label = zia.labels.update_label(99999,
110 | ... name="Updated Label Name")
111 |
112 | Update the name and description of a Rule Label:
113 |
114 | >>> label = zia.labels.update_label(99999,
115 | ... name="Updated Label Name",
116 | ... description="Updated Label Description")
117 |
118 | """
119 | # Get the label data from ZIA
120 | payload = convert_keys(self.get_label(label_id))
121 |
122 | # Add optional parameters to payload
123 | for key, value in kwargs.items():
124 | payload[snake_to_camel(key)] = value
125 |
126 | return self._put(f"ruleLabels/{label_id}", json=payload)
127 |
128 | def delete_label(self, label_id):
129 | """
130 | Deletes the specified Rule Label.
131 |
132 | Args:
133 | label_id (str): The unique identifier of the Rule Label that will be deleted.
134 |
135 | Returns:
136 | :obj:`int`: The response code for the request.
137 |
138 | Examples
139 | >>> user = zia.labels.delete_label('99999')
140 |
141 | """
142 |
143 | return self._delete(f"ruleLabels/{label_id}", box=False).status_code
144 |
--------------------------------------------------------------------------------
/pyzscaler/zia/sandbox.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly import APISession
3 | from restfly.endpoint import APIEndpoint
4 |
5 |
6 | class CloudSandboxAPI(APIEndpoint):
7 | def __init__(self, api: APISession):
8 | super().__init__(api)
9 |
10 | self.sandbox_token = api.sandbox_token
11 | self.env_cloud = api.env_cloud
12 |
13 | def submit_file(self, file: str, force: bool = False) -> Box:
14 | """
15 | Submits a file to the ZIA Advanced Cloud Sandbox for analysis.
16 |
17 | Args:
18 | file (str): The filename that will be submitted for sandbox analysis.
19 | force (bool): Force ZIA to analyse the file even if it has been submitted previously.
20 |
21 | Returns:
22 | :obj:`Box`: The Cloud Sandbox submission response information.
23 |
24 | Examples:
25 | Submit a file in the current directory called malware.exe to the cloud sandbox, forcing analysis.
26 |
27 | >>> zia.sandbox.submit_file('malware.exe', force=True)
28 |
29 | """
30 | with open(file, "rb") as f:
31 | data = f.read()
32 |
33 | params = {
34 | "api_token": self.sandbox_token,
35 | "force": int(force), # convert boolean to int for ZIA
36 | }
37 |
38 | return self._post(f"https://csbapi.{self.env_cloud}.net/zscsb/submit", params=params, data=data)
39 |
40 | def get_quota(self) -> Box:
41 | """
42 | Returns the Cloud Sandbox API quota information for the organisation.
43 |
44 | Returns:
45 | :obj:`Box`: The Cloud Sandbox quota report.
46 |
47 | Examples:
48 | >>> pprint(zia.sandbox.get_quota())
49 |
50 | """
51 | return self._get("sandbox/report/quota")[0]
52 |
53 | def get_report(self, md5_hash: str, report_details: str = "summary") -> Box:
54 | """
55 | Returns the Cloud Sandbox Report for the provided hash.
56 |
57 | Args:
58 | md5_hash (str):
59 | The MD5 hash of the file that was analysed by Cloud Sandbox.
60 | report_details (str):
61 | The type of report. Accepted values are 'full' or 'summary'. Defaults to 'summary'.
62 |
63 | Returns:
64 | :obj:`Box`: The cloud sandbox report.
65 |
66 | Examples:
67 | Get a summary report:
68 |
69 | >>> zia.sandbox.get_report('8350dED6D39DF158E51D6CFBE36FB012')
70 |
71 | Get a full report:
72 |
73 | >>> zia.sandbox.get_report('8350dED6D39DF158E51D6CFBE36FB012', 'full')
74 |
75 | """
76 |
77 | return self._get(f"sandbox/report/{md5_hash}?details={report_details}")
78 |
--------------------------------------------------------------------------------
/pyzscaler/zia/session.py:
--------------------------------------------------------------------------------
1 | from box import Box
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import obfuscate_api_key
5 |
6 |
7 | class AuthenticatedSessionAPI(APIEndpoint):
8 | def status(self) -> Box:
9 | """
10 | Returns the status of the authentication session if it exists.
11 |
12 | Returns:
13 | :obj:`Box`: Session authentication information.
14 |
15 | Examples:
16 | >>> print(zia.session.status())
17 |
18 | """
19 | return self._get("authenticatedSession")
20 |
21 | def create(self, api_key: str, username: str, password: str) -> Box:
22 | """
23 | Creates a ZIA authentication session.
24 |
25 | Args:
26 | api_key (str): The ZIA API Key.
27 | username (str): Username of admin user for the authentication session.
28 | password (str): Password of the admin user for the authentication session.
29 |
30 | Returns:
31 | :obj:`Box`: The authenticated session information.
32 |
33 | Examples:
34 | >>> zia.session.create(api_key='12khsdfh3289',
35 | ... username='admin@example.com',
36 | ... password='MyInsecurePassword')
37 |
38 |
39 | """
40 | api_obf = obfuscate_api_key(api_key)
41 |
42 | payload = {
43 | "apiKey": api_obf["key"],
44 | "username": username,
45 | "password": password,
46 | "timestamp": api_obf["timestamp"],
47 | }
48 |
49 | return self._post("authenticatedSession", json=payload)
50 |
51 | def delete(self) -> int:
52 | """
53 | Ends an authentication session.
54 |
55 | Returns:
56 | :obj:`int`: The status code of the operation.
57 |
58 | Examples:
59 | >>> zia.session.delete()
60 |
61 | """
62 | return self._delete("authenticatedSession", box=False).status_code
63 |
--------------------------------------------------------------------------------
/pyzscaler/zia/vips.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 |
5 | class DataCenterVIPSAPI(APIEndpoint):
6 | def list_public_se(self, cloud: str, continent: str = None) -> Box:
7 | """
8 | Returns a list of the Zscaler Public Service Edge information for the specified cloud.
9 |
10 | Args:
11 | cloud (str): The ZIA cloud that this request applies to.
12 | continent (str, opt):
13 | Filter entries by the specified continent. Accepted values are `apac`, `emea` and `amer`.
14 |
15 | Returns:
16 | :obj:`Box`: The Public Service Edge VIPs
17 |
18 | Examples:
19 | Print all Public Service Edge information for ``zscaler.net``:
20 |
21 | >>> pprint(zia.vips.list_public_se('zscaler'))
22 |
23 | Print all Public Service Edge information for ``zscalerthree.net`` in ``apac``:
24 |
25 | >>> pprint(zia.vips.list_public_se('zscalerthree',
26 | ... continent='apac')
27 |
28 | """
29 |
30 | if continent is not None:
31 | if continent == "amer":
32 | # This return is an edge-case to handle the JSON structure for _americas which is in the format
33 | # continent :_americas. All other continents have whitespace, e.g. continent : emea.
34 | return self._get(f"https://api.config.zscaler.com/{cloud}.net/cenr/json")[f"{cloud}.net"][
35 | "continent :_americas"
36 | ]
37 |
38 | return self._get(f"https://api.config.zscaler.com/{cloud}.net/cenr/json")[f"{cloud}.net"][
39 | f"continent : {continent}"
40 | ]
41 |
42 | return self._get(f"https://api.config.zscaler.com/{cloud}.net/cenr/json")[f"{cloud}.net"]
43 |
44 | def list_ca(self, cloud: str) -> BoxList:
45 | """
46 | Returns a list of Zscaler Central Authority server IPs for the specified cloud.
47 |
48 | Args:
49 | cloud (str): The ZIA cloud that this request applies to.
50 |
51 | Returns:
52 | :obj:`BoxList`: A list of CA server IPs.
53 |
54 | Examples:
55 | Print the IP address of Central Authority servers in `zscalertwo.net`:
56 |
57 | >>> for ip in zia.vips.list_ca('zscalertwo'):
58 | ... print(ip)
59 |
60 | """
61 | return self._get(f"https://api.config.zscaler.com/{cloud}.net/ca/json")["ranges"]
62 |
63 | def list_pac(self, cloud: str) -> BoxList:
64 | """
65 | Returns a list of Proxy Auto-configuration (PAC) server IPs for the specified cloud.
66 |
67 | Args:
68 | cloud (str): The ZIA cloud that this request applies to.
69 |
70 | Returns:
71 | :obj:`BoxList`: A list of PAC server IPs.
72 |
73 | Examples:
74 | Print the IP address of PAC servers in `zscloud.net`:
75 |
76 | >>> for ip in zia.vips.list_pac('zscloud'):
77 | ... print(ip)
78 |
79 | """
80 | return self._get(f"https://api.config.zscaler.com/{cloud}.net/pac/json")["ip"]
81 |
--------------------------------------------------------------------------------
/pyzscaler/zia/web_dlp.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from box import Box, BoxList
4 | from restfly.endpoint import APIEndpoint
5 |
6 |
7 | class WebDLPAPI(APIEndpoint):
8 | def list_rules(self, **kwargs) -> BoxList:
9 | """
10 | Returns a list of DLP policy rules, excluding SaaS Security API DLP policy rules.
11 |
12 | Returns:
13 | :obj:`BoxList`: List of Web DLP items.
14 |
15 | Examples:
16 | Get a list of all Web DLP Items
17 |
18 | >>> results = zia.web_dlp.list_rules()
19 | ... for item in results:
20 | ... print(item)
21 |
22 | """
23 | return self._get("webDlpRules")
24 |
25 | def get_rule(self, rule_id: str) -> Box:
26 | """
27 | Returns a DLP policy rule, excluding SaaS Security API DLP policy rules.
28 |
29 | Args:
30 | rule_id (str): The unique id for the Web DLP rule.
31 |
32 | Returns:
33 | :obj:`Box`: The Web DLP Rule resource record.
34 |
35 | Examples:
36 | Get information on a Web DLP item by ID
37 |
38 | >>> results = zia.web_dlp.get_rule(rule_id='9999')
39 | ... print(results)
40 |
41 | """
42 | return self._get(f"webDlpRules/{rule_id}")
43 |
44 | def list_rules_lite(self) -> BoxList:
45 | """
46 | Returns the name and ID for all DLP policy rules, excluding SaaS Security API DLP policy rules.
47 |
48 | Returns:
49 | :obj:`BoxList`: List of Web DLP name/ids.
50 |
51 | Examples:
52 | Get Web DLP Lite results
53 |
54 | >>> results = zia.web_dlp.list_rules_lite()
55 | ... for item in results:
56 | ... print(item)
57 |
58 | """
59 | return self._get("webDlpRules/lite")
60 |
61 | def add_rule(self, payload: json) -> Box:
62 | """
63 | Adds a new DLP policy rule.
64 |
65 | Args:
66 | payload (dict): Dictionary containing the Web DLP Policy rule to be added.
67 |
68 | Returns:
69 | :obj:`Box`: The newly added Web DLP Policy Rule resource record.
70 |
71 | Payload:
72 | Minimum items required in payload::
73 |
74 | payload = {
75 | 'order': 1, # A number greater than 0.
76 | 'rank': 0,
77 | 'name': "Dax testing pyZscaler post.",
78 | 'protocols': ["ANY_RULE"],
79 | 'action': "ALLOW",
80 | }
81 |
82 | Examples:
83 | Add a Web DLP Policy rule with the minimum required parameters::
84 |
85 | payload = {
86 | 'order': 1,
87 | 'rank': 0,
88 | 'name': "Dax testing pyZscaler post.",
89 | 'protocols': ["ANY_RULE"],
90 | 'action': "ALLOW",
91 | }
92 |
93 | # Add new Web DLP item
94 | print(zia.web_dlp.add_rule(payload=payload))
95 |
96 | """
97 | return self._post("webDlpRules", json=payload)
98 |
99 | def update_rule(self, rule_id: str, payload: dict) -> Box:
100 | """
101 | Updates a DLP policy rule. This endpoint is not applicable to SaaS Security API DLP policy rules.
102 |
103 | Args:
104 | rule_id (str): String of ID.
105 | payload (dict): Dictionary containing the updated Web DLP Policy Rule.
106 |
107 | Returns:
108 | :obj:`Box`: The updated Web DLP Policy Rule resource record.
109 |
110 | Examples:
111 | Update a Web DLP Policy Rule::
112 |
113 | payload = zia.web_dlp.get_rule('9999')
114 | payload['name'] = "daxm updated name."
115 | results = zia.web_dlp.update_rule(rule_id=9999, payload=payload)
116 | print(results)
117 |
118 | """
119 | return self._put(f"webDlpRules/{rule_id}", json=payload)
120 |
121 | def delete_rule(self, rule_id: str) -> Box:
122 | """
123 | Deletes a DLP policy rule. This endpoint is not applicable to SaaS Security API DLP policy rules.
124 |
125 | Args:
126 | rule_id (str): Unique id of the Web DLP Policy Rule that will be deleted.
127 |
128 | Returns:
129 | :obj:`Box`: Response message from the ZIA API endpoint.
130 |
131 | Examples:
132 | Delete a rule with an id of 9999.
133 |
134 | >>> results = zia.web_dlp.delete_rule(rule_id=9999)
135 | ... print(results)
136 |
137 |
138 | """
139 | return self._delete(f"webDlpRules/{rule_id}")
140 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/certificates.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint, APISession
3 |
4 | from pyzscaler.utils import Iterator
5 |
6 |
7 | class CertificatesAPI(APIEndpoint):
8 | def __init__(self, api: APISession):
9 | super().__init__(api)
10 |
11 | self.v2_url = api.v2_url
12 |
13 | def list_browser_access(self, **kwargs) -> BoxList:
14 | """
15 | Returns a list of all Browser Access certificates.
16 |
17 | Args:
18 | **kwargs: Optional keyword args.
19 |
20 | Keyword Args:
21 | **max_items (int, optional):
22 | The maximum number of items to request before stopping iteration.
23 | **max_pages (int, optional):
24 | The maximum number of pages to request before stopping iteration.
25 | **pagesize (int, optional):
26 | Specifies the page size. The default size is 20, but the maximum size is 500.
27 | **search (str, optional):
28 | The search string used to match against features and fields.
29 |
30 | Returns:
31 | :obj:`BoxList`: List of all Browser Access certificates.
32 |
33 | Examples:
34 | >>> for cert in zpa.certificates.list_browser_access():
35 | ... print(cert)
36 |
37 | """
38 | return BoxList(Iterator(self._api, f"{self.v2_url}/clientlessCertificate/issued", **kwargs))
39 |
40 | def get_browser_access(self, certificate_id: str) -> Box:
41 | """
42 | Returns information on a specified Browser Access certificate.
43 |
44 | Args:
45 | certificate_id (str):
46 | The unique identifier for the Browser Access certificate.
47 |
48 | Returns:
49 | :obj:`Box`:
50 | The Browser Access certificate resource record.
51 |
52 | Examples:
53 | >>> ba_certificate = zpa.certificates.get_browser_access('99999')
54 |
55 | """
56 | return self._get(f"clientlessCertificate/{certificate_id}")
57 |
58 | def get_enrolment(self, certificate_id: str) -> Box:
59 | """
60 | Returns information on the specified enrollment certificate.
61 |
62 | Args:
63 | certificate_id (str): The unique id of the enrollment certificate.
64 |
65 | Returns:
66 | :obj:`Box`: The enrollment certificate resource record.
67 |
68 | Examples:
69 | enrolment_cert = zpa.certificates.get_enrolment('99999999')
70 |
71 | """
72 | return self._get(f"enrollmentCert/{certificate_id}")
73 |
74 | def list_enrolment(self, **kwargs) -> BoxList:
75 | """
76 | Returns a list of all configured enrollment certificates.
77 |
78 | Args:
79 | **kwargs: Optional keyword args.
80 |
81 | Keyword Args:
82 | **max_items (int, optional):
83 | The maximum number of items to request before stopping iteration.
84 | **max_pages (int, optional):
85 | The maximum number of pages to request before stopping iteration.
86 | **pagesize (int, optional):
87 | Specifies the page size. The default size is 20, but the maximum size is 500.
88 | **search (str, optional):
89 | The search string used to match against features and fields.
90 |
91 | Returns:
92 | :obj:`BoxList`: List of all enrollment certificates.
93 |
94 | Examples:
95 | >>> for cert in zpa.certificates.list_enrolment():
96 | ... print(cert)
97 |
98 | """
99 | return BoxList(Iterator(self._api, f"{self.v2_url}/enrollmentCert", **kwargs))
100 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/cloud_connector_groups.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import Iterator
5 |
6 |
7 | class CloudConnectorGroupsAPI(APIEndpoint):
8 | def list_groups(self, **kwargs) -> BoxList:
9 | """
10 | Returns a list of all configured cloud connector groups.
11 |
12 | Keyword Args:
13 | **max_items (int):
14 | The maximum number of items to request before stopping iteration.
15 | **max_pages (int):
16 | The maximum number of pages to request before stopping iteration.
17 | **pagesize (int):
18 | Specifies the page size. The default size is 20, but the maximum size is 500.
19 | **search (str, optional):
20 | The search string used to match against features and fields.
21 |
22 | Returns:
23 | :obj:`BoxList`: A list of all configured cloud connector groups.
24 |
25 | Examples:
26 | >>> for cloud_connector_group in zpa.cloud_connector_groups.list_groups():
27 | ... pprint(cloud_connector_group)
28 |
29 | """
30 | return BoxList(Iterator(self._api, "cloudConnectorGroup", **kwargs))
31 |
32 | def get_group(self, group_id: str) -> Box:
33 | """
34 | Returns information on the specified cloud connector group.
35 |
36 | Args:
37 | group_id (str):
38 | The unique identifier for the cloud connector group.
39 |
40 | Returns:
41 | :obj:`Box`: The resource record for the cloud connector group.
42 |
43 | Examples:
44 | >>> pprint(zpa.cloud_connector_groups.get_group('99999'))
45 |
46 | """
47 |
48 | return self._get(f"cloudConnectorGroup/{group_id}")
49 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/idp.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly import APISession
3 | from restfly.endpoint import APIEndpoint
4 |
5 | from pyzscaler.utils import Iterator
6 |
7 |
8 | class IDPControllerAPI(APIEndpoint):
9 | def __init__(self, api: APISession):
10 | super().__init__(api)
11 |
12 | self.v2_url = api.v2_url
13 |
14 | def list_idps(self, **kwargs) -> BoxList:
15 | """
16 | Returns a list of all configured IdPs.
17 |
18 | Keyword Args:
19 | **max_items (int):
20 | The maximum number of items to request before stopping iteration.
21 | **max_pages (int):
22 | The maximum number of pages to request before stopping iteration.
23 | **pagesize (int):
24 | Specifies the page size. The default size is 20, but the maximum size is 500.
25 | **scim_enabled (bool):
26 | Returns all SCIM IdPs if ``True``. Returns all non-SCIM IdPs if ``False``.
27 | **search (str, optional):
28 | The search string used to match against features and fields.
29 |
30 | Returns:
31 | :obj:`BoxList`: A list of all configured IdPs.
32 |
33 | Examples:
34 | >>> for idp in zpa.idp.list_idps():
35 | ... pprint(idp)
36 |
37 | """
38 | return BoxList(Iterator(self._api, f"{self.v2_url}/idp", **kwargs))
39 |
40 | def get_idp(self, idp_id: str) -> Box:
41 | """
42 | Returns information on the specified IdP.
43 |
44 | Args:
45 | idp_id (str):
46 | The unique identifier for the IdP.
47 |
48 | Returns:
49 | :obj:`Box`: The resource record for the IdP.
50 |
51 | Examples:
52 | >>> pprint(zpa.idp.get_idp('99999'))
53 |
54 | """
55 |
56 | return self._get(f"idp/{idp_id}")
57 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/machine_groups.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint
3 |
4 | from pyzscaler.utils import Iterator
5 |
6 |
7 | class MachineGroupsAPI(APIEndpoint):
8 | def list_groups(self, **kwargs) -> BoxList:
9 | """
10 | Returns a list of all configured machine groups.
11 |
12 | Keyword Args:
13 | **max_items (int):
14 | The maximum number of items to request before stopping iteration.
15 | **max_pages (int):
16 | The maximum number of pages to request before stopping iteration.
17 | **pagesize (int):
18 | Specifies the page size. The default size is 20, but the maximum size is 500.
19 | **search (str, optional):
20 | The search string used to match against features and fields.
21 |
22 | Returns:
23 | :obj:`list`: A list of all configured machine groups.
24 |
25 | Examples:
26 | >>> for machine_group in zpa.machine_groups.list_groups():
27 | ... pprint(machine_group)
28 |
29 | """
30 | return BoxList(Iterator(self._api, "machineGroup", **kwargs))
31 |
32 | def get_group(self, group_id: str) -> Box:
33 | """
34 | Returns information on the specified machine group.
35 |
36 | Args:
37 | group_id (str):
38 | The unique identifier for the machine group.
39 |
40 | Returns:
41 | :obj:`Box`: The resource record for the machine group.
42 |
43 | Examples:
44 | >>> pprint(zpa.machine_groups.get_group('99999'))
45 |
46 | """
47 |
48 | return self._get(f"machineGroup/{group_id}")
49 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/posture_profiles.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly import APISession
3 | from restfly.endpoint import APIEndpoint
4 |
5 | from pyzscaler.utils import Iterator
6 |
7 |
8 | class PostureProfilesAPI(APIEndpoint):
9 | def __init__(self, api: APISession):
10 | super().__init__(api)
11 |
12 | self.v2_url = api.v2_url
13 |
14 | def list_profiles(self, **kwargs) -> BoxList:
15 | """
16 | Returns a list of all configured posture profiles.
17 |
18 | Keyword Args:
19 | **max_items (int):
20 | The maximum number of items to request before stopping iteration.
21 | **max_pages (int):
22 | The maximum number of pages to request before stopping iteration.
23 | **pagesize (int):
24 | Specifies the page size. The default size is 20, but the maximum size is 500.
25 | **search (str, optional):
26 | The search string used to match against features and fields.
27 |
28 | Returns:
29 | :obj:`BoxList`: A list of all configured posture profiles.
30 |
31 | Examples:
32 | >>> for posture_profile in zpa.posture_profiles.list_profiles():
33 | ... pprint(posture_profile)
34 |
35 | """
36 | return BoxList(Iterator(self._api, f"{self.v2_url}/posture", **kwargs))
37 |
38 | def get_profile(self, profile_id: str) -> Box:
39 | """
40 | Returns information on the specified posture profiles.
41 |
42 | Args:
43 | profile_id (str):
44 | The unique identifier for the posture profiles.
45 |
46 | Returns:
47 | :obj:`Box`: The resource record for the posture profiles.
48 |
49 | Examples:
50 | >>> pprint(zpa.posture_profiles.get_profile('99999'))
51 |
52 | """
53 |
54 | return self._get(f"posture/{profile_id}")
55 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/saml_attributes.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly import APISession
3 | from restfly.endpoint import APIEndpoint
4 |
5 | from pyzscaler.utils import Iterator
6 |
7 |
8 | class SAMLAttributesAPI(APIEndpoint):
9 | def __init__(self, api: APISession):
10 | super().__init__(api)
11 |
12 | self.v2_url = api.v2_url
13 |
14 | def list_attributes(self, **kwargs) -> BoxList:
15 | """
16 | Returns a list of all configured SAML attributes.
17 |
18 | Keyword Args:
19 | **max_items (int):
20 | The maximum number of items to request before stopping iteration.
21 | **max_pages (int):
22 | The maximum number of pages to request before stopping iteration.
23 | **pagesize (int):
24 | Specifies the page size. The default size is 20, but the maximum size is 500.
25 | **search (str, optional):
26 | The search string used to match against features and fields.
27 |
28 | Returns:
29 | :obj:`BoxList`: A list of all configured SAML attributes.
30 |
31 | Examples:
32 | >>> for saml_attribute in zpa.saml_attributes.list_attributes():
33 | ... pprint(saml_attribute)
34 |
35 | """
36 | return BoxList(Iterator(self._api, f"{self.v2_url}/samlAttribute", **kwargs))
37 |
38 | def list_attributes_by_idp(self, idp_id: str, **kwargs) -> BoxList:
39 | """
40 | Returns a list of all configured SAML attributes for the specified IdP.
41 |
42 | Args:
43 | idp_id (str): The unique id of the IdP to retrieve SAML attributes from.
44 |
45 | Keyword Args:
46 | **max_items (int):
47 | The maximum number of items to request before stopping iteration.
48 | **max_pages (int):
49 | The maximum number of pages to request before stopping iteration.
50 | **pagesize (int):
51 | Specifies the page size. The default size is 20, but the maximum size is 500.
52 | **search (str, optional):
53 | The search string used to match against features and fields.
54 |
55 | Returns:
56 | :obj:`BoxList`: A list of all configured SAML attributes for the specified IdP.
57 |
58 | Examples:
59 | >>> for saml_attribute in zpa.saml_attributes.list_attributes_by_idp('99999'):
60 | ... pprint(saml_attribute)
61 |
62 | """
63 | return BoxList(Iterator(self._api, f"{self.v2_url}/samlAttribute/idp/{idp_id}", **kwargs))
64 |
65 | def get_attribute(self, attribute_id: str) -> Box:
66 | """
67 | Returns information on the specified SAML attributes.
68 |
69 | Args:
70 | attribute_id (str):
71 | The unique identifier for the SAML attributes.
72 |
73 | Returns:
74 | :obj:`dict`: The resource record for the SAML attributes.
75 |
76 | Examples:
77 | >>> pprint(zpa.saml_attributes.get_attribute('99999'))
78 |
79 | """
80 |
81 | return self._get(f"samlAttribute/{attribute_id}")
82 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/scim_attributes.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint, APISession
3 |
4 | from pyzscaler.utils import Iterator
5 |
6 |
7 | class SCIMAttributesAPI(APIEndpoint):
8 | def __init__(self, api: APISession):
9 | super().__init__(api)
10 | self.user_config_url = api.user_config_url
11 |
12 | def list_attributes_by_idp(self, idp_id: str, **kwargs) -> BoxList:
13 | """
14 | Returns a list of all configured SCIM attributes for the specified IdP.
15 |
16 | Args:
17 | idp_id (str): The unique id of the IdP to retrieve SCIM attributes for.
18 | **kwargs: Optional keyword args.
19 |
20 | Keyword Args:
21 | **max_items (int):
22 | The maximum number of items to request before stopping iteration.
23 | **max_pages (int):
24 | The maximum number of pages to request before stopping iteration.
25 | **pagesize (int):
26 | Specifies the page size. The default size is 20, but the maximum size is 500.
27 | **search (str, optional):
28 | The search string used to match against features and fields.
29 |
30 | Returns:
31 | :obj:`BoxList`: A list of all configured SCIM attributes for the specified IdP.
32 |
33 | Examples:
34 | >>> for scim_attribute in zpa.scim_attributes.list_attributes_by_idp('99999'):
35 | ... pprint(scim_attribute)
36 |
37 | """
38 | return BoxList(Iterator(self._api, f"idp/{idp_id}/scimattribute", **kwargs))
39 |
40 | def get_attribute(self, idp_id: str, attribute_id: str) -> Box:
41 | """
42 | Returns information on the specified SCIM attribute.
43 |
44 | Args:
45 | idp_id (str):
46 | The unique id of the Idp corresponding to the SCIM attribute.
47 | attribute_id (str):
48 | The unique id of the SCIM attribute.
49 |
50 | Returns:
51 | :obj:`Box`: The resource record for the SCIM attribute.
52 |
53 | Examples:
54 | >>> pprint(zpa.scim_attributes.get_attribute('99999',
55 | ... scim_attribute_id="88888"))
56 |
57 | """
58 |
59 | return self._get(f"idp/{idp_id}/scimattribute/{attribute_id}")
60 |
61 | def get_values(self, idp_id: str, attribute_id: str, **kwargs) -> BoxList:
62 | """
63 | Returns information on the specified SCIM attributes.
64 |
65 | Args:
66 | idp_id (str):
67 | The unique identifier for the IDP.
68 | attribute_id (str):
69 | The unique identifier for the attribute.
70 | **kwargs:
71 | Optional keyword args.
72 |
73 | Keyword Args:
74 | **max_items (int):
75 | The maximum number of items to request before stopping iteration.
76 | **max_pages (int):
77 | The maximum number of pages to request before stopping iteration.
78 | **pagesize (int):
79 | Specifies the page size. The default size is 20, but the maximum size is 500.
80 | **search (str, optional):
81 | The search string used to match against features and fields.
82 |
83 | Returns:
84 | :obj:`BoxList`: The resource record for the SCIM attribute values.
85 |
86 | Examples:
87 | >>> pprint(zpa.scim_attributes.get_values('99999', '88888'))
88 |
89 | """
90 | return BoxList(
91 | Iterator(self._api, f"{self.user_config_url}/scimattribute/idpId/{idp_id}/attributeId/{attribute_id}", **kwargs)
92 | )
93 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/scim_groups.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly.endpoint import APIEndpoint, APISession
3 |
4 | from pyzscaler.utils import Iterator
5 |
6 |
7 | class SCIMGroupsAPI(APIEndpoint):
8 | def __init__(self, api: APISession):
9 | super().__init__(api)
10 | self.user_config_url = api.user_config_url
11 |
12 | def list_groups(self, idp_id: str, **kwargs) -> BoxList:
13 | """
14 | Returns a list of all configured SCIM groups for the specified IdP.
15 |
16 | Args:
17 | idp_id (str):
18 | The unique id of the IdP.
19 |
20 | Keyword Args:
21 | **end_time (str):
22 | The end of a time range for requesting last updated data (modified_time) for the SCIM group.
23 | This requires setting the ``start_time`` parameter as well.
24 | **idp_group_id (str):
25 | The unique id of the IdP group.
26 | **max_items (int):
27 | The maximum number of items to request before stopping iteration.
28 | **max_pages (int):
29 | The maximum number of pages to request before stopping iteration.
30 | **pagesize (int):
31 | Specifies the page size. The default size is 20, but the maximum size is 500.
32 | **scim_user_id (str):
33 | The unique id for the SCIM user.
34 | **search (str, optional):
35 | The search string used to match against features and fields.
36 | **sort_order (str):
37 | Sort the last updated time (modified_time) by ascending ``ASC`` or descending ``DSC`` order. Defaults to
38 | ``DSC``.
39 | **start_time (str):
40 | The start of a time range for requesting last updated data (modified_time) for the SCIM group.
41 | This requires setting the ``end_time`` parameter as well.
42 |
43 | Returns:
44 | :obj:`list`: A list of all configured SCIM groups.
45 |
46 | Examples:
47 | >>> for scim_group in zpa.scim_groups.list_groups("999999"):
48 | ... pprint(scim_group)
49 |
50 | """
51 | return BoxList(Iterator(self._api, f"{self.user_config_url}/scimgroup/idpId/{idp_id}", **kwargs))
52 |
53 | def get_group(self, group_id: str, **kwargs) -> Box:
54 | """
55 | Returns information on the specified SCIM group.
56 |
57 | Args:
58 | group_id (str):
59 | The unique identifier for the SCIM group.
60 | **kwargs:
61 | Optional keyword args.
62 |
63 | Keyword Args:
64 | all_entries (bool):
65 | Return all SCIM groups including the deleted ones if ``True``. Defaults to ``False``.
66 |
67 | Returns:
68 | :obj:`dict`: The resource record for the SCIM group.
69 |
70 | Examples:
71 | >>> pprint(zpa.scim_groups.get_group('99999'))
72 |
73 | """
74 |
75 | return self._get(f"{self.user_config_url}/scimgroup/{group_id}")
76 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/session.py:
--------------------------------------------------------------------------------
1 | from restfly import APISession
2 | from restfly.endpoint import APIEndpoint
3 |
4 |
5 | class AuthenticatedSessionAPI(APIEndpoint):
6 | def __init__(self, api: APISession):
7 | super().__init__(api)
8 |
9 | self.url_base = api.url_base
10 |
11 | def create_token(self, client_id: str, client_secret: str):
12 | """
13 | Creates a ZPA authentication token.
14 |
15 | Args:
16 | client_id (str): The ZPA API Client ID.
17 | client_secret (str): The ZPA API Client Secret Key.
18 |
19 | Returns:
20 | :obj:`dict`: The authenticated session information.
21 |
22 | Examples:
23 | >>> zpa.session.create_token(client_id='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==',
24 | ... client_secret='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')
25 |
26 | """
27 |
28 | payload = {"client_id": client_id, "client_secret": client_secret}
29 |
30 | headers = {
31 | "Content-Type": "application/x-www-form-urlencoded",
32 | }
33 | return self._post(f"{self.url_base}/signin", headers=headers, data=payload).access_token
34 |
--------------------------------------------------------------------------------
/pyzscaler/zpa/trusted_networks.py:
--------------------------------------------------------------------------------
1 | from box import Box, BoxList
2 | from restfly import APISession
3 | from restfly.endpoint import APIEndpoint
4 |
5 | from pyzscaler.utils import Iterator
6 |
7 |
8 | class TrustedNetworksAPI(APIEndpoint):
9 | def __init__(self, api: APISession):
10 | super().__init__(api)
11 |
12 | self.v2_url = api.v2_url
13 |
14 | def list_networks(self, **kwargs) -> BoxList:
15 | """
16 | Returns a list of all configured trusted networks.
17 |
18 | Keyword Args:
19 | **max_items (int):
20 | The maximum number of items to request before stopping iteration.
21 | **max_pages (int):
22 | The maximum number of pages to request before stopping iteration.
23 | **pagesize (int):
24 | Specifies the page size. The default size is 20, but the maximum size is 500.
25 | **search (str, optional):
26 | The search string used to match against features and fields.
27 |
28 | Returns:
29 | :obj:`BoxList`: A list of all configured trusted networks.
30 |
31 | Examples:
32 | >>> for trusted_network in zpa.trusted_networks.list_networks():
33 | ... pprint(trusted_network)
34 |
35 | """
36 | return BoxList(Iterator(self._api, f"{self.v2_url}/network", **kwargs))
37 |
38 | def get_network(self, network_id: str) -> Box:
39 | """
40 | Returns information on the specified trusted network.
41 |
42 | Args:
43 | network_id (str):
44 | The unique identifier for the trusted network.
45 |
46 | Returns:
47 | :obj:`Box`: The resource record for the trusted network.
48 |
49 | Examples:
50 | >>> pprint(zpa.trusted_networks.get_network('99999'))
51 |
52 | """
53 |
54 | return self._get(f"network/{network_id}")
55 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | restfly==1.4.7
2 | python-box==7.0.1
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitchos/pyZscaler/51002a5bec289700e54ffc1e92eb6b8576b6c181/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 |
4 | def stub_sleep(func):
5 | """Decorator to speed up time.sleep function used in any methods under test."""
6 | import time
7 | from time import sleep
8 |
9 | def newsleep(seconds):
10 | sleep_speed_factor = 10.0
11 | sleep(seconds / sleep_speed_factor)
12 |
13 | time.sleep = newsleep
14 |
15 | @wraps(func)
16 | def wrapper(*args, **kwargs):
17 | return func(*args, **kwargs)
18 |
19 | return wrapper
20 |
--------------------------------------------------------------------------------
/tests/test_pyzscaler.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import toml
4 |
5 | import pyzscaler
6 |
7 |
8 | def test_versions_are_in_sync():
9 | """Checks if the pyproject.toml and package.__init__.py __version__ are in sync."""
10 |
11 | path = Path(__file__).resolve().parents[1] / "pyproject.toml"
12 | pyproject = toml.loads(open(str(path)).read())
13 | pyproject_version = pyproject["tool"]["poetry"]["version"]
14 |
15 | package_init_version = pyzscaler.__version__
16 |
17 | assert package_init_version == pyproject_version
18 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | from pyzscaler.utils import zdx_params
2 |
3 |
4 | def test_zdx_params():
5 | @zdx_params
6 | def dummy_function(self, **kwargs):
7 | return kwargs
8 |
9 | result = dummy_function(
10 | None, since=10, search="test_search", location_id="test_loc", department_id="test_dept", geo_id="test_geo"
11 | )
12 |
13 | assert result["to"] is not None
14 | assert result["from"] is not None
15 | assert result["q"] == "test_search"
16 | assert result["loc"] == "test_loc"
17 | assert result["dept"] == "test_dept"
18 | assert result["geo"] == "test_geo"
19 |
--------------------------------------------------------------------------------
/tests/zcc/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 | from pyzscaler.zcc import ZCC
5 |
6 |
7 | @pytest.fixture(name="session")
8 | def fixture_session():
9 | return {
10 | "jwtToken": "ADMIN_LOGIN",
11 | }
12 |
13 |
14 | @pytest.fixture(name="zcc")
15 | @responses.activate
16 | def zcc(session):
17 | responses.add(
18 | responses.POST,
19 | url="https://api-mobile.zscaler.net/papi/auth/v1/login",
20 | content_type="application/json",
21 | json=session,
22 | status=200,
23 | )
24 | return ZCC(
25 | client_id="abc123",
26 | client_secret="999999",
27 | cloud="zscaler",
28 | )
29 |
--------------------------------------------------------------------------------
/tests/zcc/test_zcc_devices.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | @pytest.fixture(name="devices")
9 | def fixture_devices():
10 | return [{"id": 1}, {"id": 2}]
11 |
12 |
13 | @responses.activate
14 | @stub_sleep
15 | def test_list_devices(devices, zcc):
16 | responses.add(
17 | method="GET",
18 | url="https://api-mobile.zscaler.net/papi/public/v1/getDevices?osType=3&pageSize=1&page=1",
19 | json=devices,
20 | status=200,
21 | )
22 | responses.add(
23 | method="GET",
24 | url="https://api-mobile.zscaler.net/papi/public/v1/getDevices?osType=3&pageSize=1&page=2",
25 | json=[],
26 | status=200,
27 | )
28 | resp = zcc.devices.list_devices(os_type="windows", page_size=1)
29 |
30 | assert isinstance(resp, BoxList)
31 | assert resp[0].id == 1
32 |
--------------------------------------------------------------------------------
/tests/zcc/test_zcc_secrets.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box
4 | from responses import matchers
5 |
6 |
7 | @responses.activate
8 | def test_get_otp(zcc):
9 | responses.add(
10 | method="GET",
11 | url="https://api-mobile.zscaler.net/papi/public/v1/getOtp",
12 | json={"otp": "123abc"},
13 | match=[matchers.query_param_matcher({"udid": "999999"})],
14 | status=200,
15 | )
16 | resp = zcc.secrets.get_otp("999999")
17 |
18 | assert isinstance(resp, Box)
19 | assert resp.otp == "123abc"
20 |
21 |
22 | @responses.activate
23 | def test_get_passwords_default(zcc):
24 | responses.add(
25 | method="GET",
26 | url="https://api-mobile.zscaler.net/papi/public/v1/getPasswords",
27 | json={
28 | "logout_pass": "test",
29 | "exit_pass": "test",
30 | "zia_disable_pass": "test",
31 | "zpa_disable_pass": "test",
32 | "zdx_disable_pass": "test",
33 | "uninstall_pass": "test",
34 | },
35 | match=[matchers.query_param_matcher({"username": "test@example.com", "osType": 3})],
36 | status=200,
37 | )
38 | resp = zcc.secrets.get_passwords("test@example.com")
39 |
40 | assert isinstance(resp, Box)
41 | assert resp.logout_pass == "test"
42 |
43 |
44 | @responses.activate
45 | def test_get_passwords_os_type(zcc):
46 | responses.add(
47 | method="GET",
48 | url="https://api-mobile.zscaler.net/papi/public/v1/getPasswords",
49 | json={
50 | "logout_pass": "test",
51 | "exit_pass": "test",
52 | "zia_disable_pass": "test",
53 | "zpa_disable_pass": "test",
54 | "zdx_disable_pass": "test",
55 | "uninstall_pass": "test",
56 | },
57 | match=[matchers.query_param_matcher({"username": "test@example.com", "osType": 1})],
58 | status=200,
59 | )
60 | resp = zcc.secrets.get_passwords("test@example.com", os_type="ios")
61 |
62 | assert isinstance(resp, Box)
63 | assert resp.logout_pass == "test"
64 |
65 |
66 | @responses.activate
67 | def test_get_passwords_error(zcc):
68 | with pytest.raises(Exception) as e_info:
69 | resp = zcc.secrets.get_passwords("test@example.com", os_type="unix")
70 |
--------------------------------------------------------------------------------
/tests/zcc/test_zcc_session.py:
--------------------------------------------------------------------------------
1 | import responses
2 |
3 |
4 | @responses.activate
5 | def test_create_token(zcc, session):
6 | responses.add(
7 | responses.POST,
8 | url="https://api-mobile.zscaler.net/papi/auth/v1/login",
9 | json=session,
10 | status=200,
11 | )
12 |
13 | resp = zcc.session.create_token(client_id="abc123", client_secret="999999")
14 |
15 | assert isinstance(resp, str)
16 | assert resp == "ADMIN_LOGIN"
17 |
--------------------------------------------------------------------------------
/tests/zcon/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 | from pyzscaler.zcon import ZCON
5 |
6 |
7 | @pytest.fixture(name="session")
8 | def fixture_session():
9 | return {
10 | "authType": "ADMIN_LOGIN",
11 | "obfuscateApiKey": False,
12 | "passwordExpiryTime": 0,
13 | "passwordExpiryDays": 0,
14 | }
15 |
16 |
17 | @pytest.fixture(name="zcon")
18 | @responses.activate
19 | def zcon(session):
20 | responses.add(
21 | responses.POST,
22 | url="https://connector.zscaler.net/api/v1/auth",
23 | content_type="application/json",
24 | json=session,
25 | status=200,
26 | )
27 | return ZCON(
28 | username="test@example.com",
29 | password="hunter2",
30 | cloud="zscaler",
31 | api_key="123456789abcdef",
32 | )
33 |
--------------------------------------------------------------------------------
/tests/zcon/test_zcon_config.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box
4 |
5 |
6 | @pytest.fixture(name="activate_status")
7 | def fixture_activate_status():
8 | return {"status": 200}
9 |
10 |
11 | @pytest.fixture(name="status_data")
12 | def fixture_status_data():
13 | return {
14 | "status": "Active",
15 | }
16 |
17 |
18 | @responses.activate
19 | def test_activate_min_args(zcon, activate_status):
20 | responses.add(
21 | method="POST",
22 | url="https://connector.zscaler.net/api/v1/ecAdminActivateStatus/activate",
23 | json=activate_status,
24 | status=200,
25 | )
26 |
27 | resp = zcon.config.activate()
28 | assert isinstance(resp, Box)
29 | assert resp["status"] == 200
30 |
31 |
32 | @responses.activate
33 | def test_force_activate(zcon, activate_status):
34 | responses.add(
35 | method="POST",
36 | url="https://connector.zscaler.net/api/v1/ecAdminActivateStatus/forcedActivate",
37 | json=activate_status,
38 | status=200,
39 | )
40 |
41 | resp = zcon.config.activate(force=True)
42 | assert isinstance(resp, Box)
43 | assert resp["status"] == 200
44 |
45 |
46 | @responses.activate
47 | def test_get_status(zcon, status_data):
48 | responses.add(
49 | method="GET",
50 | url="https://connector.zscaler.net/api/v1/ecAdminActivateStatus",
51 | json=status_data,
52 | status=200,
53 | )
54 |
55 | resp = zcon.config.get_status()
56 | assert isinstance(resp, Box)
57 | assert resp["status"] == "Active"
58 |
--------------------------------------------------------------------------------
/tests/zcon/test_zcon_connectors.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 |
5 | @pytest.fixture
6 | def connector_groups_list():
7 | return [
8 | {
9 | "id": 1,
10 | "name": "GroupA",
11 | "ec_vms": [
12 | {
13 | "id": 10,
14 | "name": "GroupA-VM1",
15 | "ec_instances": [{"id": 100, "name": "Instance1"}, {"id": 101, "name": "Instance2"}],
16 | },
17 | {"id": 11, "name": "GroupA-VM2", "ec_instances": []},
18 | ],
19 | },
20 | {"id": 2, "name": "GroupB", "ec_vms": [{"id": 20, "name": "GroupB-VM1", "ec_instances": []}]},
21 | ]
22 |
23 |
24 | @responses.activate
25 | def test_list_groups(zcon, connector_groups_list):
26 | responses.add(
27 | method="GET",
28 | url="https://connector.zscaler.net/api/v1/ecgroup",
29 | json=connector_groups_list,
30 | status=200,
31 | )
32 |
33 | resp = zcon.connectors.list_groups()
34 | assert isinstance(resp, list)
35 | assert len(resp) == 2
36 | assert resp[0]["id"] == 1
37 | assert resp[0]["name"] == "GroupA"
38 | assert resp[0]["ec_vms"][0]["id"] == 10
39 |
40 |
41 | @responses.activate
42 | def test_get_group(zcon, connector_groups_list):
43 | connector_group = connector_groups_list[0]
44 | responses.add(
45 | method="GET",
46 | url="https://connector.zscaler.net/api/v1/ecgroup/1",
47 | json=connector_group,
48 | status=200,
49 | )
50 |
51 | resp = zcon.connectors.get_group("1")
52 | assert isinstance(resp, dict)
53 | assert resp["id"] == 1
54 | assert resp["name"] == "GroupA"
55 | assert resp["ec_vms"][0]["ec_instances"][0]["id"] == 100
56 |
57 |
58 | @responses.activate
59 | def test_get_vm(zcon, connector_groups_list):
60 | # Mock the API call to get a specific VM
61 | responses.add(
62 | method="GET",
63 | url="https://connector.zscaler.net/api/v1/ecgroup/1/vm/10",
64 | json=connector_groups_list[0]["ec_vms"][0],
65 | status=204,
66 | )
67 |
68 | # Execute the function and get the result
69 | result = zcon.connectors.get_vm("1", "10")
70 |
71 | # Verify the response
72 | assert result["id"] == connector_groups_list[0]["ec_vms"][0]["id"]
73 | assert result["name"] == connector_groups_list[0]["ec_vms"][0]["name"]
74 |
75 |
76 | @responses.activate
77 | def test_delete_vm(zcon):
78 | # Mock the API call to delete a specific VM
79 | group_id = "1"
80 | vm_id = "10"
81 | responses.add(
82 | method="DELETE",
83 | url=f"https://connector.zscaler.net/api/v1/ecgroup/{group_id}/vm/{vm_id}",
84 | status=204,
85 | )
86 |
87 | # Execute the function and get the result
88 | status_code = zcon.connectors.delete_vm(group_id, vm_id)
89 |
90 | # Verify the response
91 | assert status_code == 204
92 |
--------------------------------------------------------------------------------
/tests/zcon/test_zcon_session.py:
--------------------------------------------------------------------------------
1 | import responses
2 |
3 |
4 | @responses.activate
5 | def test_create(zcon, session):
6 | responses.add(
7 | responses.POST,
8 | url="https://connector.zscaler.net/api/v1/auth",
9 | json=session,
10 | status=200,
11 | )
12 |
13 | resp = zcon.session.create(api_key="test1234567890", username="test@example.com", password="hunter2")
14 |
15 | assert isinstance(resp, dict)
16 | assert resp.auth_type == "ADMIN_LOGIN"
17 | assert resp.obfuscate_api_key is False
18 | assert resp.password_expiry_time == 0
19 | assert resp.password_expiry_days == 0
20 |
--------------------------------------------------------------------------------
/tests/zdx/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 | from pyzscaler.zdx import ZDX
5 |
6 |
7 | @pytest.fixture(name="session")
8 | def fixture_session():
9 | return {
10 | "token": "ADMIN_LOGIN",
11 | }
12 |
13 |
14 | @pytest.fixture(name="zdx")
15 | @responses.activate
16 | def zdx(session):
17 | responses.add(
18 | responses.POST,
19 | url="https://api.zdxcloud.net/v1/oauth/token",
20 | content_type="application/json",
21 | json=session,
22 | status=200,
23 | )
24 |
25 | return ZDX(
26 | client_id="abc123",
27 | client_secret="999999",
28 | )
29 |
--------------------------------------------------------------------------------
/tests/zdx/test_zdx_admin.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import BoxList
3 |
4 | from pyzscaler.utils import calculate_epoch
5 |
6 |
7 | @responses.activate
8 | def test_list_geolocations(zdx):
9 | # set up the mock response
10 |
11 | mock_response = [
12 | {
13 | "id": "1",
14 | "name": "geolocation1",
15 | "geo_type": "region",
16 | "children": [{"id": "11", "description": "child geolocation1", "geo_type": "country"}],
17 | },
18 | {
19 | "id": "2",
20 | "name": "geolocation2",
21 | "geo_type": "region",
22 | "children": [{"id": "21", "description": "child geolocation2", "geo_type": "country"}],
23 | },
24 | ]
25 | current, past = calculate_epoch(2)
26 | url = "https://api.zdxcloud.net/v1/active_geo"
27 | responses.add(responses.GET, url, json=mock_response, status=200)
28 |
29 | # call the method
30 | result = zdx.admin.list_geolocations()
31 |
32 | # assert the response is correct
33 | assert isinstance(result, BoxList)
34 | assert len(result) == 2
35 | assert result[0]["id"] == "1"
36 | assert result[1]["id"] == "2"
37 |
38 | # assert the request is correct
39 | request = responses.calls[0].request
40 | assert request.url == url
41 | assert request.method == "GET"
42 |
43 |
44 | @responses.activate
45 | def test_list_departments(zdx):
46 | url = "https://api.zdxcloud.net/v1/administration/departments"
47 | mock_response = [{"id": "1", "name": "department1"}, {"id": "2", "name": "department2"}]
48 | responses.add(responses.GET, url, json=mock_response, status=200)
49 |
50 | # call the method
51 | result = zdx.admin.list_departments()
52 |
53 | # assert the response is correct
54 | assert isinstance(result, BoxList)
55 | assert len(result) == 2
56 | assert result[0]["id"] == "1"
57 | assert result[1]["id"] == "2"
58 |
59 | # assert the request is correct
60 | request = responses.calls[0].request
61 | assert request.url == url
62 | assert request.method == "GET"
63 |
64 |
65 | @responses.activate
66 | def test_list_locations(zdx):
67 | url = "https://api.zdxcloud.net/v1/administration/locations"
68 | mock_response = [{"id": "1", "name": "location1"}, {"id": "2", "name": "location2"}]
69 | responses.add(responses.GET, url, json=mock_response, status=200)
70 |
71 | # call the method
72 | result = zdx.admin.list_locations()
73 |
74 | # assert the response is correct
75 | assert isinstance(result, BoxList)
76 | assert len(result) == 2
77 | assert result[0]["id"] == "1"
78 | assert result[1]["id"] == "2"
79 |
80 | # assert the request is correct
81 | request = responses.calls[0].request
82 | assert request.url == url
83 | assert request.method == "GET"
84 |
--------------------------------------------------------------------------------
/tests/zdx/test_zdx_session.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import Box
3 |
4 | from tests.conftest import stub_sleep
5 |
6 |
7 | @responses.activate
8 | def test_create_token(zdx):
9 | client_id = "999999999"
10 | client_secret = "admin@example.com"
11 | url = "https://api.zdxcloud.net/v1/oauth/token"
12 | mock_response = {"token": "test_token", "token_type": "Bearer", "expires_in": 3600}
13 | responses.add(responses.POST, url, json=mock_response, status=200)
14 |
15 | result = zdx.session.create_token(client_id, client_secret)
16 |
17 | assert isinstance(result, Box)
18 | assert result.token == mock_response["token"]
19 | assert result.token_type == mock_response["token_type"]
20 | assert result.expires_in == mock_response["expires_in"]
21 |
22 |
23 | @responses.activate
24 | @stub_sleep
25 | def test_validate_token(zdx):
26 | url = "https://api.zdxcloud.net/v1/oauth/validate"
27 | mock_response = {"valid": True}
28 | responses.add(responses.GET, url, json=mock_response, status=200)
29 |
30 | result = zdx.session.validate_token()
31 |
32 | assert isinstance(result, Box)
33 | assert result.valid == mock_response["valid"]
34 |
35 |
36 | @responses.activate
37 | def test_get_jwks(zdx):
38 | url = "https://api.zdxcloud.net/v1/oauth/jwks"
39 | mock_response = {
40 | "keys": [{"alg": "RS256", "kty": "RSA", "use": "sig", "x5c": ["test_string"], "kid": "test_kid", "x5t": "test_x5t"}]
41 | }
42 | responses.add(responses.GET, url, json=mock_response, status=200)
43 |
44 | result = zdx.session.get_jwks()
45 |
46 | assert isinstance(result, Box)
47 | assert result.get("keys")[0].get("alg") == mock_response["keys"][0]["alg"]
48 |
--------------------------------------------------------------------------------
/tests/zdx/test_zdx_users.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import Box, BoxList
3 |
4 |
5 | @responses.activate
6 | def test_list_users(zdx):
7 | url = "https://api.zdxcloud.net/v1/users"
8 | mock_response = {"users": [{"id": 0, "name": "string", "email": "string"}], "next_offset": None}
9 | responses.add(responses.GET, url, json=mock_response, status=200)
10 |
11 | result = zdx.users.list_users()
12 |
13 | assert isinstance(result, BoxList)
14 | assert result == BoxList(mock_response["users"])
15 |
16 |
17 | @responses.activate
18 | def test_get_user(zdx):
19 | user_id = "999999999"
20 | url = f"https://api.zdxcloud.net/v1/users/{user_id}"
21 | mock_response = {
22 | "id": 0,
23 | "name": "string",
24 | "email": "string",
25 | "devices": [
26 | {
27 | "id": 0,
28 | "name": "string",
29 | "geo_loc": [
30 | {
31 | "id": "string",
32 | "city": "string",
33 | "state": "string",
34 | "country": "string",
35 | "geo_type": "string",
36 | "geo_lat": "string",
37 | "geo_long": "string",
38 | "geo_detection": "string",
39 | }
40 | ],
41 | "zs_loc": [{"id": 0, "name": "string"}],
42 | }
43 | ],
44 | }
45 | responses.add(responses.GET, url, json=mock_response, status=200)
46 |
47 | result = zdx.users.get_user(user_id)
48 |
49 | assert isinstance(result, Box)
50 | assert result == Box(mock_response)
51 |
--------------------------------------------------------------------------------
/tests/zia/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 | from pyzscaler.zia import ZIA
5 |
6 |
7 | @pytest.fixture(name="session")
8 | def fixture_session():
9 | return {
10 | "authType": "ADMIN_LOGIN",
11 | "obfuscateApiKey": False,
12 | "passwordExpiryTime": 0,
13 | "passwordExpiryDays": 0,
14 | }
15 |
16 |
17 | @pytest.fixture(name="zia")
18 | @responses.activate
19 | def zia(session):
20 | responses.add(
21 | responses.POST,
22 | url="https://zsapi.zscaler.net/api/v1/authenticatedSession",
23 | content_type="application/json",
24 | json=session,
25 | status=200,
26 | )
27 | return ZIA(
28 | username="test@example.com",
29 | password="hunter2",
30 | cloud="zscaler",
31 | api_key="123456789abcdef",
32 | sandbox_token="SANDBOXTOKEN",
33 | )
34 |
35 |
36 | @pytest.fixture(name="paginated_items")
37 | def fixture_pagination_items():
38 | def _method(num):
39 | items = []
40 | for x in range(0, num):
41 | items.append({"id": x})
42 | return items
43 |
44 | return _method
45 |
--------------------------------------------------------------------------------
/tests/zia/test_apptotal.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import Box
3 |
4 |
5 | @responses.activate
6 | def test_get_app(zia):
7 | app_info = {"id": "12345", "name": "AppName", "verbose": False}
8 | responses.add(
9 | method="GET", url="https://zsapi.zscaler.net/api/v1/apps/app?app_id=12345&verbose=False", json=app_info, status=200
10 | )
11 | resp = zia.apptotal.get_app(app_id="12345", verbose=False)
12 | assert isinstance(resp, Box)
13 | assert resp.id == "12345"
14 | assert resp.name == "AppName"
15 |
16 |
17 | @responses.activate
18 | def test_scan_app(zia):
19 | scan_status = {
20 | "id": "12345",
21 | "status": "Scanned",
22 | }
23 | responses.add(method="POST", url="https://zsapi.zscaler.net/api/v1/apps/app", json=scan_status, status=200)
24 | resp = zia.apptotal.scan_app(app_id="12345")
25 | assert isinstance(resp, Box)
26 | assert resp.id == "12345"
27 | assert resp.status == "Scanned"
28 |
--------------------------------------------------------------------------------
/tests/zia/test_audit_logs.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import Box
3 |
4 |
5 | @responses.activate
6 | def test_audit_log_create(zia):
7 | responses.add(
8 | method="POST",
9 | url="https://zsapi.zscaler.net/api/v1/auditlogEntryReport",
10 | status=204,
11 | )
12 | resp = zia.audit_logs.create(start_time="1627221600000", end_time="1627271676622")
13 |
14 | assert isinstance(resp, int)
15 | assert resp == 204
16 |
17 |
18 | @responses.activate
19 | def test_audit_log_status(zia):
20 | audit_log_status = {
21 | "error_code": None,
22 | "error_message": None,
23 | "progress_end_time": 0,
24 | "progress_items_complete": 0,
25 | "status": "EXECUTING",
26 | }
27 | responses.add(
28 | method="GET",
29 | url="https://zsapi.zscaler.net/api/v1/auditlogEntryReport",
30 | json=audit_log_status,
31 | status=200,
32 | )
33 | resp = zia.audit_logs.status()
34 | assert isinstance(resp, Box)
35 | assert resp.status == "EXECUTING"
36 |
37 |
38 | @responses.activate
39 | def test_audit_log_cancel(zia):
40 | responses.add(
41 | method="DELETE",
42 | url="https://zsapi.zscaler.net/api/v1/auditlogEntryReport",
43 | status=204,
44 | )
45 | resp = zia.audit_logs.cancel()
46 | assert isinstance(resp, int)
47 | assert resp == 204
48 |
49 |
50 | @responses.activate
51 | def test_audit_log_get_report(zia):
52 | audit_report = (
53 | "Administrator,admin@test.example.com\n"
54 | 'Report Created,"31 Dec 2021 23:59:59, AEDT"\n'
55 | 'Start Time,"31 Dec 2021 23:59:59, AEST"\n'
56 | 'End Time,"31 Dec 2021 23:59:59, AEST"\n'
57 | "Time,User,Action,AA in Cloud,Result,Client "
58 | "IP,Interface,Category,Subcategory,Resource,Pre Action,Post Action\n"
59 | '"31 Dec 2021 23:59:59, AEST",admin@test.example.com,Sign '
60 | "In,zscaler.net,Successful,203.0.113.1,UI,Login,Login,,,,\n"
61 | )
62 |
63 | responses.add(
64 | method="GET",
65 | url="https://zsapi.zscaler.net/api/v1/auditlogEntryReport/download",
66 | body=audit_report,
67 | status=200,
68 | )
69 | resp = zia.audit_logs.get_report()
70 | assert isinstance(resp, str)
71 | assert resp == audit_report
72 |
--------------------------------------------------------------------------------
/tests/zia/test_config.py:
--------------------------------------------------------------------------------
1 | import responses
2 |
3 |
4 | @responses.activate
5 | def test_config_status(zia):
6 | responses.add(
7 | responses.GET,
8 | url="https://zsapi.zscaler.net/api/v1/status",
9 | json={"status": "ACTIVE"},
10 | status=200,
11 | )
12 | resp = zia.config.status()
13 |
14 | assert resp == "ACTIVE"
15 |
16 |
17 | @responses.activate
18 | def test_config_activation(zia):
19 | responses.add(
20 | responses.POST,
21 | url="https://zsapi.zscaler.net/api/v1/status/activate",
22 | json={"status": "ACTIVE"},
23 | status=200,
24 | )
25 | resp = zia.config.activate()
26 |
27 | assert resp == "ACTIVE"
28 |
--------------------------------------------------------------------------------
/tests/zia/test_init.py:
--------------------------------------------------------------------------------
1 | import responses
2 |
3 |
4 | @responses.activate
5 | def test_zia_deauthenticate(zia):
6 | responses.add(
7 | responses.DELETE,
8 | url="https://zsapi.zscaler.net/api/v1/authenticatedSession",
9 | content_type="application/json",
10 | status=200,
11 | )
12 | with zia:
13 | pass
14 |
--------------------------------------------------------------------------------
/tests/zia/test_sandbox.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import responses
4 | from box import Box
5 | from responses import matchers
6 |
7 |
8 | @responses.activate
9 | def test_sandbox_get_quota(zia):
10 | sandbox_response = [
11 | {
12 | "allowed": 1000,
13 | "scale": "DAYS",
14 | "start_time": -1,
15 | "unused": 1000,
16 | "used": 0,
17 | }
18 | ]
19 |
20 | responses.add(
21 | method="GET",
22 | url="https://zsapi.zscaler.net/api/v1/sandbox/report/quota",
23 | json=sandbox_response,
24 | status=200,
25 | )
26 |
27 | resp = zia.sandbox.get_quota()
28 | assert isinstance(resp, Box)
29 | assert resp.allowed == 1000
30 |
31 |
32 | @responses.activate
33 | def test_sandbox_get_report(zia):
34 | sandbox_response = {"summary": "test"}
35 |
36 | responses.add(
37 | method="GET",
38 | url="https://zsapi.zscaler.net/api/v1/sandbox/report/HASH?details=summary",
39 | json=sandbox_response,
40 | status=200,
41 | )
42 |
43 | resp = zia.sandbox.get_report("HASH")
44 | assert isinstance(resp, Box)
45 | assert resp.summary == "test"
46 |
47 |
48 | @responses.activate
49 | def test_sandbox_submit_file(zia):
50 | with open("sandboxtest.txt", "w") as f:
51 | f.write("Sandbox Test")
52 |
53 | params = {"api_token": "SANDBOXTOKEN", "force": 1}
54 |
55 | responses.add(
56 | method="POST",
57 | url="https://csbapi.zscaler.net/zscsb/submit?api_token=SANDBOXTOKEN&force=1",
58 | json={
59 | "code": 200,
60 | "message": "/submit response OK",
61 | "virus_name": "malicious beha",
62 | "virus_type": "Sandbox Malware",
63 | "file_type": "exe",
64 | "md5": "XYZ",
65 | "sandbox_submission": "Sandbox Malware",
66 | },
67 | match=[matchers.query_param_matcher(params)],
68 | status=200,
69 | )
70 |
71 | resp = zia.sandbox.submit_file("sandboxtest.txt", True)
72 | os.remove("sandboxtest.txt")
73 |
74 | assert resp.code == 200
75 |
--------------------------------------------------------------------------------
/tests/zia/test_session.py:
--------------------------------------------------------------------------------
1 | import responses
2 |
3 |
4 | @responses.activate
5 | def test_create(zia, session):
6 | responses.add(
7 | responses.POST,
8 | url="https://zsapi.zscaler.net/api/v1/authenticatedSession",
9 | json=session,
10 | status=200,
11 | )
12 |
13 | resp = zia.session.create(api_key="test1234567890", username="test@example.com", password="hunter2")
14 |
15 | assert isinstance(resp, dict)
16 | assert resp.auth_type == "ADMIN_LOGIN"
17 | assert resp.obfuscate_api_key is False
18 | assert resp.password_expiry_time == 0
19 | assert resp.password_expiry_days == 0
20 |
21 |
22 | @responses.activate
23 | def test_status(zia, session):
24 | responses.add(
25 | responses.GET,
26 | url="https://zsapi.zscaler.net/api/v1/authenticatedSession",
27 | json=session,
28 | status=200,
29 | )
30 |
31 | resp = zia.session.status()
32 |
33 | assert isinstance(resp, dict)
34 | assert resp.auth_type == "ADMIN_LOGIN"
35 | assert resp.obfuscate_api_key is False
36 | assert resp.password_expiry_time == 0
37 | assert resp.password_expiry_days == 0
38 |
39 |
40 | @responses.activate
41 | def test_delete(zia):
42 | delete_status = {"status": "ACTIVE"}
43 | responses.add(
44 | responses.DELETE,
45 | url="https://zsapi.zscaler.net/api/v1/authenticatedSession",
46 | json=delete_status,
47 | status=200,
48 | )
49 |
50 | resp = zia.session.delete()
51 |
52 | assert isinstance(resp, int)
53 | assert resp == 200
54 |
--------------------------------------------------------------------------------
/tests/zia/test_ssl_inspection.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from box import Box
3 | from responses import matchers
4 |
5 |
6 | @responses.activate
7 | def test_ssl_inspection_get_csr(zia):
8 | responses.add(
9 | method="GET",
10 | url="https://zsapi.zscaler.net/api/v1/sslSettings/downloadcsr",
11 | body="test",
12 | status=200,
13 | )
14 | resp = zia.ssl.get_csr()
15 |
16 | assert isinstance(resp, str)
17 | assert resp == "test"
18 |
19 |
20 | @responses.activate
21 | def test_ssl_inspection_get_intermediate_ca(zia):
22 | intermediate_cert = {
23 | "cert_name": "test.pem",
24 | "city": "Test",
25 | "comm_name": "example.com",
26 | "country": "COUNTRY_TEST",
27 | "dept_name": "Test",
28 | "exp_date": "31-Feb-2029",
29 | "key_size": 2048,
30 | "org_name": "example.com",
31 | "sha1fingerprint": "XX YY ZZ",
32 | "signature_algorithm": "SHA_256",
33 | "state": "TEST",
34 | }
35 | responses.add(
36 | method="GET",
37 | url="https://zsapi.zscaler.net/api/v1/sslSettings/showcert",
38 | json=intermediate_cert,
39 | status=200,
40 | )
41 | resp = zia.ssl.get_intermediate_ca()
42 | assert isinstance(resp, Box)
43 | assert resp.cert_name == "test.pem"
44 |
45 |
46 | @responses.activate
47 | def test_ssl_inspection_generate_csr(zia):
48 | responses.add(
49 | method="POST",
50 | url="https://zsapi.zscaler.net/api/v1/sslSettings/generatecsr",
51 | status=200,
52 | match=[
53 | matchers.json_params_matcher(
54 | {
55 | "certName": "test",
56 | "commName": "test",
57 | "orgName": "test",
58 | "deptName": "test",
59 | "city": "test",
60 | "state": "test",
61 | "country": "test",
62 | "signatureAlgorithm": "test",
63 | }
64 | )
65 | ],
66 | )
67 | resp = zia.ssl.generate_csr(
68 | cert_name="test", cn="test", org="test", dept="test", city="test", state="test", country="test", signature="test"
69 | )
70 |
71 | assert isinstance(resp, int)
72 | assert resp == 200
73 |
74 |
75 | @responses.activate
76 | def test_ssl_inspection_upload_int_ca_cert(zia):
77 | req_file = str.encode("test")
78 |
79 | responses.add(
80 | method="POST",
81 | url="https://zsapi.zscaler.net/api/v1/sslSettings/uploadcert/text",
82 | match=[matchers.multipart_matcher({"fileUpload": req_file})],
83 | status=200,
84 | )
85 | resp = zia.ssl.upload_int_ca_cert(req_file)
86 | assert isinstance(resp, int)
87 | assert resp == 200
88 |
89 |
90 | @responses.activate
91 | def test_ssl_inspection_upload_int_ca_chain(zia):
92 | req_file = str.encode("test")
93 |
94 | responses.add(
95 | method="POST",
96 | url="https://zsapi.zscaler.net/api/v1/sslSettings/uploadcertchain/text",
97 | match=[matchers.multipart_matcher({"fileUpload": req_file})],
98 | status=200,
99 | )
100 | resp = zia.ssl.upload_int_ca_chain(req_file)
101 | assert isinstance(resp, int)
102 | assert resp == 200
103 |
104 |
105 | @responses.activate
106 | def test_ssl_inspection_delete_int_chain(zia):
107 | responses.add(
108 | method="DELETE",
109 | url="https://zsapi.zscaler.net/api/v1/sslSettings/certchain",
110 | status=204,
111 | )
112 | resp = zia.ssl.delete_int_chain()
113 | assert isinstance(resp, int)
114 | assert resp == 204
115 |
--------------------------------------------------------------------------------
/tests/zpa/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 |
4 | from pyzscaler.zpa import ZPA
5 |
6 |
7 | @pytest.fixture(name="session")
8 | def fixture_session():
9 | return {
10 | "token_type": "Bearer",
11 | "access_token": "xyz",
12 | "expires_in": 3600,
13 | }
14 |
15 |
16 | @pytest.fixture(name="zpa")
17 | @responses.activate
18 | def zpa(session):
19 | responses.add(
20 | responses.POST,
21 | url="https://config.private.zscaler.com/signin",
22 | content_type="application/json",
23 | json=session,
24 | status=200,
25 | )
26 | return ZPA(
27 | client_id="1",
28 | client_secret="yyy",
29 | customer_id="1",
30 | )
31 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_certificates.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="certificates")
11 | def fixture_certificates():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_browser_access(zpa, certificates):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/clientlessCertificate/issued?page=1",
21 | json=certificates,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/clientlessCertificate/issued?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.certificates.list_browser_access()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_browser_access(zpa, certificates):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/clientlessCertificate/1",
41 | json=certificates["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.certificates.get_browser_access("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
48 |
49 | @responses.activate
50 | @stub_sleep
51 | def test_list_enrolment(zpa, certificates):
52 | responses.add(
53 | responses.GET,
54 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/enrollmentCert?page=1",
55 | json=certificates,
56 | status=200,
57 | )
58 | responses.add(
59 | responses.GET,
60 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/enrollmentCert?page=2",
61 | json=[],
62 | status=200,
63 | )
64 | resp = zpa.certificates.list_enrolment()
65 | assert isinstance(resp, BoxList)
66 | assert len(resp) == 2
67 | assert resp[0].id == "1"
68 |
69 |
70 | @responses.activate
71 | def test_get_enrolment(zpa, certificates):
72 | responses.add(
73 | responses.GET,
74 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/enrollmentCert/1",
75 | json=certificates["list"][0],
76 | status=200,
77 | )
78 | resp = zpa.certificates.get_enrolment("1")
79 | assert isinstance(resp, Box)
80 | assert resp.id == "1"
81 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_cloud_connector_groups.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="cloud_connector_groups")
11 | def fixture_cloud_connector_groups():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_cloud_connector_groups(zpa, cloud_connector_groups):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/cloudConnectorGroup?page=1",
21 | json=cloud_connector_groups,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/cloudConnectorGroup?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.cloud_connector_groups.list_groups()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_cloud_connector_groups(zpa, cloud_connector_groups):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/cloudConnectorGroup/1",
41 | json=cloud_connector_groups["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.cloud_connector_groups.get_group("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_idp.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | @pytest.fixture(name="idps")
9 | def fixture_idps():
10 | return {
11 | "totalPages": 1,
12 | "list": [
13 | {
14 | "id": "1",
15 | "modifiedTime": "1623042158",
16 | "creationTime": "1623041306",
17 | "modifiedBy": "1",
18 | "name": "Test",
19 | "loginUrl": "https://idp.example.com",
20 | "idpEntityId": "https://idp.example.com/",
21 | "autoProvision": "0",
22 | "signSamlRequest": "1",
23 | "ssoType": ["USER"],
24 | "domainList": ["example.com"],
25 | "useCustomSpMetadata": True,
26 | "scimEnabled": True,
27 | "enableScimBasedPolicy": False,
28 | "disableSamlBasedPolicy": False,
29 | "reauthOnUserUpdate": False,
30 | "scimSharedSecretExists": False,
31 | "enabled": True,
32 | "redirectBinding": False,
33 | },
34 | {
35 | "id": "2",
36 | "modifiedTime": "1623042158",
37 | "creationTime": "1623041306",
38 | "modifiedBy": "1",
39 | "name": "Test",
40 | "loginUrl": "https://idp.example.com",
41 | "idpEntityId": "https://idp.example.com/",
42 | "autoProvision": "0",
43 | "signSamlRequest": "1",
44 | "ssoType": ["USER"],
45 | "domainList": ["example.com"],
46 | "useCustomSpMetadata": True,
47 | "scimEnabled": True,
48 | "enableScimBasedPolicy": False,
49 | "disableSamlBasedPolicy": False,
50 | "reauthOnUserUpdate": False,
51 | "scimSharedSecretExists": False,
52 | "enabled": True,
53 | "redirectBinding": False,
54 | },
55 | ],
56 | }
57 |
58 |
59 | @responses.activate
60 | @stub_sleep
61 | def test_list_idps(zpa, idps):
62 | responses.add(
63 | responses.GET,
64 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/idp?page=1",
65 | json=idps,
66 | status=200,
67 | )
68 | responses.add(
69 | responses.GET,
70 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/idp?page=2",
71 | json=[],
72 | status=200,
73 | )
74 | resp = zpa.idp.list_idps()
75 | assert isinstance(resp, BoxList)
76 | assert len(resp) == 2
77 | assert resp[0].id == "1"
78 |
79 |
80 | @responses.activate
81 | def test_get_idp(zpa, idps):
82 | responses.add(
83 | responses.GET,
84 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/idp/1",
85 | json=idps["list"][0],
86 | status=200,
87 | )
88 | resp = zpa.idp.get_idp("1")
89 | assert isinstance(resp, Box)
90 | assert resp.id == "1"
91 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_machine_groups.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="machine_groups")
11 | def fixture_machine_groups():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_idps(zpa, machine_groups):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/machineGroup?page=1",
21 | json=machine_groups,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/machineGroup?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.machine_groups.list_groups()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_idp(zpa, machine_groups):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/machineGroup/1",
41 | json=machine_groups["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.machine_groups.get_group("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_posture_profiles.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="posture_profiles")
11 | def fixture_posture_profiles():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_posture_profiles(zpa, posture_profiles):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/posture?page=1",
21 | json=posture_profiles,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/posture?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.posture_profiles.list_profiles()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_posture_profiles(zpa, posture_profiles):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/posture/1",
41 | json=posture_profiles["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.posture_profiles.get_profile("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_saml_attributes.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="saml_attributes")
11 | def fixture_saml_attributes():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_saml_attributes(zpa, saml_attributes):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/samlAttribute?page=1",
21 | json=saml_attributes,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/samlAttribute?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.saml_attributes.list_attributes()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | @stub_sleep
38 | def test_list_saml_attributes_by_idp(zpa, saml_attributes):
39 | responses.add(
40 | responses.GET,
41 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/samlAttribute/idp/1?page=1",
42 | json=saml_attributes,
43 | status=200,
44 | )
45 | responses.add(
46 | responses.GET,
47 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/samlAttribute/idp/1?page=2",
48 | json=[],
49 | status=200,
50 | )
51 | resp = zpa.saml_attributes.list_attributes_by_idp("1")
52 | assert isinstance(resp, BoxList)
53 | assert len(resp) == 2
54 | assert resp[0].id == "1"
55 |
56 |
57 | @responses.activate
58 | def test_get_saml_attribute(zpa, saml_attributes):
59 | responses.add(
60 | responses.GET,
61 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/samlAttribute/1",
62 | json=saml_attributes["list"][0],
63 | status=200,
64 | )
65 | resp = zpa.saml_attributes.get_attribute("1")
66 | assert isinstance(resp, Box)
67 | assert resp.id == "1"
68 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_scim_attributes.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="scim_attributes")
11 | def fixture_scim_attributes():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_scim_attributes_by_idp(zpa, scim_attributes):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/idp/1/scimattribute?page=1",
21 | json=scim_attributes,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/idp/1/scimattribute?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.scim_attributes.list_attributes_by_idp("1")
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_scim_attribute(zpa, scim_attributes):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/idp/1/scimattribute/1",
41 | json=scim_attributes["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.scim_attributes.get_attribute("1", "1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
48 |
49 | @responses.activate
50 | @stub_sleep
51 | def test_list_scim_values(zpa, scim_attributes):
52 | responses.add(
53 | responses.GET,
54 | url="https://config.private.zscaler.com/userconfig/v1/customers/1/scimattribute/idpId/1/attributeId/1?page=1",
55 | json=scim_attributes,
56 | status=200,
57 | )
58 | responses.add(
59 | responses.GET,
60 | url="https://config.private.zscaler.com/userconfig/v1/customers/1/scimattribute/idpId/1/attributeId/1?page=2",
61 | json=[],
62 | status=200,
63 | )
64 | resp = zpa.scim_attributes.get_values("1", "1")
65 | assert isinstance(resp, BoxList)
66 | assert len(resp) == 2
67 | assert resp[0].id == "1"
68 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_scim_groups.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="scim_groups")
11 | def fixture_scim_groups():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_scim_groups(zpa, scim_groups):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/userconfig/v1/customers/1/scimgroup/idpId/1",
21 | json=scim_groups,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/userconfig/v1/customers/1/scimgroup/idpId/1",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.scim_groups.list_groups("1")
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_scim_group(zpa, scim_groups):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/userconfig/v1/customers/1/scimgroup/1",
41 | json=scim_groups["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.scim_groups.get_group("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_servers.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 | from responses import matchers
5 |
6 | from tests.conftest import stub_sleep
7 |
8 |
9 | # Don't need to test the data structure as we just have list and get
10 | # methods available. id will suffice until add/update endpoints are available.
11 | @pytest.fixture(name="servers")
12 | def fixture_servers():
13 | return {
14 | "totalPages": 1,
15 | "list": [
16 | {
17 | "id": "1",
18 | "creationTime": "1625698796",
19 | "modifiedBy": "1",
20 | "name": "Test",
21 | "address": "1.1.1.1",
22 | "enabled": True,
23 | "appServerGroupIds": ["1"],
24 | "configSpace": "DEFAULT",
25 | },
26 | {
27 | "id": "2",
28 | "creationTime": "1625698796",
29 | "modifiedBy": "1",
30 | "name": "Test",
31 | "address": "1.1.1.1",
32 | "enabled": True,
33 | "appServerGroupIds": ["1"],
34 | "configSpace": "DEFAULT",
35 | },
36 | ],
37 | }
38 |
39 |
40 | @responses.activate
41 | @stub_sleep
42 | def test_list_servers(zpa, servers):
43 | responses.add(
44 | responses.GET,
45 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server?page=1",
46 | json=servers,
47 | status=200,
48 | )
49 | responses.add(
50 | responses.GET,
51 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server?page=2",
52 | json=[],
53 | status=200,
54 | )
55 | resp = zpa.servers.list_servers()
56 | assert isinstance(resp, BoxList)
57 | assert len(resp) == 2
58 | assert resp[0].id == "1"
59 |
60 |
61 | @responses.activate
62 | def test_get_server(zpa, servers):
63 | responses.add(
64 | responses.GET,
65 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server/1",
66 | json=servers["list"][0],
67 | status=200,
68 | )
69 | resp = zpa.servers.get_server("1")
70 | assert isinstance(resp, Box)
71 | assert resp.id == "1"
72 |
73 |
74 | @responses.activate
75 | def test_delete_server(zpa):
76 | responses.add(
77 | responses.DELETE,
78 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server/1",
79 | status=204,
80 | )
81 | resp = zpa.servers.delete_server("1")
82 | assert isinstance(resp, int)
83 | assert resp == 204
84 |
85 |
86 | @responses.activate
87 | def test_add_server(zpa, servers):
88 | responses.add(
89 | responses.POST,
90 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server",
91 | json=servers["list"][0],
92 | status=200,
93 | match=[
94 | matchers.json_params_matcher(
95 | {
96 | "name": "Test",
97 | "address": "1.1.1.1",
98 | "enabled": True,
99 | "description": "Test",
100 | }
101 | )
102 | ],
103 | )
104 | resp = zpa.servers.add_server(name="Test", address="1.1.1.1", enabled=True, description="Test")
105 |
106 | assert isinstance(resp, Box)
107 | assert resp.id == "1"
108 |
109 |
110 | @responses.activate
111 | def test_update_server(zpa, servers):
112 | updated_server = servers["list"][0]
113 | updated_server["name"] = "Updated Test"
114 | updated_server["appServerGroupIds"] = ["1", "2"]
115 |
116 | responses.add(
117 | responses.GET,
118 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server/1",
119 | json=servers["list"][0],
120 | status=200,
121 | )
122 |
123 | responses.add(
124 | responses.PUT,
125 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server/1",
126 | status=204,
127 | match=[matchers.json_params_matcher(updated_server)],
128 | )
129 |
130 | responses.add(
131 | responses.GET,
132 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/server/1",
133 | json=updated_server,
134 | status=200,
135 | )
136 |
137 | resp = zpa.servers.update_server("1", name="Updated Test", app_server_group_ids=["1", "2"])
138 |
139 | assert isinstance(resp, Box)
140 | assert resp.id == "1"
141 | assert resp.name == updated_server["name"]
142 | assert resp.app_server_group_ids == updated_server["appServerGroupIds"]
143 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_session.py:
--------------------------------------------------------------------------------
1 | import responses
2 | from responses import matchers
3 |
4 |
5 | @responses.activate
6 | def test_create_token(zpa, session):
7 | responses.add(
8 | responses.POST,
9 | url="https://config.private.zscaler.com/signin",
10 | json=session,
11 | status=200,
12 | match=[
13 | matchers.urlencoded_params_matcher(
14 | {
15 | "client_id": "1",
16 | "client_secret": "yyy",
17 | }
18 | ),
19 | matchers.header_matcher({"Content-Type": "application/x-www-form-urlencoded"}),
20 | ],
21 | )
22 |
23 | resp = zpa.session.create_token(client_id="1", client_secret="yyy")
24 |
25 | assert isinstance(resp, str)
26 | assert resp == "xyz"
27 |
--------------------------------------------------------------------------------
/tests/zpa/test_zpa_trusted_networks.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import responses
3 | from box import Box, BoxList
4 |
5 | from tests.conftest import stub_sleep
6 |
7 |
8 | # Don't need to test the data structure as we just have list and get
9 | # methods available. id will suffice until add/update endpoints are available.
10 | @pytest.fixture(name="trusted_networks")
11 | def fixture_trusted_networks():
12 | return {"totalPages": 1, "list": [{"id": "1"}, {"id": "2"}]}
13 |
14 |
15 | @responses.activate
16 | @stub_sleep
17 | def test_list_networks(zpa, trusted_networks):
18 | responses.add(
19 | responses.GET,
20 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/network?page=1",
21 | json=trusted_networks,
22 | status=200,
23 | )
24 | responses.add(
25 | responses.GET,
26 | url="https://config.private.zscaler.com/mgmtconfig/v2/admin/customers/1/network?page=2",
27 | json=[],
28 | status=200,
29 | )
30 | resp = zpa.trusted_networks.list_networks()
31 | assert isinstance(resp, BoxList)
32 | assert len(resp) == 2
33 | assert resp[0].id == "1"
34 |
35 |
36 | @responses.activate
37 | def test_get_network(zpa, trusted_networks):
38 | responses.add(
39 | responses.GET,
40 | url="https://config.private.zscaler.com/mgmtconfig/v1/admin/customers/1/network/1",
41 | json=trusted_networks["list"][0],
42 | status=200,
43 | )
44 | resp = zpa.trusted_networks.get_network("1")
45 | assert isinstance(resp, Box)
46 | assert resp.id == "1"
47 |
--------------------------------------------------------------------------------