├── .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 | [![pyZscaler](https://raw.githubusercontent.com/mitchos/pyZscaler/gh-pages/docs/assets/images/logo.svg)](https://github.com/mitchos/pyZscaler) 2 | # pyZscaler - An unofficial SDK for the Zscaler API 3 | 4 | [![Build Status](https://github.com/mitchos/pyZscaler/actions/workflows/build.yml/badge.svg)](https://github.com/mitchos/pyZscaler/actions/workflows/build.yml) 5 | [![Documentation Status](https://readthedocs.org/projects/pyzscaler/badge/?version=latest)](https://pyzscaler.readthedocs.io/?badge=latest) 6 | [![License](https://img.shields.io/github/license/mitchos/pyZscaler.svg)](https://github.com/mitchos/pyZscaler) 7 | [![Code Quality](https://app.codacy.com/project/badge/Grade/d339fa5d957140f496fdb5c40abc4666)](https://www.codacy.com/gh/mitchos/pyZscaler/dashboard?utm_source=github.com&utm_medium=referral&utm_content=mitchos/pyZscaler&utm_campaign=Badge_Grade) 8 | [![PyPI Version](https://img.shields.io/pypi/v/pyzscaler.svg)](https://pypi.org/project/pyZscaler) 9 | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/pyzscaler.svg)](https://pypi.python.org/pypi/pyzscaler/) 10 | [![GitHub Release](https://img.shields.io/github/release/mitchos/pyZscaler.svg)](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 | --------------------------------------------------------------------------------