From 7cef48f7259edb6950f595e157ae334cff701530 Mon Sep 17 00:00:00 2001 From: carlos Date: Wed, 16 Feb 2022 18:02:17 +0000 Subject: [PATCH 001/222] Fix get_groups() so that it returns groups and not users --- keycloak/keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 4890c3c..38daa63 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -705,7 +705,7 @@ class KeycloakAdmin: """ query = query or {} params_path = {"realm-name": self.realm_name} - url = URL_ADMIN_USERS.format(**params_path) + url = URL_ADMIN_GROUPS.format(**params_path) if "first" in query or "max" in query: return self.__fetch_paginated(url, query) From b57d88471824749f5f959bcd182f65f4ae94afc0 Mon Sep 17 00:00:00 2001 From: Salem Wafi Date: Sat, 5 Mar 2022 10:40:14 -0600 Subject: [PATCH 002/222] Fix the issue of the token getting expire for some functions in the keycloak admin --- keycloak/keycloak_admin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 4890c3c..b77234e 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1348,7 +1348,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.connection.raw_put(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), + data_raw = self.raw_put(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) @@ -1360,7 +1360,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.connection.raw_delete( + data_raw = self.raw_delete( URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) @@ -2337,7 +2337,7 @@ class KeycloakAdmin: :return: UserSessionRepresentation """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) + data_raw = self.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_user_realm_role(self, user_id, payload): @@ -2347,7 +2347,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": str(user_id) } - data_raw = self.connection.raw_delete(URL_ADMIN_DELETE_USER_ROLE.format(**params_path), + data_raw = self.raw_delete(URL_ADMIN_DELETE_USER_ROLE.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) From 17b507427f8feea239dfa9eb59523868599b2082 Mon Sep 17 00:00:00 2001 From: maxnoto Date: Fri, 25 Mar 2022 17:46:16 +0100 Subject: [PATCH 003/222] Bugfix get_group_members --- keycloak/keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 4890c3c..53bec0f 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -762,7 +762,7 @@ class KeycloakAdmin: :return: Keycloak server response (UserRepresentation) """ params_path = {"realm-name": self.realm_name, "id": group_id} - url = URL_ADMIN_USERS.format(**params_path) + url = URL_ADMIN_GROUP_MEMBERS.format(**params_path) if "first" in query or "max" in query: return self.__fetch_paginated(url, query) From 64d74be6afd3e546551429eac047d497caa5c759 Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Tue, 3 May 2022 10:55:38 +0200 Subject: [PATCH 004/222] Add support of TOTP to KeycloakAdmin --- keycloak/keycloak_admin.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 4890c3c..bf09ecd 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -65,6 +65,7 @@ class KeycloakAdmin: _server_url = None _username = None _password = None + _totp = None _realm_name = None _client_id = None _verify = None @@ -75,13 +76,14 @@ class KeycloakAdmin: _custom_headers = None _user_realm_name = None - def __init__(self, server_url, username=None, password=None, realm_name='master', client_id='admin-cli', verify=True, - client_secret_key=None, custom_headers=None, user_realm_name=None, auto_refresh_token=None): + def __init__(self, server_url, username=None, password=None, totp=None, realm_name='master', client_id='admin-cli', + verify=True, client_secret_key=None, custom_headers=None, user_realm_name=None, auto_refresh_token=None): """ :param server_url: Keycloak server url :param username: admin username :param password: admin password + :param totp: Time based OTP :param realm_name: realm name :param client_id: client id :param verify: True if want check connection SSL @@ -93,6 +95,7 @@ class KeycloakAdmin: self.server_url = server_url self.username = username self.password = password + self.totp = totp self.realm_name = realm_name self.client_id = client_id self.verify = verify @@ -168,6 +171,14 @@ class KeycloakAdmin: def password(self, value): self._password = value + @property + def totp(self): + return self._totp + + @totp.setter + def totp(self, value): + self._totp = value + @property def token(self): return self._token @@ -2286,7 +2297,8 @@ class KeycloakAdmin: self.realm_name = self.user_realm_name if self.username and self.password: - self._token = self.keycloak_openid.token(self.username, self.password, grant_type=grant_type) + self._token = self.keycloak_openid.token(self.username, self.password, + grant_type=grant_type, totp=self.totp) headers = { 'Authorization': 'Bearer ' + self.token.get('access_token'), From 25b9097fd10569df023ddf875d3160803e2fc150 Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Tue, 3 May 2022 16:26:43 +0200 Subject: [PATCH 005/222] Add 202 expected return code when update flow #288 --- keycloak/keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 4890c3c..45e3374 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1707,7 +1707,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[202,204]) def get_authentication_flow_execution(self, execution_id): """ From 1f574ab5d8f2faed0b7326b35264268f0572e6d0 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 18 May 2022 20:43:33 +0200 Subject: [PATCH 006/222] fix(release): version bumps for hotfix release Applied version bumps --- docs/source/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9dc7661..166a8f4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -60,9 +60,9 @@ author = 'Marcos Pereira' # built documents. # # The short X.Y version. -version = '0.27.0' +version = '0.27.1' # The full version, including alpha/beta/rc tags. -release = '0.27.0' +release = '0.27.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 556882f..0ffc5aa 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with open("README.md", "r") as fh: setup( name='python-keycloak', - version='0.27.0', + version='0.27.1', url='https://github.com/marcospereirampj/python-keycloak', license='The MIT License', author='Marcos Pereira', From cc82e6a8749da1188a8ce57fa2bd9d4e899233b6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 11:51:53 +0200 Subject: [PATCH 007/222] feat: initial setup of CICD and linting --- .circleci/config.yml | 37 - .github/workflows/bump.yaml | 32 + .github/workflows/daily.yaml | 27 + .github/workflows/lint.yaml | 90 ++ .github/workflows/publish.yaml | 33 + .gitignore | 3 +- .pre-commit-config.yaml | 16 + .readthedocs.yaml | 10 + .releaserc.json | 8 + CHANGELOG.md | 31 +- CODEOWNERS | 1 + CONTRIBUTING.md | 86 ++ MANIFEST.in | 3 + dev-requirements.txt | 5 + docs-requirements.txt | 9 + docs/source/conf.py | 70 +- keycloak/_version.py | 1 + keycloak/authorization/__init__.py | 53 +- keycloak/authorization/policy.py | 5 +- keycloak/connection.py | 102 ++- keycloak/exceptions.py | 26 +- keycloak/keycloak_admin.py | 626 ++++++++----- keycloak/keycloak_openid.py | 140 +-- keycloak/tests/test_connection.py | 191 ---- keycloak/urls_patterns.py | 59 +- pyproject.toml | 6 + requirements.txt | 6 +- setup.py | 65 +- test_keycloak_init.sh | 35 + {keycloak/tests => tests}/__init__.py | 0 tests/conftest.py | 61 ++ tests/test_keycloak_admin.py | 1201 +++++++++++++++++++++++++ tests/test_urls_patterns.py | 26 + tox.env | 4 + tox.ini | 48 + 35 files changed, 2416 insertions(+), 700 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/bump.yaml create mode 100644 .github/workflows/daily.yaml create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/publish.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 .releaserc.json create mode 100644 CODEOWNERS create mode 100644 CONTRIBUTING.md create mode 100644 dev-requirements.txt create mode 100644 docs-requirements.txt create mode 100644 keycloak/_version.py delete mode 100644 keycloak/tests/test_connection.py create mode 100644 pyproject.toml create mode 100755 test_keycloak_init.sh rename {keycloak/tests => tests}/__init__.py (100%) create mode 100644 tests/conftest.py create mode 100644 tests/test_keycloak_admin.py create mode 100644 tests/test_urls_patterns.py create mode 100644 tox.env create mode 100644 tox.ini diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index e2ab2c3..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/python:3.6.1 - - working_directory: ~/repo - - steps: - - checkout - - restore_cache: - keys: - - v1-dependencies-{{ checksum "requirements.txt" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - - run: - name: install dependencies - command: | - python3 -m venv venv - . venv/bin/activate - pip install -r requirements.txt - - - save_cache: - paths: - - ./venv - key: v1-dependencies-{{ checksum "requirements.txt" }} - - - run: - name: run tests - command: | - . venv/bin/activate - python3 -m unittest discover - - - store_artifacts: - path: test-reports - destination: test-reports \ No newline at end of file diff --git a/.github/workflows/bump.yaml b/.github/workflows/bump.yaml new file mode 100644 index 0000000..e562346 --- /dev/null +++ b/.github/workflows/bump.yaml @@ -0,0 +1,32 @@ +name: Bump version + +on: + workflow_run: + workflows: [ "Lint" ] + branches: [ master ] + types: + - completed + +jobs: + tag-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.PAT_TOKEN }} + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: determine-version + run: | + VERSION=$(npx semantic-release --branches master --dry-run | { grep -i 'the next release version is' || test $? = 1; } | sed -E 's/.* ([[:digit:].]+)$/\1/') + echo "VERSION=$VERSION" >> $GITHUB_ENV + id: version + - uses: rickstaa/action-create-tag@v1 + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + with: + tag: v${{ env.VERSION }} + message: "Releasing v${{ env.VERSION }}" + github_token: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml new file mode 100644 index 0000000..7ddc622 --- /dev/null +++ b/.github/workflows/daily.yaml @@ -0,0 +1,27 @@ +name: Daily check + +on: + schedule: + - cron: '0 4 * * *' + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - uses: docker-practice/actions-setup-docker@master + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tests + run: | + tox -e tests diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..2cade19 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,90 @@ +name: Lint + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + check-commits: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: webiny/action-conventional-commits@v1.0.3 + + check-linting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Check linting, formatting + run: | + tox -e check + + check-docs: + runs-on: ubuntu-latest + needs: + - check-commits + - check-linting + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Check documentation build + run: | + tox -e docs + + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + needs: + - check-commits + - check-linting + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - uses: docker-practice/actions-setup-docker@master + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tests + run: | + tox -e tests + + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run build + run: | + tox -e build diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..9519829 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,33 @@ +name: Publish + +on: + push: + tags: + - 'v*' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox wheel twine + - name: Apply the tag version + run: | + version=${{ github.ref_name }} + sed -i 's/__version__ = .*/__version__ = "'${version:1}'"/' keycloak/_version.py + - name: Run build + run: | + tox -e build + - name: Publish to PyPi + env: + TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} + TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + run: | + twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* diff --git a/.gitignore b/.gitignore index 7ea9902..4c8d46d 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,5 @@ ENV/ .idea/ main.py main2.py -s3air-authz-config.json \ No newline at end of file +s3air-authz-config.json +.vscode \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..806a12c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/compilerla/conventional-pre-commit + rev: v1.2.0 + hooks: + - id: conventional-pre-commit + stages: [ commit-msg ] + args: [ ] # optional: list of Conventional Commits types to allow diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..7aa6ce5 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,10 @@ +version: 2 + +build: + os: "ubuntu-20.04" + tools: + python: "3.10" + +python: + install: + - requirements: docs-requirements.txt diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..c89e61e --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,8 @@ +{ + "plugins": ["@semantic-release/commit-analyzer"], + "verifyConditions": false, + "npmPublish": false, + "publish": false, + "fail": false, + "success": false +} diff --git a/CHANGELOG.md b/CHANGELOG.md index c8891db..4492c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,44 @@ -Changelog -============ +# Changelog All notable changes to this project will be documented in this file. ## [0.5.0] - 2017-08-21 -* Basic functions for Keycloak API (well_know, token, userinfo, logout, certs, -entitlement, instropect) +- Basic functions for Keycloak API (well_know, token, userinfo, logout, certs, + entitlement, instropect) ## [0.6.0] - 2017-08-23 -* Added load authorization settings +- Added load authorization settings ## [0.7.0] - 2017-08-23 -* Added polices +- Added polices ## [0.8.0] - 2017-08-23 -* Added permissions +- Added permissions ## [0.9.0] - 2017-09-05 -* Added functions for Admin Keycloak API +- Added functions for Admin Keycloak API ## [0.10.0] - 2017-10-23 -* Updated libraries versions -* Updated Docs +- Updated libraries versions +- Updated Docs ## [0.11.0] - 2017-12-12 -* Changed Instropect RPT +- Changed Instropect RPT ## [0.12.0] - 2018-01-25 -* Add groups functions -* Add Admin Tasks for user and client role management -* Function to trigger user sync from provider +- Add groups functions +- Add Admin Tasks for user and client role management +- Function to trigger user sync from provider ## [0.12.1] - 2018-08-04 -* Add get_idps -* Rework group functions +- Add get_idps +- Rework group functions diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..853ebe5 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @ryshoooo @marcospereirampj diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..683f7ee --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,86 @@ +# Contributing + +Welcome to the Python Keycloak contributing guidelines. We are all more than happy to receive +any contributions to the repository and want to thank you in advance for your contributions! +This document outlines the process and the guidelines on how contributions work for this repository. + +## Setting up the dev environment + +The development environment is mainly up to the developer. Our recommendations are to create a python +virtual environment and install the necessary requirements. Example + +```sh +python -m venv venv +source venv/bin/activate +python -m pip install -U pip +python -m pip install -r requirements.txt +python -m pip install -r dev-requirements.txt +``` + +## Running checks and tests + +We're utilizing `tox` for most of the testing workflows. However we also have an external dependency on `docker`. +We're using docker to spin up a local keycloak instance which we run our test cases against. This is to avoid +a lot of unnecessary mocking and yet have immediate feedback from the actual Keycloak instance. All of the setup +is done for you with the tox environments, all you need is to have both tox and docker installed +(`tox` is included in the `dev-requirements.txt`). + +To run the unit tests, simply run + +```sh +tox -e tests +``` + +The project is also adhering to strict linting (flake8) and formatting (black + isort). You can always check that +your code changes adhere to the format by running + +```sh +tox -e check +``` + +If the check fails, you'll see an error message specifying what went wrong. To simplify things, you can also run + +```sh +tox -e apply-check +``` + +which will apply isort and black formatting for you in the repository. The flake8 problems however need to be resolved +manually by the developer. + +Additionally we require that the documentation pages are built without warnings. This check is also run via tox, using +the command + +```sh +tox -e docs +``` + +The check is also run in the CICD pipelines. We require that the documentation pages built from the code docstrings +do not create visually "bad" pages. + +## Conventional commits + +Commits to this project must adhere to the [Conventional Commits +specification](https://www.conventionalcommits.org/en/v1.0.0/) that will allow +us to automate version bumps and changelog entry creation. + +After cloning this repository, you must install the pre-commit hook for +conventional commits (this is included in the `dev-requirements.txt`) + +```sh +python3 -m venv .venv +source .venv/bin/activate +python3 -m pip install pre-commit +pre-commit install --install-hooks -t pre-commit -t pre-push -t commit-msg +``` + +## How to contribute + +1. Fork this repository, develop and test your changes +2. Make sure that your changes do not decrease the test coverage +3. Make sure you're commits follow the conventional commits +4. Submit a pull request + +## How to release + +The CICD pipelines are set up for the repository. When a PR is merged, a new version of the library +will be automatically deployed to the PyPi server, meaning you'll be able to see your changes immediately. diff --git a/MANIFEST.in b/MANIFEST.in index 1aba38f..acf84af 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,4 @@ include LICENSE +include requirements.txt +include dev-requirements.txt +include docs-requirements.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..d2f6981 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,5 @@ +tox +pytest +pytest-cov +wheel +pre-commit diff --git a/docs-requirements.txt b/docs-requirements.txt new file mode 100644 index 0000000..343bf89 --- /dev/null +++ b/docs-requirements.txt @@ -0,0 +1,9 @@ +mock +alabaster +commonmark +recommonmark +sphinx +sphinx-rtd-theme +readthedocs-sphinx-ext +m2r2 +sphinx-autoapi diff --git a/docs/source/conf.py b/docs/source/conf.py index 166a8f4..23e9bbf 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -32,37 +32,37 @@ import sphinx_rtd_theme # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'python-keycloak' -copyright = '2017, Marcos Pereira' -author = 'Marcos Pereira' +project = "python-keycloak" +copyright = "2017, Marcos Pereira" +author = "Marcos Pereira" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.27.1' +version = "0.27.1" # The full version, including alpha/beta/rc tags. -release = '0.27.1' +release = "0.27.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -74,13 +74,13 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] add_function_parentheses = False add_module_names = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -91,7 +91,7 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme @@ -103,7 +103,7 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] html_use_smartypants = False @@ -116,7 +116,7 @@ html_show_copyright = True # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -#html_sidebars = { +# html_sidebars = { # '**': [ # 'about.html', # 'navigation.html', @@ -124,13 +124,13 @@ html_show_copyright = True # 'searchbox.html', # 'donate.html', # ] -#} +# } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'python-keycloakdoc' +htmlhelp_basename = "python-keycloakdoc" # -- Options for LaTeX output --------------------------------------------- @@ -139,15 +139,12 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -157,8 +154,13 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'python-keycloak.tex', 'python-keycloak Documentation', - 'Marcos Pereira', 'manual'), + ( + master_doc, + "python-keycloak.tex", + "python-keycloak Documentation", + "Marcos Pereira", + "manual", + ) ] @@ -166,10 +168,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'python-keycloak', 'python-keycloak Documentation', - [author], 1) -] +man_pages = [(master_doc, "python-keycloak", "python-keycloak Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -178,10 +177,13 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'python-keycloak', 'python-keycloak Documentation', - author, 'python-keycloak', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "python-keycloak", + "python-keycloak Documentation", + author, + "python-keycloak", + "One line description of project.", + "Miscellaneous", + ) ] - - - diff --git a/keycloak/_version.py b/keycloak/_version.py new file mode 100644 index 0000000..6c8e6b9 --- /dev/null +++ b/keycloak/_version.py @@ -0,0 +1 @@ +__version__ = "0.0.0" diff --git a/keycloak/authorization/__init__.py b/keycloak/authorization/__init__.py index 219687b..dad1078 100644 --- a/keycloak/authorization/__init__.py +++ b/keycloak/authorization/__init__.py @@ -55,39 +55,44 @@ class Authorization: :param data: keycloak authorization data (dict) :return: """ - for pol in data['policies']: - if pol['type'] == 'role': - policy = Policy(name=pol['name'], - type=pol['type'], - logic=pol['logic'], - decision_strategy=pol['decisionStrategy']) - - config_roles = json.loads(pol['config']['roles']) + for pol in data["policies"]: + if pol["type"] == "role": + policy = Policy( + name=pol["name"], + type=pol["type"], + logic=pol["logic"], + decision_strategy=pol["decisionStrategy"], + ) + + config_roles = json.loads(pol["config"]["roles"]) for role in config_roles: - policy.add_role(Role(name=role['id'], - required=role['required'])) + policy.add_role(Role(name=role["id"], required=role["required"])) self.policies[policy.name] = policy - if pol['type'] == 'scope': - permission = Permission(name=pol['name'], - type=pol['type'], - logic=pol['logic'], - decision_strategy=pol['decisionStrategy']) + if pol["type"] == "scope": + permission = Permission( + name=pol["name"], + type=pol["type"], + logic=pol["logic"], + decision_strategy=pol["decisionStrategy"], + ) - permission.scopes = ast.literal_eval(pol['config']['scopes']) + permission.scopes = ast.literal_eval(pol["config"]["scopes"]) - for policy_name in ast.literal_eval(pol['config']['applyPolicies']): + for policy_name in ast.literal_eval(pol["config"]["applyPolicies"]): self.policies[policy_name].add_permission(permission) - if pol['type'] == 'resource': - permission = Permission(name=pol['name'], - type=pol['type'], - logic=pol['logic'], - decision_strategy=pol['decisionStrategy']) + if pol["type"] == "resource": + permission = Permission( + name=pol["name"], + type=pol["type"], + logic=pol["logic"], + decision_strategy=pol["decisionStrategy"], + ) - permission.resources = ast.literal_eval(pol['config'].get('resources', "[]")) + permission.resources = ast.literal_eval(pol["config"].get("resources", "[]")) - for policy_name in ast.literal_eval(pol['config']['applyPolicies']): + for policy_name in ast.literal_eval(pol["config"]["applyPolicies"]): if self.policies.get(policy_name) is not None: self.policies[policy_name].add_permission(permission) diff --git a/keycloak/authorization/policy.py b/keycloak/authorization/policy.py index 9f688f7..4fbe913 100644 --- a/keycloak/authorization/policy.py +++ b/keycloak/authorization/policy.py @@ -98,9 +98,10 @@ class Policy: :param role: keycloak role. :return: """ - if self.type != 'role': + if self.type != "role": raise KeycloakAuthorizationConfigError( - "Can't add role. Policy type is different of role") + "Can't add role. Policy type is different of role" + ) self._roles.append(role) def add_permission(self, permission): diff --git a/keycloak/connection.py b/keycloak/connection.py index bdecfce..8ef45b1 100644 --- a/keycloak/connection.py +++ b/keycloak/connection.py @@ -29,11 +29,11 @@ except ImportError: import requests from requests.adapters import HTTPAdapter -from .exceptions import (KeycloakConnectionError) +from .exceptions import KeycloakConnectionError class ConnectionManager(object): - """ Represents a simple server connection. + """Represents a simple server connection. Args: base_url (str): The server URL. headers (dict): The header parameters of the requests to the server. @@ -52,15 +52,15 @@ class ConnectionManager(object): # retry once to reset connection with Keycloak after tomcat's ConnectionTimeout # see https://github.com/marcospereirampj/python-keycloak/issues/36 - for protocol in ('https://', 'http://'): + for protocol in ("https://", "http://"): adapter = HTTPAdapter(max_retries=1) # adds POST to retry whitelist allowed_methods = set(adapter.max_retries.allowed_methods) - allowed_methods.add('POST') + allowed_methods.add("POST") adapter.max_retries.allowed_methods = frozenset(allowed_methods) self._s.mount(protocol, adapter) - + if proxies: self._s.proxies.update(proxies) @@ -69,7 +69,7 @@ class ConnectionManager(object): @property def base_url(self): - """ Return base url in use for requests to the server. """ + """Return base url in use for requests to the server.""" return self._base_url @base_url.setter @@ -79,7 +79,7 @@ class ConnectionManager(object): @property def timeout(self): - """ Return timeout in use for request to the server. """ + """Return timeout in use for request to the server.""" return self._timeout @timeout.setter @@ -89,7 +89,7 @@ class ConnectionManager(object): @property def verify(self): - """ Return verify in use for request to the server. """ + """Return verify in use for request to the server.""" return self._verify @verify.setter @@ -99,7 +99,7 @@ class ConnectionManager(object): @property def headers(self): - """ Return header request to the server. """ + """Return header request to the server.""" return self._headers @headers.setter @@ -108,7 +108,7 @@ class ConnectionManager(object): self._headers = value def param_headers(self, key): - """ Return a specific header parameter. + """Return a specific header parameter. :arg key (str): Header parameters key. :return: @@ -117,11 +117,11 @@ class ConnectionManager(object): return self.headers.get(key) def clean_headers(self): - """ Clear header parameters. """ + """Clear header parameters.""" self.headers = {} def exist_param_headers(self, key): - """ Check if the parameter exists in the header. + """Check if the parameter exists in the header. :arg key (str): Header parameters key. :return: @@ -130,7 +130,7 @@ class ConnectionManager(object): return self.param_headers(key) is not None def add_param_headers(self, key, value): - """ Add a single parameter inside the header. + """Add a single parameter inside the header. :arg key (str): Header parameters key. value (str): Value to be added. @@ -138,14 +138,14 @@ class ConnectionManager(object): self.headers[key] = value def del_param_headers(self, key): - """ Remove a specific parameter. + """Remove a specific parameter. :arg key (str): Key of the header parameters. """ self.headers.pop(key, None) def raw_get(self, path, **kwargs): - """ Submit get request to the path. + """Submit get request to the path. :arg path (str): Path for request. :return @@ -155,17 +155,18 @@ class ConnectionManager(object): """ try: - return self._s.get(urljoin(self.base_url, path), - params=kwargs, - headers=self.headers, - timeout=self.timeout, - verify=self.verify) + return self._s.get( + urljoin(self.base_url, path), + params=kwargs, + headers=self.headers, + timeout=self.timeout, + verify=self.verify, + ) except Exception as e: - raise KeycloakConnectionError( - "Can't connect to server (%s)" % e) + raise KeycloakConnectionError("Can't connect to server (%s)" % e) def raw_post(self, path, data, **kwargs): - """ Submit post request to the path. + """Submit post request to the path. :arg path (str): Path for request. data (dict): Payload for request. @@ -175,18 +176,19 @@ class ConnectionManager(object): HttpError: Can't connect to server. """ try: - return self._s.post(urljoin(self.base_url, path), - params=kwargs, - data=data, - headers=self.headers, - timeout=self.timeout, - verify=self.verify) + return self._s.post( + urljoin(self.base_url, path), + params=kwargs, + data=data, + headers=self.headers, + timeout=self.timeout, + verify=self.verify, + ) except Exception as e: - raise KeycloakConnectionError( - "Can't connect to server (%s)" % e) + raise KeycloakConnectionError("Can't connect to server (%s)" % e) def raw_put(self, path, data, **kwargs): - """ Submit put request to the path. + """Submit put request to the path. :arg path (str): Path for request. data (dict): Payload for request. @@ -196,18 +198,19 @@ class ConnectionManager(object): HttpError: Can't connect to server. """ try: - return self._s.put(urljoin(self.base_url, path), - params=kwargs, - data=data, - headers=self.headers, - timeout=self.timeout, - verify=self.verify) + return self._s.put( + urljoin(self.base_url, path), + params=kwargs, + data=data, + headers=self.headers, + timeout=self.timeout, + verify=self.verify, + ) except Exception as e: - raise KeycloakConnectionError( - "Can't connect to server (%s)" % e) + raise KeycloakConnectionError("Can't connect to server (%s)" % e) def raw_delete(self, path, data={}, **kwargs): - """ Submit delete request to the path. + """Submit delete request to the path. :arg path (str): Path for request. @@ -218,12 +221,13 @@ class ConnectionManager(object): HttpError: Can't connect to server. """ try: - return self._s.delete(urljoin(self.base_url, path), - params=kwargs, - data=data, - headers=self.headers, - timeout=self.timeout, - verify=self.verify) + return self._s.delete( + urljoin(self.base_url, path), + params=kwargs, + data=data, + headers=self.headers, + timeout=self.timeout, + verify=self.verify, + ) except Exception as e: - raise KeycloakConnectionError( - "Can't connect to server (%s)" % e) + raise KeycloakConnectionError("Can't connect to server (%s)" % e) diff --git a/keycloak/exceptions.py b/keycloak/exceptions.py index 67da62a..a9c1b2b 100644 --- a/keycloak/exceptions.py +++ b/keycloak/exceptions.py @@ -25,8 +25,7 @@ import requests class KeycloakError(Exception): - def __init__(self, error_message="", response_code=None, - response_body=None): + def __init__(self, error_message="", response_code=None, response_body=None): Exception.__init__(self, error_message) @@ -56,10 +55,23 @@ class KeycloakOperationError(KeycloakError): class KeycloakDeprecationError(KeycloakError): pass + class KeycloakGetError(KeycloakOperationError): pass +class KeycloakPostError(KeycloakOperationError): + pass + + +class KeycloakPutError(KeycloakOperationError): + pass + + +class KeycloakDeleteError(KeycloakOperationError): + pass + + class KeycloakSecretNotFound(KeycloakOperationError): pass @@ -90,10 +102,10 @@ def raise_error_from_response(response, error, expected_codes=None, skip_exists= return response.content if skip_exists and response.status_code == 409: - return {"Already exists"} + return {"msg": "Already exists"} try: - message = response.json()['message'] + message = response.json()["message"] except (KeyError, ValueError): message = response.content @@ -103,6 +115,6 @@ def raise_error_from_response(response, error, expected_codes=None, skip_exists= if response.status_code == 401: error = KeycloakAuthenticationError - raise error(error_message=message, - response_code=response.status_code, - response_body=response.content) + raise error( + error_message=message, response_code=response.status_code, response_body=response.content + ) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 41ecab8..41f4567 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -29,33 +29,91 @@ from builtins import isinstance from typing import Iterable from .connection import ConnectionManager -from .exceptions import raise_error_from_response, KeycloakGetError +from .exceptions import KeycloakGetError, raise_error_from_response from .keycloak_openid import KeycloakOpenID - -from .urls_patterns import URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS, URL_ADMIN_CLIENT_AUTHZ_POLICIES, \ - URL_ADMIN_CLIENT_AUTHZ_SCOPES, URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURCES, URL_ADMIN_CLIENT_ROLES, \ - URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY, URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION, \ - URL_ADMIN_GET_SESSIONS, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_GROUPS_REALM_ROLES, \ - URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE, URL_ADMIN_CLIENT_INSTALLATION_PROVIDER, \ - URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, URL_ADMIN_GROUPS_CLIENT_ROLES, \ - URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, URL_ADMIN_USER_GROUP, URL_ADMIN_REALM_ROLES, URL_ADMIN_GROUP_CHILD, \ - URL_ADMIN_USER_CONSENTS, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_CLIENT, URL_ADMIN_USER, URL_ADMIN_CLIENT_ROLE, \ - URL_ADMIN_USER_GROUPS, URL_ADMIN_CLIENTS, URL_ADMIN_FLOWS_EXECUTIONS, URL_ADMIN_GROUPS, URL_ADMIN_USER_CLIENT_ROLES, \ - URL_ADMIN_REALMS, URL_ADMIN_USERS_COUNT, URL_ADMIN_FLOWS, URL_ADMIN_GROUP, URL_ADMIN_CLIENT_AUTHZ_SETTINGS, \ - URL_ADMIN_GROUP_MEMBERS, URL_ADMIN_USER_STORAGE, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_IDPS, URL_ADMIN_IDP, \ - URL_ADMIN_IDP_MAPPERS, URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ - URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ - URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_USER_REALM_ROLES_AVAILABLE, URL_ADMIN_USER_REALM_ROLES_COMPOSITE, \ - URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ - URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, URL_ADMIN_CLIENT_ROLE_MEMBERS, \ - URL_ADMIN_REALM_ROLES_MEMBERS, URL_ADMIN_CLIENT_PROTOCOL_MAPPER, URL_ADMIN_CLIENT_SCOPES_MAPPERS, \ - URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY, \ - URL_ADMIN_FLOWS_ALIAS, URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, URL_ADMIN_AUTHENTICATOR_CONFIG, \ - URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE, URL_ADMIN_CLIENT_ALL_SESSIONS, URL_ADMIN_EVENTS, \ - URL_ADMIN_REALM_EXPORT, URL_ADMIN_DELETE_USER_ROLE, URL_ADMIN_USER_LOGOUT, URL_ADMIN_FLOWS_EXECUTION, \ - URL_ADMIN_FLOW, URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES, URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE, \ - URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES, URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE, \ - URL_ADMIN_USER_CREDENTIALS, URL_ADMIN_USER_CREDENTIAL, URL_ADMIN_CLIENT_PROTOCOL_MAPPERS +from .urls_patterns import ( + URL_ADMIN_AUTHENTICATOR_CONFIG, + URL_ADMIN_CLIENT, + URL_ADMIN_CLIENT_ALL_SESSIONS, + URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS, + URL_ADMIN_CLIENT_AUTHZ_POLICIES, + URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION, + URL_ADMIN_CLIENT_AUTHZ_RESOURCES, + URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY, + URL_ADMIN_CLIENT_AUTHZ_SCOPES, + URL_ADMIN_CLIENT_AUTHZ_SETTINGS, + URL_ADMIN_CLIENT_INSTALLATION_PROVIDER, + URL_ADMIN_CLIENT_PROTOCOL_MAPPER, + URL_ADMIN_CLIENT_PROTOCOL_MAPPERS, + URL_ADMIN_CLIENT_ROLE, + URL_ADMIN_CLIENT_ROLE_MEMBERS, + URL_ADMIN_CLIENT_ROLES, + URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE, + URL_ADMIN_CLIENT_SCOPE, + URL_ADMIN_CLIENT_SCOPES, + URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, + URL_ADMIN_CLIENT_SCOPES_MAPPERS, + URL_ADMIN_CLIENT_SECRETS, + URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, + URL_ADMIN_CLIENTS, + URL_ADMIN_COMPONENT, + URL_ADMIN_COMPONENTS, + URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE, + URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES, + URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE, + URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES, + URL_ADMIN_DELETE_USER_ROLE, + URL_ADMIN_EVENTS, + URL_ADMIN_FLOW, + URL_ADMIN_FLOWS, + URL_ADMIN_FLOWS_ALIAS, + URL_ADMIN_FLOWS_COPY, + URL_ADMIN_FLOWS_EXECUTION, + URL_ADMIN_FLOWS_EXECUTIONS, + URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION, + URL_ADMIN_FLOWS_EXECUTIONS_FLOW, + URL_ADMIN_GET_SESSIONS, + URL_ADMIN_GROUP, + URL_ADMIN_GROUP_CHILD, + URL_ADMIN_GROUP_MEMBERS, + URL_ADMIN_GROUP_PERMISSIONS, + URL_ADMIN_GROUPS, + URL_ADMIN_GROUPS_CLIENT_ROLES, + URL_ADMIN_GROUPS_REALM_ROLES, + URL_ADMIN_IDP, + URL_ADMIN_IDP_MAPPERS, + URL_ADMIN_IDPS, + URL_ADMIN_KEYS, + URL_ADMIN_REALM, + URL_ADMIN_REALM_EXPORT, + URL_ADMIN_REALM_ROLES, + URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE, + URL_ADMIN_REALM_ROLES_MEMBERS, + URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, + URL_ADMIN_REALMS, + URL_ADMIN_RESET_PASSWORD, + URL_ADMIN_SEND_UPDATE_ACCOUNT, + URL_ADMIN_SEND_VERIFY_EMAIL, + URL_ADMIN_SERVER_INFO, + URL_ADMIN_USER, + URL_ADMIN_USER_CLIENT_ROLES, + URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, + URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, + URL_ADMIN_USER_CONSENTS, + URL_ADMIN_USER_CREDENTIAL, + URL_ADMIN_USER_CREDENTIALS, + URL_ADMIN_USER_FEDERATED_IDENTITIES, + URL_ADMIN_USER_FEDERATED_IDENTITY, + URL_ADMIN_USER_GROUP, + URL_ADMIN_USER_GROUPS, + URL_ADMIN_USER_LOGOUT, + URL_ADMIN_USER_REALM_ROLES, + URL_ADMIN_USER_REALM_ROLES_AVAILABLE, + URL_ADMIN_USER_REALM_ROLES_COMPOSITE, + URL_ADMIN_USER_STORAGE, + URL_ADMIN_USERS, + URL_ADMIN_USERS_COUNT, +) class KeycloakAdmin: @@ -75,8 +133,19 @@ class KeycloakAdmin: _custom_headers = None _user_realm_name = None - def __init__(self, server_url, username=None, password=None, realm_name='master', client_id='admin-cli', verify=True, - client_secret_key=None, custom_headers=None, user_realm_name=None, auto_refresh_token=None): + def __init__( + self, + server_url, + username=None, + password=None, + realm_name="master", + client_id="admin-cli", + verify=True, + client_secret_key=None, + custom_headers=None, + user_realm_name=None, + auto_refresh_token=None, + ): """ :param server_url: Keycloak server url @@ -198,40 +267,46 @@ class KeycloakAdmin: @auto_refresh_token.setter def auto_refresh_token(self, value): - allowed_methods = {'get', 'post', 'put', 'delete'} + allowed_methods = {"get", "post", "put", "delete"} if not isinstance(value, Iterable): - raise TypeError('Expected a list of strings among {allowed}'.format(allowed=allowed_methods)) + raise TypeError( + "Expected a list of strings among {allowed}".format(allowed=allowed_methods) + ) if not all(method in allowed_methods for method in value): - raise TypeError('Unexpected method in auto_refresh_token, accepted methods are {allowed}'.format(allowed=allowed_methods)) + raise TypeError( + "Unexpected method in auto_refresh_token, accepted methods are {allowed}".format( + allowed=allowed_methods + ) + ) self._auto_refresh_token = value def __fetch_all(self, url, query=None): - '''Wrapper function to paginate GET requests + """Wrapper function to paginate GET requests :param url: The url on which the query is executed :param query: Existing query parameters (optional) :return: Combined results of paginated queries - ''' + """ results = [] # initalize query if it was called with None if not query: query = {} page = 0 - query['max'] = self.PAGE_SIZE + query["max"] = self.PAGE_SIZE # fetch until we can while True: - query['first'] = page*self.PAGE_SIZE + query["first"] = page * self.PAGE_SIZE partial_results = raise_error_from_response( - self.raw_get(url, **query), - KeycloakGetError) + self.raw_get(url, **query), KeycloakGetError + ) if not partial_results: break results.extend(partial_results) - if len(partial_results) < query['max']: + if len(partial_results) < query["max"]: break page += 1 return results @@ -239,9 +314,7 @@ class KeycloakAdmin: def __fetch_paginated(self, url, query=None): query = query or {} - return raise_error_from_response( - self.raw_get(url, **query), - KeycloakGetError) + return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError) def import_realm(self, payload): """ @@ -255,8 +328,7 @@ class KeycloakAdmin: :return: RealmRepresentation """ - data_raw = self.raw_post(URL_ADMIN_REALMS, - data=json.dumps(payload)) + data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def export_realm(self, export_clients=False, export_groups_and_role=False): @@ -271,7 +343,11 @@ class KeycloakAdmin: :return: realm configurations JSON """ - params_path = {"realm-name": self.realm_name, "export-clients": export_clients, "export-groups-and-roles": export_groups_and_role } + params_path = { + "realm-name": self.realm_name, + "export-clients": export_clients, + "export-groups-and-roles": export_groups_and_role, + } data_raw = self.raw_post(URL_ADMIN_REALM_EXPORT.format(**params_path), data="") return raise_error_from_response(data_raw, KeycloakGetError) @@ -296,9 +372,10 @@ class KeycloakAdmin: :return: Keycloak server response (RealmRepresentation) """ - data_raw = self.raw_post(URL_ADMIN_REALMS, - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def update_realm(self, realm_name, payload): """ @@ -314,8 +391,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": realm_name} - data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_realm(self, realm_name): @@ -359,8 +435,7 @@ class KeycloakAdmin: :param: payload: IdentityProviderRepresentation """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def add_mapper_to_idp(self, idp_alias, payload): @@ -374,8 +449,9 @@ class KeycloakAdmin: :param: payload: IdentityProviderMapperRepresentation """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} - data_raw = self.raw_post(URL_ADMIN_IDP_MAPPERS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def get_idps(self): @@ -416,16 +492,15 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} if exist_ok: - exists = self.get_user_id(username=payload['username']) + exists = self.get_user_id(username=payload["username"]) if exists is not None: return str(exists) - data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)) raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) - _last_slash_idx = data_raw.headers['Location'].rindex('/') - return data_raw.headers['Location'][_last_slash_idx + 1:] + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] def users_count(self): """ @@ -490,8 +565,7 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_user(self, user_id): @@ -522,8 +596,9 @@ class KeycloakAdmin: """ payload = {"type": "password", "temporary": temporary, "value": password} params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_put(URL_ADMIN_RESET_PASSWORD.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_credentials(self, user_id): @@ -551,7 +626,11 @@ class KeycloakAdmin: :param: credential_id: credential id :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": user_id, "credential_id": credential_id} + params_path = { + "realm-name": self.realm_name, + "id": user_id, + "credential_id": credential_id, + } data_raw = self.raw_get(URL_ADMIN_USER_CREDENTIAL.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) @@ -566,7 +645,11 @@ class KeycloakAdmin: :param: credential_id: credential id :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": user_id, "credential_id": credential_id} + params_path = { + "realm-name": self.realm_name, + "id": user_id, + "credential_id": credential_id, + } data_raw = self.raw_delete(URL_ADMIN_USER_CREDENTIAL.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) @@ -615,9 +698,15 @@ class KeycloakAdmin: :param provider_username: username specified by the provider :return: """ - payload = {"identityProvider": provider_id, "userId": provider_userid, "userName": provider_username} + payload = { + "identityProvider": provider_id, + "userId": provider_userid, + "userName": provider_username, + } params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} - data_raw = self.raw_post(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload) + ) def delete_user_social_login(self, user_id, provider_id): @@ -631,7 +720,9 @@ class KeycloakAdmin: data_raw = self.raw_delete(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None): + def send_update_account( + self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None + ): """ Send an update account email to the user. An email contains a link the user can click to perform a set of required actions. @@ -646,8 +737,11 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} - data_raw = self.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), - data=json.dumps(payload), **params_query) + data_raw = self.raw_put( + URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), + data=json.dumps(payload), + **params_query + ) return raise_error_from_response(data_raw, KeycloakGetError) def send_verify_email(self, user_id, client_id=None, redirect_uri=None): @@ -663,8 +757,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "redirect_uri": redirect_uri} - data_raw = self.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), - data={}, **params_query) + data_raw = self.raw_put( + URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), data={}, **params_query + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_sessions(self, user_id): @@ -740,7 +835,7 @@ class KeycloakAdmin: """ for subgroup in group["subGroups"]: - if subgroup['path'] == path: + if subgroup["path"] == path: return subgroup elif subgroup["subGroups"]: for subgroup in group["subGroups"]: @@ -787,11 +882,11 @@ class KeycloakAdmin: # TODO: Review this code is necessary for group in groups: - if group['path'] == path: + if group["path"] == path: return group elif search_in_subgroups and group["subGroups"]: for group in group["subGroups"]: - if group['path'] == path: + if group["path"] == path: return group res = self.get_subgroups(group, path) if res != None: @@ -814,14 +909,18 @@ class KeycloakAdmin: if parent is None: params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_GROUPS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload) + ) else: - params_path = {"realm-name": self.realm_name, "id": parent, } - data_raw = self.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path), - data=json.dumps(payload)) + params_path = {"realm-name": self.realm_name, "id": parent} + data_raw = self.raw_post( + URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload) + ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def update_group(self, group_id, payload): """ @@ -837,8 +936,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def group_set_permissions(self, group_id, enabled=True): @@ -851,8 +949,10 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_put(URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), - data=json.dumps({"enabled": enabled})) + data_raw = self.raw_put( + URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), + data=json.dumps({"enabled": enabled}), + ) return raise_error_from_response(data_raw, KeycloakGetError) def group_user_add(self, user_id, group_id): @@ -935,7 +1035,7 @@ class KeycloakAdmin: clients = self.get_clients() for client in clients: - if client_name == client.get('name') or client_name == client.get('clientId'): + if client_name == client.get("name") or client_name == client.get("clientId"): return client["id"] return None @@ -965,12 +1065,14 @@ class KeycloakAdmin: :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, - "id": client_id} + params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_post(URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def get_client_authz_resources(self, client_id): """ @@ -1008,12 +1110,15 @@ class KeycloakAdmin: :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, - "id": client_id} + params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_post(URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): """ @@ -1039,12 +1144,15 @@ class KeycloakAdmin: :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, - "id": client_id} + params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_post(URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def get_client_authz_scopes(self, client_id): """ @@ -1110,9 +1218,10 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def update_client(self, client_id, payload): """ @@ -1124,8 +1233,7 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_client(self, client_id): @@ -1182,7 +1290,7 @@ class KeycloakAdmin: :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_roles_resource) :return: Keycloak Server Response (UserRepresentation) """ - params_path = {"realm-name": self.realm_name, "role-name":role_name} + params_path = {"realm-name": self.realm_name, "role-name": role_name} return self.__fetch_all(URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query) def get_client_roles(self, client_id): @@ -1250,9 +1358,12 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": client_role_id} - data_raw = self.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): """ @@ -1266,8 +1377,10 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} - data_raw = self.raw_post(URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path), + data=json.dumps(payload), + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_client_role(self, client_role_id, role_name): @@ -1296,8 +1409,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} - data_raw = self.raw_post(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_client_role_members(self, client_id, role_name, **query): @@ -1308,9 +1422,8 @@ class KeycloakAdmin: :param query: Additional query parameters ( see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_clients_resource) :return: Keycloak server response (UserRepresentation) """ - params_path = {"realm-name": self.realm_name, "id":client_id, "role-name":role_name} - return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path) , query) - + params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} + return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query) def create_realm_role(self, payload, skip_exists=False): """ @@ -1322,9 +1435,12 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_REALM_ROLES.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def get_realm_role(self, role_name): """ @@ -1348,8 +1464,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_put(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_realm_role(self, role_name): @@ -1360,8 +1477,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_delete( - URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) + data_raw = self.raw_delete(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def add_composite_realm_roles_to_role(self, role_name, roles): @@ -1377,9 +1493,9 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_post( URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, - expected_codes=[204]) + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def remove_composite_realm_roles_to_role(self, role_name, roles): """ @@ -1394,9 +1510,9 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, - expected_codes=[204]) + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_composite_realm_roles_of_role(self, role_name): """ @@ -1407,8 +1523,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_get( - URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)) + data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def assign_realm_roles(self, user_id, roles): @@ -1422,8 +1537,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_post(URL_ADMIN_USER_REALM_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_realm_roles_of_user(self, user_id, roles): @@ -1437,8 +1553,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_delete(URL_ADMIN_USER_REALM_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_delete( + URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_realm_roles_of_user(self, user_id): @@ -1484,8 +1601,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_post(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_group_realm_roles(self, group_id, roles): @@ -1499,8 +1617,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_delete(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_delete( + URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_group_realm_roles(self, group_id): @@ -1526,8 +1645,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} - data_raw = self.raw_post(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_group_client_roles(self, group_id, client_id): @@ -1555,8 +1675,9 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} - data_raw = self.raw_delete(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_delete( + URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_client_roles_of_user(self, user_id, client_id): @@ -1577,7 +1698,9 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) :return: Keycloak server response (array RoleRepresentation) """ - return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id) + return self._get_client_roles_of_user( + URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id + ) def get_composite_client_roles_of_user(self, user_id, client_id): """ @@ -1587,7 +1710,9 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) :return: Keycloak server response (array RoleRepresentation) """ - return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id) + return self._get_client_roles_of_user( + URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id + ) def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id): params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} @@ -1605,8 +1730,9 @@ class KeycloakAdmin: """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} - data_raw = self.raw_delete(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_delete( + URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_authentication_flows(self): @@ -1649,9 +1775,10 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def copy_authentication_flow(self, payload, flow_alias): """ @@ -1663,8 +1790,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_post(URL_ADMIN_FLOWS_COPY.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def delete_authentication_flow(self, flow_id): @@ -1705,9 +1833,10 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_put(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[202,204]) + data_raw = self.raw_put( + URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[202, 204]) def get_authentication_flow_execution(self, execution_id): """ @@ -1736,8 +1865,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def delete_authentication_flow_execution(self, execution_id): @@ -1768,9 +1898,12 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def get_authenticator_config(self, config_id): """ @@ -1795,8 +1928,9 @@ class KeycloakAdmin: :return: Response(json) """ params_path = {"realm-name": self.realm_name, "id": config_id} - data_raw = self.raw_put(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_authenticator_config(self, config_id): @@ -1821,12 +1955,13 @@ class KeycloakAdmin: :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync" :return: """ - data = {'action': action} + data = {"action": action} params_query = {"action": action} params_path = {"realm-name": self.realm_name, "id": storage_id} - data_raw = self.raw_post(URL_ADMIN_USER_STORAGE.format(**params_path), - data=json.dumps(data), **params_query) + data_raw = self.raw_post( + URL_ADMIN_USER_STORAGE.format(**params_path), data=json.dumps(data), **params_query + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scopes(self): @@ -1866,9 +2001,12 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_CLIENT_SCOPES.format(**params_path), - data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + data_raw = self.raw_post( + URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response( + data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + ) def update_client_scope(self, client_scope_id, payload): """ @@ -1882,8 +2020,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} - data_raw = self.raw_put(URL_ADMIN_CLIENT_SCOPE.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def add_mapper_to_client_scope(self, client_scope_id, payload): @@ -1899,7 +2038,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload)) + URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) @@ -1913,11 +2053,13 @@ class KeycloakAdmin: :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id, - "protocol-mapper-id": protocol_mppaer_id} + params_path = { + "realm-name": self.realm_name, + "scope-id": client_scope_id, + "protocol-mapper-id": protocol_mppaer_id, + } - data_raw = self.raw_delete( - URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)) + data_raw = self.raw_delete(URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) @@ -1933,11 +2075,15 @@ class KeycloakAdmin: :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id, - "protocol-mapper-id": protocol_mapper_id} + params_path = { + "realm-name": self.realm_name, + "scope-id": client_scope_id, + "protocol-mapper-id": protocol_mapper_id, + } data_raw = self.raw_put( - URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload)) + URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) @@ -1951,7 +2097,6 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def delete_default_default_client_scope(self, scope_id): """ Delete default default client scope @@ -1963,7 +2108,6 @@ class KeycloakAdmin: data_raw = self.raw_delete(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def add_default_default_client_scope(self, scope_id): """ Add default default client scope @@ -1973,10 +2117,11 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} - data_raw = self.raw_put(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def get_default_optional_client_scopes(self): """ Return list of default optional client scopes @@ -1987,7 +2132,6 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def delete_default_optional_client_scope(self, scope_id): """ Delete default optional client scope @@ -1999,7 +2143,6 @@ class KeycloakAdmin: data_raw = self.raw_delete(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def add_default_optional_client_scope(self, scope_id): """ Add default optional client scope @@ -2009,10 +2152,11 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} - data_raw = self.raw_put(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def add_mapper_to_client(self, client_id, payload): """ Add a mapper to a client @@ -2026,28 +2170,30 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), data=json.dumps(payload)) + URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) - + def update_client_mapper(self, client_id, mapper_id, payload): """ Update client mapper :param client_id: The id of the client :param client_mapper_id: The id of the mapper to be deleted :param payload: ProtocolMapperRepresentation - :return: Keycloak server response + :return: Keycloak server response """ params_path = { "realm-name": self.realm_name, - "id": self.client_id, + "id": self.client_id, "protocol-mapper-id": mapper_id, } data_raw = self.raw_put( - URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload)) - + URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def remove_client_mapper(self, client_id, client_mapper_id): @@ -2062,14 +2208,13 @@ class KeycloakAdmin: params_path = { "realm-name": self.realm_name, "id": client_id, - "protocol-mapper-id": client_mapper_id + "protocol-mapper-id": client_mapper_id, } - data_raw = self.raw_delete( - URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)) - + data_raw = self.raw_delete(URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - + def generate_client_secrets(self, client_id): """ @@ -2109,8 +2254,7 @@ class KeycloakAdmin: :return: components list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path), - data=None, **query) + data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query) return raise_error_from_response(data_raw, KeycloakGetError) def create_component(self, payload): @@ -2126,8 +2270,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_COMPONENTS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_post( + URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def get_component(self, component_id): @@ -2156,8 +2301,9 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "component-id": component_id} - data_raw = self.raw_put(URL_ADMIN_COMPONENT.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put( + URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def delete_component(self, component_id): @@ -2182,8 +2328,7 @@ class KeycloakAdmin: :return: keys list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path), - data=None) + data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path), data=None) return raise_error_from_response(data_raw, KeycloakGetError) def get_events(self, query=None): @@ -2196,8 +2341,7 @@ class KeycloakAdmin: :return: events list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_EVENTS.format(**params_path), - data=None, **query) + data_raw = self.raw_get(URL_ADMIN_EVENTS.format(**params_path), data=None, **query) return raise_error_from_response(data_raw, KeycloakGetError) def set_events(self, payload): @@ -2210,8 +2354,7 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_put(URL_ADMIN_EVENTS.format(**params_path), - data=json.dumps(payload)) + data_raw = self.raw_put(URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def raw_get(self, *args, **kwargs): @@ -2222,7 +2365,7 @@ class KeycloakAdmin: and try *get* once more. """ r = self.connection.raw_get(*args, **kwargs) - if 'get' in self.auto_refresh_token and r.status_code == 401: + if "get" in self.auto_refresh_token and r.status_code == 401: self.refresh_token() return self.connection.raw_get(*args, **kwargs) return r @@ -2235,7 +2378,7 @@ class KeycloakAdmin: and try *post* once more. """ r = self.connection.raw_post(*args, **kwargs) - if 'post' in self.auto_refresh_token and r.status_code == 401: + if "post" in self.auto_refresh_token and r.status_code == 401: self.refresh_token() return self.connection.raw_post(*args, **kwargs) return r @@ -2248,7 +2391,7 @@ class KeycloakAdmin: and try *put* once more. """ r = self.connection.raw_put(*args, **kwargs) - if 'put' in self.auto_refresh_token and r.status_code == 401: + if "put" in self.auto_refresh_token and r.status_code == 401: self.refresh_token() return self.connection.raw_put(*args, **kwargs) return r @@ -2261,7 +2404,7 @@ class KeycloakAdmin: and try *delete* once more. """ r = self.connection.raw_delete(*args, **kwargs) - if 'delete' in self.auto_refresh_token and r.status_code == 401: + if "delete" in self.auto_refresh_token and r.status_code == 401: self.refresh_token() return self.connection.raw_delete(*args, **kwargs) return r @@ -2273,11 +2416,15 @@ class KeycloakAdmin: token_realm_name = self.realm_name else: token_realm_name = "master" - - self.keycloak_openid = KeycloakOpenID(server_url=self.server_url, client_id=self.client_id, - realm_name=token_realm_name, verify=self.verify, - client_secret_key=self.client_secret_key, - custom_headers=self.custom_headers) + + self.keycloak_openid = KeycloakOpenID( + server_url=self.server_url, + client_id=self.client_id, + realm_name=token_realm_name, + verify=self.verify, + client_secret_key=self.client_secret_key, + custom_headers=self.custom_headers, + ) grant_type = ["password"] if self.client_secret_key: @@ -2286,11 +2433,13 @@ class KeycloakAdmin: self.realm_name = self.user_realm_name if self.username and self.password: - self._token = self.keycloak_openid.token(self.username, self.password, grant_type=grant_type) + self._token = self.keycloak_openid.token( + self.username, self.password, grant_type=grant_type + ) headers = { - 'Authorization': 'Bearer ' + self.token.get('access_token'), - 'Content-Type': 'application/json' + "Authorization": "Bearer " + self.token.get("access_token"), + "Content-Type": "application/json", } else: self._token = None @@ -2300,13 +2449,12 @@ class KeycloakAdmin: # merge custom headers to main headers headers.update(self.custom_headers) - self._connection = ConnectionManager(base_url=self.server_url, - headers=headers, - timeout=60, - verify=self.verify) + self._connection = ConnectionManager( + base_url=self.server_url, headers=headers, timeout=60, verify=self.verify + ) def refresh_token(self): - refresh_token = self.token.get('refresh_token', None) + refresh_token = self.token.get("refresh_token", None) if refresh_token is None: self.get_token() else: @@ -2314,16 +2462,18 @@ class KeycloakAdmin: self.token = self.keycloak_openid.refresh_token(refresh_token) except KeycloakGetError as e: list_errors = [ - b'Refresh token expired', - b'Token is not active', - b'Session not active' + b"Refresh token expired", + b"Token is not active", + b"Session not active", ] if e.response_code == 400 and any(err in e.response_body for err in list_errors): - self.get_token() + self.get_token() else: raise - - self.connection.add_param_headers('Authorization', 'Bearer ' + self.token.get('access_token')) + + self.connection.add_param_headers( + "Authorization", "Bearer " + self.token.get("access_token") + ) def get_client_all_sessions(self, client_id): """ @@ -2346,9 +2496,10 @@ class KeycloakAdmin: DELETE admin/realms/{realm-name}/users/{id}/role-mappings/realm """ - params_path = {"realm-name": self.realm_name, "id": str(user_id) } - data_raw = self.raw_delete(URL_ADMIN_DELETE_USER_ROLE.format(**params_path), - data=json.dumps(payload)) + params_path = {"realm-name": self.realm_name, "id": str(user_id)} + data_raw = self.raw_delete( + URL_ADMIN_DELETE_USER_ROLE.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_client_sessions_stats(self): @@ -2360,8 +2511,5 @@ class KeycloakAdmin: :return: Dict of clients and session count """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get( - self.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path) - ) + data_raw = self.raw_get(self.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - diff --git a/keycloak/keycloak_openid.py b/keycloak/keycloak_openid.py index 1d6ed28..d9c068f 100644 --- a/keycloak/keycloak_openid.py +++ b/keycloak/keycloak_openid.py @@ -27,24 +27,38 @@ from jose import jwt from .authorization import Authorization from .connection import ConnectionManager -from .exceptions import raise_error_from_response, KeycloakGetError, \ - KeycloakRPTNotFound, KeycloakAuthorizationConfigError, KeycloakInvalidTokenError, KeycloakDeprecationError +from .exceptions import ( + KeycloakAuthorizationConfigError, + KeycloakDeprecationError, + KeycloakGetError, + KeycloakInvalidTokenError, + KeycloakRPTNotFound, + raise_error_from_response, +) from .urls_patterns import ( - URL_REALM, URL_AUTH, + URL_CERTS, + URL_ENTITLEMENT, + URL_INTROSPECT, + URL_LOGOUT, + URL_REALM, URL_TOKEN, URL_USERINFO, URL_WELL_KNOWN, - URL_LOGOUT, - URL_CERTS, - URL_ENTITLEMENT, - URL_INTROSPECT ) class KeycloakOpenID: - - def __init__(self, server_url, realm_name, client_id, client_secret_key=None, verify=True, custom_headers=None, proxies=None): + def __init__( + self, + server_url, + realm_name, + client_id, + client_secret_key=None, + verify=True, + custom_headers=None, + proxies=None, + ): """ :param server_url: Keycloak server url @@ -62,11 +76,9 @@ class KeycloakOpenID: if custom_headers is not None: # merge custom headers to main headers headers.update(custom_headers) - self._connection = ConnectionManager(base_url=server_url, - headers=headers, - timeout=60, - verify=verify, - proxies=proxies) + self._connection = ConnectionManager( + base_url=server_url, headers=headers, timeout=60, verify=verify, proxies=proxies + ) self._authorization = Authorization() @@ -138,7 +150,7 @@ class KeycloakOpenID: :param kwargs: :return: """ - if method_token_info == 'introspect': + if method_token_info == "introspect": token_info = self.introspect(token) else: token_info = self.decode_token(token, **kwargs) @@ -146,11 +158,11 @@ class KeycloakOpenID: return token_info def well_know(self): - """ The most important endpoint to understand is the well-known configuration - endpoint. It lists endpoints and other configuration options relevant to - the OpenID Connect implementation in Keycloak. + """The most important endpoint to understand is the well-known configuration + endpoint. It lists endpoints and other configuration options relevant to + the OpenID Connect implementation in Keycloak. - :return It lists endpoints and other configuration options relevant. + :return It lists endpoints and other configuration options relevant. """ params_path = {"realm-name": self.realm_name} @@ -165,12 +177,23 @@ class KeycloakOpenID: :return: """ - params_path = {"authorization-endpoint": self.well_know()['authorization_endpoint'], - "client-id": self.client_id, - "redirect-uri": redirect_uri} + params_path = { + "authorization-endpoint": self.well_know()["authorization_endpoint"], + "client-id": self.client_id, + "redirect-uri": redirect_uri, + } return URL_AUTH.format(**params_path) - def token(self, username="", password="", grant_type=["password"], code="", redirect_uri="", totp=None, **extra): + def token( + self, + username="", + password="", + grant_type=["password"], + code="", + redirect_uri="", + totp=None, + **extra + ): """ The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on @@ -188,9 +211,14 @@ class KeycloakOpenID: :return: """ params_path = {"realm-name": self.realm_name} - payload = {"username": username, "password": password, - "client_id": self.client_id, "grant_type": grant_type, - "code": code, "redirect_uri": redirect_uri} + payload = { + "username": username, + "password": password, + "client_id": self.client_id, + "grant_type": grant_type, + "code": code, + "redirect_uri": redirect_uri, + } if extra: payload.update(extra) @@ -198,8 +226,7 @@ class KeycloakOpenID: payload["totp"] = totp payload = self._add_secret_key(payload) - data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), - data=payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError) def refresh_token(self, refresh_token, grant_type=["refresh_token"]): @@ -216,10 +243,13 @@ class KeycloakOpenID: :return: """ params_path = {"realm-name": self.realm_name} - payload = {"client_id": self.client_id, "grant_type": grant_type, "refresh_token": refresh_token} + payload = { + "client_id": self.client_id, + "grant_type": grant_type, + "refresh_token": refresh_token, + } payload = self._add_secret_key(payload) - data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), - data=payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError) def userinfo(self, token): @@ -250,8 +280,7 @@ class KeycloakOpenID: payload = {"client_id": self.client_id, "refresh_token": refresh_token} payload = self._add_secret_key(payload) - data_raw = self.connection.raw_post(URL_LOGOUT.format(**params_path), - data=payload) + data_raw = self.connection.raw_post(URL_LOGOUT.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) @@ -268,7 +297,7 @@ class KeycloakOpenID: params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_CERTS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - + def public_key(self): """ The public key is exposed by the realm page directly. @@ -277,8 +306,7 @@ class KeycloakOpenID: """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_REALM.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError)['public_key'] - + return raise_error_from_response(data_raw, KeycloakGetError)["public_key"] def entitlement(self, token, resource_server_id): """ @@ -293,8 +321,8 @@ class KeycloakOpenID: self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name, "resource-server-id": resource_server_id} data_raw = self.connection.raw_get(URL_ENTITLEMENT.format(**params_path)) - - if data_raw.status_code == 404: + + if data_raw.status_code == 404: return raise_error_from_response(data_raw, KeycloakDeprecationError) return raise_error_from_response(data_raw, KeycloakGetError) @@ -316,7 +344,7 @@ class KeycloakOpenID: payload = {"client_id": self.client_id, "token": token} - if token_type_hint == 'requesting_party_token': + if token_type_hint == "requesting_party_token": if rpt: payload.update({"token": rpt, "token_type_hint": token_type_hint}) self.connection.add_param_headers("Authorization", "Bearer " + token) @@ -325,12 +353,11 @@ class KeycloakOpenID: payload = self._add_secret_key(payload) - data_raw = self.connection.raw_post(URL_INTROSPECT.format(**params_path), - data=payload) + data_raw = self.connection.raw_post(URL_INTROSPECT.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError) - def decode_token(self, token, key, algorithms=['RS256'], **kwargs): + def decode_token(self, token, key, algorithms=["RS256"], **kwargs): """ A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. This specification @@ -347,8 +374,7 @@ class KeycloakOpenID: :return: """ - return jwt.decode(token, key, algorithms=algorithms, - audience=self.client_id, **kwargs) + return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) def load_authorization_config(self, path): """ @@ -357,12 +383,12 @@ class KeycloakOpenID: :param path: settings file (json) :return: """ - authorization_file = open(path, 'r') + authorization_file = open(path, "r") authorization_json = json.loads(authorization_file.read()) self.authorization.load_config(authorization_json) authorization_file.close() - def get_policies(self, token, method_token_info='introspect', **kwargs): + def get_policies(self, token, method_token_info="introspect", **kwargs): """ Get policies by user token @@ -377,12 +403,10 @@ class KeycloakOpenID: token_info = self._token_info(token, method_token_info, **kwargs) - if method_token_info == 'introspect' and not token_info['active']: - raise KeycloakInvalidTokenError( - "Token expired or invalid." - ) + if method_token_info == "introspect" and not token_info["active"]: + raise KeycloakInvalidTokenError("Token expired or invalid.") - user_resources = token_info['resource_access'].get(self.client_id) + user_resources = token_info["resource_access"].get(self.client_id) if not user_resources: return None @@ -390,13 +414,13 @@ class KeycloakOpenID: policies = [] for policy_name, policy in self.authorization.policies.items(): - for role in user_resources['roles']: + for role in user_resources["roles"]: if self._build_name_role(role) in policy.roles: policies.append(policy) return list(set(policies)) - def get_permissions(self, token, method_token_info='introspect', **kwargs): + def get_permissions(self, token, method_token_info="introspect", **kwargs): """ Get permission by user token @@ -413,12 +437,10 @@ class KeycloakOpenID: token_info = self._token_info(token, method_token_info, **kwargs) - if method_token_info == 'introspect' and not token_info['active']: - raise KeycloakInvalidTokenError( - "Token expired or invalid." - ) + if method_token_info == "introspect" and not token_info["active"]: + raise KeycloakInvalidTokenError("Token expired or invalid.") - user_resources = token_info['resource_access'].get(self.client_id) + user_resources = token_info["resource_access"].get(self.client_id) if not user_resources: return None @@ -426,7 +448,7 @@ class KeycloakOpenID: permissions = [] for policy_name, policy in self.authorization.policies.items(): - for role in user_resources['roles']: + for role in user_resources["roles"]: if self._build_name_role(role) in policy.roles: permissions += policy.permissions diff --git a/keycloak/tests/test_connection.py b/keycloak/tests/test_connection.py deleted file mode 100644 index cb98feb..0000000 --- a/keycloak/tests/test_connection.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2017 Marcos Pereira -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -from unittest import mock - -from httmock import urlmatch, response, HTTMock, all_requests - -from keycloak import KeycloakAdmin, KeycloakOpenID -from ..connection import ConnectionManager - -try: - import unittest -except ImportError: - import unittest2 as unittest - - -class TestConnection(unittest.TestCase): - - def setUp(self): - self._conn = ConnectionManager( - base_url="http://localhost/", - headers={}, - timeout=60) - - @all_requests - def response_content_success(self, url, request): - headers = {'content-type': 'application/json'} - content = b'response_ok' - return response(200, content, headers, None, 5, request) - - def test_raw_get(self): - with HTTMock(self.response_content_success): - resp = self._conn.raw_get("/known_path") - self.assertEqual(resp.content, b'response_ok') - self.assertEqual(resp.status_code, 200) - - def test_raw_post(self): - @urlmatch(path="/known_path", method="post") - def response_post_success(url, request): - headers = {'content-type': 'application/json'} - content = 'response'.encode("utf-8") - return response(201, content, headers, None, 5, request) - - with HTTMock(response_post_success): - resp = self._conn.raw_post("/known_path", - {'field': 'value'}) - self.assertEqual(resp.content, b'response') - self.assertEqual(resp.status_code, 201) - - def test_raw_put(self): - @urlmatch(netloc="localhost", path="/known_path", method="put") - def response_put_success(url, request): - headers = {'content-type': 'application/json'} - content = 'response'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(response_put_success): - resp = self._conn.raw_put("/known_path", - {'field': 'value'}) - self.assertEqual(resp.content, b'response') - self.assertEqual(resp.status_code, 200) - - def test_raw_get_fail(self): - @urlmatch(netloc="localhost", path="/known_path", method="get") - def response_get_fail(url, request): - headers = {'content-type': 'application/json'} - content = "404 page not found".encode("utf-8") - return response(404, content, headers, None, 5, request) - - with HTTMock(response_get_fail): - resp = self._conn.raw_get("/known_path") - - self.assertEqual(resp.content, b"404 page not found") - self.assertEqual(resp.status_code, 404) - - def test_raw_post_fail(self): - @urlmatch(netloc="localhost", path="/known_path", method="post") - def response_post_fail(url, request): - headers = {'content-type': 'application/json'} - content = str(["Start can't be blank"]).encode("utf-8") - return response(404, content, headers, None, 5, request) - - with HTTMock(response_post_fail): - resp = self._conn.raw_post("/known_path", - {'field': 'value'}) - self.assertEqual(resp.content, str(["Start can't be blank"]).encode("utf-8")) - self.assertEqual(resp.status_code, 404) - - def test_raw_put_fail(self): - @urlmatch(netloc="localhost", path="/known_path", method="put") - def response_put_fail(url, request): - headers = {'content-type': 'application/json'} - content = str(["Start can't be blank"]).encode("utf-8") - return response(404, content, headers, None, 5, request) - - with HTTMock(response_put_fail): - resp = self._conn.raw_put("/known_path", - {'field': 'value'}) - self.assertEqual(resp.content, str(["Start can't be blank"]).encode("utf-8")) - self.assertEqual(resp.status_code, 404) - - def test_add_param_headers(self): - self._conn.add_param_headers("test", "value") - self.assertEqual(self._conn.headers, - {"test": "value"}) - - def test_del_param_headers(self): - self._conn.add_param_headers("test", "value") - self._conn.del_param_headers("test") - self.assertEqual(self._conn.headers, {}) - - def test_clean_param_headers(self): - self._conn.add_param_headers("test", "value") - self.assertEqual(self._conn.headers, - {"test": "value"}) - self._conn.clean_headers() - self.assertEqual(self._conn.headers, {}) - - def test_exist_param_headers(self): - self._conn.add_param_headers("test", "value") - self.assertTrue(self._conn.exist_param_headers("test")) - self.assertFalse(self._conn.exist_param_headers("test_no")) - - def test_get_param_headers(self): - self._conn.add_param_headers("test", "value") - self.assertTrue(self._conn.exist_param_headers("test")) - self.assertFalse(self._conn.exist_param_headers("test_no")) - - def test_get_headers(self): - self._conn.add_param_headers("test", "value") - self.assertEqual(self._conn.headers, - {"test": "value"}) - - def test_KeycloakAdmin_custom_header(self): - - class FakeToken: - @staticmethod - def get(string_val): - return "faketoken" - - fake_token = FakeToken() - - with mock.patch.object(KeycloakOpenID, "__init__", return_value=None) as mock_keycloak_open_id: - with mock.patch("keycloak.keycloak_openid.KeycloakOpenID.token", return_value=fake_token): - with mock.patch("keycloak.connection.ConnectionManager.__init__", return_value=None) as mock_connection_manager: - with mock.patch("keycloak.connection.ConnectionManager.__del__", return_value=None) as mock_connection_manager_delete: - server_url = "https://localhost/auth/" - username = "admin" - password = "secret" - realm_name = "master" - - headers = { - 'Custom': 'test-custom-header' - } - KeycloakAdmin(server_url=server_url, - username=username, - password=password, - realm_name=realm_name, - verify=False, - custom_headers=headers) - - mock_keycloak_open_id.assert_called_with(server_url=server_url, - realm_name=realm_name, - client_id='admin-cli', - client_secret_key=None, - verify=False, - custom_headers=headers) - - expected_header = {'Authorization': 'Bearer faketoken', - 'Content-Type': 'application/json', - 'Custom': 'test-custom-header' - } - - mock_connection_manager.assert_called_with(base_url=server_url, - headers=expected_header, - timeout=60, - verify=False) - mock_connection_manager_delete.assert_called_once_with() diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index d7dd16a..43699eb 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -30,7 +30,9 @@ URL_LOGOUT = "realms/{realm-name}/protocol/openid-connect/logout" URL_CERTS = "realms/{realm-name}/protocol/openid-connect/certs" URL_INTROSPECT = "realms/{realm-name}/protocol/openid-connect/token/introspect" URL_ENTITLEMENT = "realms/{realm-name}/authz/entitlement/{resource-server-id}" -URL_AUTH = "{authorization-endpoint}?client_id={client-id}&response_type=code&redirect_uri={redirect-uri}" +URL_AUTH = ( + "{authorization-endpoint}?client_id={client-id}&response_type=code&redirect_uri={redirect-uri}" +) # ADMIN URLS URL_ADMIN_USERS = "admin/realms/{realm-name}/users" @@ -41,14 +43,26 @@ URL_ADMIN_SEND_UPDATE_ACCOUNT = "admin/realms/{realm-name}/users/{id}/execute-ac URL_ADMIN_SEND_VERIFY_EMAIL = "admin/realms/{realm-name}/users/{id}/send-verify-email" URL_ADMIN_RESET_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password" URL_ADMIN_GET_SESSIONS = "admin/realms/{realm-name}/users/{id}/sessions" -URL_ADMIN_USER_CLIENT_ROLES = "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}" +URL_ADMIN_USER_CLIENT_ROLES = ( + "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}" +) URL_ADMIN_USER_REALM_ROLES = "admin/realms/{realm-name}/users/{id}/role-mappings/realm" -URL_ADMIN_USER_REALM_ROLES_AVAILABLE = "admin/realms/{realm-name}/users/{id}/role-mappings/realm/available" -URL_ADMIN_USER_REALM_ROLES_COMPOSITE = "admin/realms/{realm-name}/users/{id}/role-mappings/realm/composite" +URL_ADMIN_USER_REALM_ROLES_AVAILABLE = ( + "admin/realms/{realm-name}/users/{id}/role-mappings/realm/available" +) +URL_ADMIN_USER_REALM_ROLES_COMPOSITE = ( + "admin/realms/{realm-name}/users/{id}/role-mappings/realm/composite" +) URL_ADMIN_GROUPS_REALM_ROLES = "admin/realms/{realm-name}/groups/{id}/role-mappings/realm" -URL_ADMIN_GROUPS_CLIENT_ROLES = "admin/realms/{realm-name}/groups/{id}/role-mappings/clients/{client-id}" -URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE = "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}/available" -URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE = "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}/composite" +URL_ADMIN_GROUPS_CLIENT_ROLES = ( + "admin/realms/{realm-name}/groups/{id}/role-mappings/clients/{client-id}" +) +URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE = ( + "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}/available" +) +URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE = ( + "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}/composite" +) URL_ADMIN_USER_GROUP = "admin/realms/{realm-name}/users/{id}/groups/{group-id}" URL_ADMIN_USER_GROUPS = "admin/realms/{realm-name}/users/{id}/groups" URL_ADMIN_USER_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password" @@ -79,8 +93,12 @@ URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/re URL_ADMIN_CLIENT_AUTHZ_SCOPES = URL_ADMIN_CLIENT + "/authz/resource-server/scope?max=-1" URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS = URL_ADMIN_CLIENT + "/authz/resource-server/permission?max=-1" URL_ADMIN_CLIENT_AUTHZ_POLICIES = URL_ADMIN_CLIENT + "/authz/resource-server/policy?max=-1" -URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = URL_ADMIN_CLIENT + "/authz/resource-server/policy/role?max=-1" -URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = URL_ADMIN_CLIENT + "/authz/resource-server/permission/resource?max=-1" +URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = ( + URL_ADMIN_CLIENT + "/authz/resource-server/policy/role?max=-1" +) +URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = ( + URL_ADMIN_CLIENT + "/authz/resource-server/permission/resource?max=-1" +) URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER = URL_ADMIN_CLIENT + "/service-account-user" URL_ADMIN_CLIENT_CERTS = URL_ADMIN_CLIENT + "/certificates/{attr}" @@ -101,7 +119,9 @@ URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_IDP_MAPPERS = "admin/realms/{realm-name}/identity-provider/instances/{idp-alias}/mappers" URL_ADMIN_IDP = "admin/realms//{realm-name}/identity-provider/instances/{alias}" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" -URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE = "admin/realms/{realm-name}/roles/{role-name}/composites" +URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE = ( + "admin/realms/{realm-name}/roles/{role-name}/composites" +) URL_ADMIN_REALM_EXPORT = "admin/realms/{realm-name}/partial-export?exportClients={export-clients}&exportGroupsAndRoles={export-groups-and-roles}" URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES = URL_ADMIN_REALM + "/default-default-client-scopes" @@ -113,10 +133,16 @@ URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" URL_ADMIN_FLOW = URL_ADMIN_FLOWS + "/{id}" URL_ADMIN_FLOWS_ALIAS = "admin/realms/{realm-name}/authentication/flows/{flow-id}" URL_ADMIN_FLOWS_COPY = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/copy" -URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" +URL_ADMIN_FLOWS_EXECUTIONS = ( + "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" +) URL_ADMIN_FLOWS_EXECUTION = "admin/realms/{realm-name}/authentication/executions/{id}" -URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/execution" -URL_ADMIN_FLOWS_EXECUTIONS_FLOW = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" +URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION = ( + "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/execution" +) +URL_ADMIN_FLOWS_EXECUTIONS_FLOW = ( + "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" +) URL_ADMIN_AUTHENTICATOR_CONFIG = "admin/realms/{realm-name}/authentication/config/{id}" URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" @@ -124,10 +150,11 @@ URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/components/{component-id}" URL_ADMIN_KEYS = "admin/realms/{realm-name}/keys" URL_ADMIN_USER_FEDERATED_IDENTITIES = "admin/realms/{realm-name}/users/{id}/federated-identity" -URL_ADMIN_USER_FEDERATED_IDENTITY = "admin/realms/{realm-name}/users/{id}/federated-identity/{provider}" +URL_ADMIN_USER_FEDERATED_IDENTITY = ( + "admin/realms/{realm-name}/users/{id}/federated-identity/{provider}" +) -URL_ADMIN_EVENTS = 'admin/realms/{realm-name}/events' +URL_ADMIN_EVENTS = "admin/realms/{realm-name}/events" URL_ADMIN_DELETE_USER_ROLE = "admin/realms/{realm-name}/users/{id}/role-mappings/realm" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f947450 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[tool.black] +line-length = 99 + +[tool.isort] +line_length = 99 +profile = "black" diff --git a/requirements.txt b/requirements.txt index a353c7f..5474982 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,3 @@ requests>=2.20.0 -httmock>=1.2.5 python-jose>=1.4.0 -twine==1.13.0 -jose~=1.0.0 -setuptools~=54.2.0 -urllib3>=1.26.5 \ No newline at end of file +urllib3>=1.26.0 diff --git a/setup.py b/setup.py index 0ffc5aa..8f3b7fc 100644 --- a/setup.py +++ b/setup.py @@ -1,31 +1,56 @@ # -*- coding: utf-8 -*- - +import re from setuptools import setup with open("README.md", "r") as fh: long_description = fh.read() +with open("requirements.txt", "r") as fh: + reqs = fh.read().split("\n") + +with open("dev-requirements.txt", "r") as fh: + dev_reqs = fh.read().split("\n") + +with open("docs-requirements.txt", "r") as fh: + docs_reqs = fh.read().split("\n") + + +VERSIONFILE = "keycloak/_version.py" +verstrline = open(VERSIONFILE, "rt").read() +VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" +mo = re.search(VSRE, verstrline, re.M) +if mo: + verstr = mo.group(1) +else: + raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) + setup( - name='python-keycloak', - version='0.27.1', - url='https://github.com/marcospereirampj/python-keycloak', - license='The MIT License', - author='Marcos Pereira', - author_email='marcospereira.mpj@gmail.com', - keywords='keycloak openid', - description='python-keycloak is a Python package providing access to the Keycloak API.', + name="python-keycloak", + version=verstr, + url="https://github.com/marcospereirampj/python-keycloak", + license="The MIT License", + author="Marcos Pereira, Richard Nemeth", + author_email="marcospereira.mpj@gmail.com; ryshoooo@gmail.com", + keywords="keycloak openid oidc", + description="python-keycloak is a Python package providing access to the Keycloak API.", long_description=long_description, long_description_content_type="text/markdown", - packages=['keycloak', 'keycloak.authorization', 'keycloak.tests'], - install_requires=['requests>=2.20.0', 'python-jose>=1.4.0'], - tests_require=['httmock>=1.2.5'], + packages=["keycloak"], + install_requires=reqs, + tests_require=dev_reqs, + extras_require={"docs": docs_reqs}, + python_requires=">=3.7", + project_urls={ + "Documentation": "https://python-keycloak.readthedocs.io/en/latest/", + "Issue tracker": "https://github.com/marcospereirampj/python-keycloak/issues", + }, classifiers=[ - 'Programming Language :: Python :: 3', - 'License :: OSI Approved :: MIT License', - 'Development Status :: 3 - Alpha', - 'Operating System :: MacOS', - 'Operating System :: Unix', - 'Operating System :: Microsoft :: Windows', - 'Topic :: Utilities' - ] + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Development Status :: 3 - Alpha", + "Operating System :: MacOS", + "Operating System :: Unix", + "Operating System :: Microsoft :: Windows", + "Topic :: Utilities", + ], ) diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh new file mode 100755 index 0000000..bee8830 --- /dev/null +++ b/test_keycloak_init.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +CMD_ARGS=$1 +KEYCLOAK_DOCKER_IMAGE="quay.io/keycloak/keycloak:latest" + +echo "${CMD_ARGS}" + +function keycloak_stop() { + docker stop unittest_keycloak &> /dev/null + docker rm unittest_keycloak &> /dev/null +} + +function keycloak_start() { + echo "Starting keycloak docker container" + docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev + SECONDS=0 + until curl localhost:$KEYCLOAK_PORT; do + sleep 5; + if [ ${SECONDS} -gt 180 ]; then + echo "Timeout exceeded"; + exit 1; + fi + done +} + +# Ensuring that keycloak is stopped in case of CTRL-C +trap keycloak_stop err exit + +keycloak_stop # In case it did not shut down correctly last time. +keycloak_start + +eval ${CMD_ARGS} +RETURN_VALUE=$? + +exit ${RETURN_VALUE} diff --git a/keycloak/tests/__init__.py b/tests/__init__.py similarity index 100% rename from keycloak/tests/__init__.py rename to tests/__init__.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..b9c266a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,61 @@ +import os +import uuid + +import pytest + +from keycloak import KeycloakAdmin + + +@pytest.fixture +def env(): + class KeycloakTestEnv(object): + KEYCLOAK_HOST = os.environ["KEYCLOAK_HOST"] + KEYCLOAK_PORT = os.environ["KEYCLOAK_PORT"] + KEYCLOAK_ADMIN = os.environ["KEYCLOAK_ADMIN"] + KEYCLOAK_ADMIN_PASSWORD = os.environ["KEYCLOAK_ADMIN_PASSWORD"] + + return KeycloakTestEnv() + + +@pytest.fixture +def admin(env): + return KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + ) + + +@pytest.fixture +def realm(admin: KeycloakAdmin) -> str: + realm_name = str(uuid.uuid4()) + admin.create_realm(payload={"realm": realm_name}) + yield realm_name + admin.delete_realm(realm_name=realm_name) + + +@pytest.fixture +def user(admin: KeycloakAdmin, realm: str) -> str: + admin.realm_name = realm + username = str(uuid.uuid4()) + user_id = admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) + yield user_id + admin.delete_user(user_id=user_id) + + +@pytest.fixture +def group(admin: KeycloakAdmin, realm: str) -> str: + admin.realm_name = realm + group_name = str(uuid.uuid4()) + group_id = admin.create_group(payload={"name": group_name}) + yield group_id + admin.delete_group(group_id=group_id) + + +@pytest.fixture +def client(admin: KeycloakAdmin, realm: str) -> str: + admin.realm_name = realm + client = str(uuid.uuid4()) + client_id = admin.create_client(payload={"name": client, "clientId": client}) + yield client_id + admin.delete_client(client_id=client_id) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py new file mode 100644 index 0000000..29d3b15 --- /dev/null +++ b/tests/test_keycloak_admin.py @@ -0,0 +1,1201 @@ +import pytest + +import keycloak +from keycloak import KeycloakAdmin +from keycloak.connection import ConnectionManager +from keycloak.exceptions import ( + KeycloakDeleteError, + KeycloakGetError, + KeycloakPostError, + KeycloakPutError, +) + + +def test_keycloak_version(): + assert keycloak.__version__, keycloak.__version__ + + +def test_keycloak_admin_bad_init(env): + with pytest.raises(TypeError) as err: + KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + auto_refresh_token=1, + ) + assert err.match("Expected a list of strings") + + with pytest.raises(TypeError) as err: + KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + auto_refresh_token=["patch"], + ) + assert err.match("Unexpected method in auto_refresh_token") + + +def test_keycloak_admin_init(env): + admin = KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + ) + assert admin.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", admin.server_url + assert admin.realm_name == "master", admin.realm_name + assert isinstance(admin.connection, ConnectionManager), type(admin.connection) + assert admin.client_id == "admin-cli", admin.client_id + assert admin.client_secret_key is None, admin.client_secret_key + assert admin.verify, admin.verify + assert admin.username == env.KEYCLOAK_ADMIN, admin.username + assert admin.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.password + assert admin.totp is None, admin.totp + assert admin.token is not None, admin.token + assert admin.auto_refresh_token == list(), admin.auto_refresh_token + assert admin.user_realm_name is None, admin.user_realm_name + assert admin.custom_headers is None, admin.custom_headers + + +def test_realms(admin: KeycloakAdmin): + # Get realms + realms = admin.get_realms() + assert len(realms) == 1, realms + assert "master" == realms[0]["realm"] + + # Create a test realm + res = admin.create_realm(payload={"realm": "test"}) + assert res == b"", res + + # Create the same realm, should fail + with pytest.raises(KeycloakPostError) as err: + res = admin.create_realm(payload={"realm": "test"}) + assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'') + + # Create the same realm, skip_exists true + res = admin.create_realm(payload={"realm": "test"}, skip_exists=True) + assert res == {"msg": "Already exists"}, res + + # Get a single realm + res = admin.get_realm(realm_name="test") + assert res["realm"] == "test" + + # Get non-existing realm + with pytest.raises(KeycloakGetError) as err: + admin.get_realm(realm_name="non-existent") + assert err.match('404: b\'{"error":"Realm not found."}\'') + + # Update realm + res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"}) + assert res == dict(), res + + # Check that the update worked + res = admin.get_realm(realm_name="test") + assert res["realm"] == "test" + assert res["accountTheme"] == "test" + + # Update wrong payload + with pytest.raises(KeycloakPutError) as err: + admin.update_realm(realm_name="test", payload={"wrong": "payload"}) + assert err.match('400: b\'{"error":"Unrecognized field') + + # Check that get realms returns both realms + realms = admin.get_realms() + realm_names = [x["realm"] for x in realms] + assert len(realms) == 2, realms + assert "master" in realm_names, realm_names + assert "test" in realm_names, realm_names + + # Delete the realm + res = admin.delete_realm(realm_name="test") + assert res == dict(), res + + # Check that the realm does not exist anymore + with pytest.raises(KeycloakGetError) as err: + admin.get_realm(realm_name="test") + assert err.match('404: b\'{"error":"Realm not found."}\'') + + # Delete non-existing realm + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_realm(realm_name="non-existent") + assert err.match('404: b\'{"error":"Realm not found."}\'') + + +def test_import_export_realms(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True) + assert realm_export != dict(), realm_export + + admin.delete_realm(realm_name=realm) + admin.realm_name = "master" + res = admin.import_realm(payload=realm_export) + assert res == b"", res + + # Test bad import + with pytest.raises(KeycloakPostError) as err: + admin.import_realm(payload=dict()) + assert err.match('500: b\'{"error":"unknown_error"}\'') + + +def test_users(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Check no users present + users = admin.get_users() + assert users == list(), users + + # Test create user + user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"}) + assert user_id is not None, user_id + + # Test create the same user + with pytest.raises(KeycloakPostError) as err: + admin.create_user(payload={"username": "test", "email": "test@test.test"}) + assert err.match('409: b\'{"errorMessage":"User exists with same username"}\'') + + # Test create the same user, exists_ok true + user_id_2 = admin.create_user( + payload={"username": "test", "email": "test@test.test"}, exist_ok=True + ) + assert user_id == user_id_2 + + # Test get user + user = admin.get_user(user_id=user_id) + assert user["username"] == "test", user["username"] + assert user["email"] == "test@test.test", user["email"] + + # Test update user + res = admin.update_user(user_id=user_id, payload={"firstName": "Test"}) + assert res == dict(), res + user = admin.get_user(user_id=user_id) + assert user["firstName"] == "Test" + + # Test update user fail + with pytest.raises(KeycloakPutError) as err: + admin.update_user(user_id=user_id, payload={"wrong": "payload"}) + assert err.match('400: b\'{"error":"Unrecognized field') + + # Test get users again + users = admin.get_users() + usernames = [x["username"] for x in users] + assert "test" in usernames + + # Test users counts + count = admin.users_count() + assert count == 1, count + + # Test user groups + groups = admin.get_user_groups(user_id=user["id"]) + assert len(groups) == 0 + + # Test user groups bad id + with pytest.raises(KeycloakGetError) as err: + admin.get_user_groups(user_id="does-not-exist") + assert err.match('404: b\'{"error":"User not found"}\'') + + # Test logout + res = admin.user_logout(user_id=user["id"]) + assert res == dict(), res + + # Test logout fail + with pytest.raises(KeycloakPostError) as err: + admin.user_logout(user_id="non-existent-id") + assert err.match('404: b\'{"error":"User not found"}\'') + + # Test consents + res = admin.user_consents(user_id=user["id"]) + assert len(res) == 0, res + + # Test consents fail + with pytest.raises(KeycloakGetError) as err: + admin.user_consents(user_id="non-existent-id") + assert err.match('404: b\'{"error":"User not found"}\'') + + # Test delete user + res = admin.delete_user(user_id=user_id) + assert res == dict(), res + with pytest.raises(KeycloakGetError) as err: + admin.get_user(user_id=user_id) + err.match('404: b\'{"error":"User not found"}\'') + + # Test delete fail + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_user(user_id="non-existent-id") + assert err.match('404: b\'{"error":"User not found"}\'') + + +def test_users_pagination(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + for ind in range(admin.PAGE_SIZE + 50): + username = f"user_{ind}" + admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) + + users = admin.get_users() + assert len(users) == admin.PAGE_SIZE + 50, len(users) + + users = admin.get_users(query={"first": 100}) + assert len(users) == 50, len(users) + + users = admin.get_users(query={"max": 20}) + assert len(users) == 20, len(users) + + +def test_idps(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Create IDP + res = admin.create_idp( + payload=dict( + providerId="github", alias="github", config=dict(clientId="test", clientSecret="test") + ) + ) + assert res == b"", res + + # Test create idp fail + with pytest.raises(KeycloakPostError) as err: + admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"}) + assert err.match("Invalid identity provider id"), err + + # Test listing + idps = admin.get_idps() + assert len(idps) == 1 + assert "github" == idps[0]["alias"] + + # Test adding a mapper + res = admin.add_mapper_to_idp( + idp_alias="github", + payload={ + "identityProviderAlias": "github", + "identityProviderMapper": "github-user-attribute-mapper", + "name": "test", + }, + ) + assert res == b"", res + + # Test mapper fail + with pytest.raises(KeycloakPostError) as err: + admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict()) + assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') + + # Test delete + res = admin.delete_idp(idp_alias="github") + assert res == dict(), res + + # Test delete fail + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_idp(idp_alias="does-not-exist") + assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') + + +def test_user_credentials(admin: KeycloakAdmin, user: str): + res = admin.set_user_password(user_id=user, password="booya", temporary=True) + assert res == dict(), res + + # Test user password set fail + with pytest.raises(KeycloakPutError) as err: + admin.set_user_password(user_id="does-not-exist", password="") + assert err.match('404: b\'{"error":"User not found"}\'') + + credentials = admin.get_credentials(user_id=user) + assert len(credentials) == 1 + assert credentials[0]["type"] == "password", credentials + + # Test get credentials fail + with pytest.raises(KeycloakGetError) as err: + admin.get_credentials(user_id="does-not-exist") + assert err.match('404: b\'{"error":"User not found"}\'') + + res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"]) + assert res == dict(), res + + # Test delete fail + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_credential(user_id=user, credential_id="does-not-exist") + assert err.match('404: b\'{"error":"Credential not found"}\'') + + +def test_social_logins(admin: KeycloakAdmin, user: str): + res = admin.add_user_social_login( + user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test" + ) + assert res == dict(), res + admin.add_user_social_login( + user_id=user, provider_id="github", provider_userid="test", provider_username="test" + ) + assert res == dict(), res + + # Test add social login fail + with pytest.raises(KeycloakPostError) as err: + admin.add_user_social_login( + user_id="does-not-exist", + provider_id="does-not-exist", + provider_userid="test", + provider_username="test", + ) + assert err.match('404: b\'{"error":"User not found"}\'') + + res = admin.get_user_social_logins(user_id=user) + assert res == list(), res + + # Test get social logins fail + with pytest.raises(KeycloakGetError) as err: + admin.get_user_social_logins(user_id="does-not-exist") + assert err.match('404: b\'{"error":"User not found"}\'') + + res = admin.delete_user_social_login(user_id=user, provider_id="gitlab") + assert res == {}, res + + res = admin.delete_user_social_login(user_id=user, provider_id="github") + assert res == {}, res + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_user_social_login(user_id=user, provider_id="instagram") + assert err.match('404: b\'{"error":"Link not found"}\''), err + + +def test_server_info(admin: KeycloakAdmin): + info = admin.get_server_info() + assert set(info.keys()) == { + "systemInfo", + "memoryInfo", + "profileInfo", + "themes", + "socialProviders", + "identityProviders", + "providers", + "protocolMapperTypes", + "builtinProtocolMappers", + "clientInstallations", + "componentTypes", + "passwordPolicies", + "enums", + }, info.keys() + + +def test_groups(admin: KeycloakAdmin, user: str): + # Test get groups + groups = admin.get_groups() + assert len(groups) == 0 + + # Test create group + group_id = admin.create_group(payload={"name": "main-group"}) + assert group_id is not None, group_id + + # Test create subgroups + subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id) + subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id) + + # Test create group fail + with pytest.raises(KeycloakPostError) as err: + admin.create_group(payload={"name": "subgroup-1"}, parent=group_id) + assert err.match('409: b\'{"error":"unknown_error"}\''), err + + # Test skip exists OK + subgroup_id_1_eq = admin.create_group( + payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True + ) + assert subgroup_id_1_eq is None + + # Test get groups again + groups = admin.get_groups() + assert len(groups) == 1, groups + assert len(groups[0]["subGroups"]) == 2, groups["subGroups"] + assert groups[0]["id"] == group_id + assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2} + + # Test get groups query + groups = admin.get_groups(query={"max": 10}) + assert len(groups) == 1, groups + assert len(groups[0]["subGroups"]) == 2, groups["subGroups"] + assert groups[0]["id"] == group_id + assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2} + + # Test get group + res = admin.get_group(group_id=subgroup_id_1) + assert res["id"] == subgroup_id_1, res + assert res["name"] == "subgroup-1" + assert res["path"] == "/main-group/subgroup-1" + + # Test get group fail + with pytest.raises(KeycloakGetError) as err: + admin.get_group(group_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find group by id"}\''), err + + # Create 1 more subgroup + subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2) + main_group = admin.get_group(group_id=group_id) + + # Test nested searches + res = admin.get_subgroups(group=main_group, path="/main-group/subgroup-2/subsubgroup-1") + assert res is not None, res + assert res["id"] == subsubgroup_id_1 + + # Test empty search + res = admin.get_subgroups(group=main_group, path="/none") + assert res is None, res + + # Test get group by path + res = admin.get_group_by_path(path="/main-group/subgroup-1") + assert res is None, res + + res = admin.get_group_by_path(path="/main-group/subgroup-1", search_in_subgroups=True) + assert res is not None, res + assert res["id"] == subgroup_id_1, res + + res = admin.get_group_by_path( + path="/main-group/subgroup-2/subsubgroup-1/test", search_in_subgroups=True + ) + assert res is None, res + + res = admin.get_group_by_path( + path="/main-group/subgroup-2/subsubgroup-1", search_in_subgroups=True + ) + assert res is not None, res + assert res["id"] == subsubgroup_id_1 + + res = admin.get_group_by_path(path="/main-group") + assert res is not None, res + assert res["id"] == group_id, res + + # Test group members + res = admin.get_group_members(group_id=subgroup_id_2) + assert len(res) == 0, res + + # Test fail group members + with pytest.raises(KeycloakGetError) as err: + admin.get_group_members(group_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find group by id"}\'') + + res = admin.group_user_add(user_id=user, group_id=subgroup_id_2) + assert res == dict(), res + + res = admin.get_group_members(group_id=subgroup_id_2) + assert len(res) == 1, res + assert res[0]["id"] == user + + # Test get group members query + res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10}) + assert len(res) == 1, res + assert res[0]["id"] == user + + with pytest.raises(KeycloakDeleteError) as err: + admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2) + assert err.match('404: b\'{"error":"User not found"}\''), err + + res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2) + assert res == dict(), res + + # Test set permissions + res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True) + assert res["enabled"], res + res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False) + assert not res["enabled"], res + with pytest.raises(KeycloakPutError) as err: + admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah") + assert err.match('500: b\'{"error":"unknown_error"}\''), err + + # Test update group + res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"}) + assert res == dict(), res + assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2" + + # test update fail + with pytest.raises(KeycloakPutError) as err: + admin.update_group(group_id="does-not-exist", payload=dict()) + assert err.match('404: b\'{"error":"Could not find group by id"}\''), err + + # Test delete + res = admin.delete_group(group_id=group_id) + assert res == dict(), res + assert len(admin.get_groups()) == 0 + + # Test delete fail + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_group(group_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find group by id"}\''), err + + +def test_clients(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Test get clients + clients = admin.get_clients() + assert len(clients) == 6, clients + assert {x["name"] for x in clients} == set( + [ + "${client_admin-cli}", + "${client_security-admin-console}", + "${client_account-console}", + "${client_broker}", + "${client_account}", + "${client_realm-management}", + ] + ), clients + + # Test create client + client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"}) + assert client_id, client_id + + with pytest.raises(KeycloakPostError) as err: + admin.create_client(payload={"name": "test-client", "clientId": "test-client"}) + assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err + + client_id_2 = admin.create_client( + payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True + ) + assert client_id == client_id_2, client_id_2 + + # Test get client + res = admin.get_client(client_id=client_id) + assert res["clientId"] == "test-client", res + assert res["name"] == "test-client", res + assert res["id"] == client_id, res + + with pytest.raises(KeycloakGetError) as err: + admin.get_client(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + assert len(admin.get_clients()) == 7 + + # Test get client id + assert admin.get_client_id(client_name="test-client") == client_id + assert admin.get_client_id(client_name="does-not-exist") is None + + # Test update client + res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"}) + assert res == dict(), res + + with pytest.raises(KeycloakPutError) as err: + admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"}) + assert err.match('404: b\'{"error":"Could not find client"}\'') + + # Test authz + auth_client_id = admin.create_client( + payload={ + "name": "authz-client", + "clientId": "authz-client", + "authorizationServicesEnabled": True, + "serviceAccountsEnabled": True, + } + ) + res = admin.get_client_authz_settings(client_id=auth_client_id) + assert res["allowRemoteResourceManagement"] + assert res["decisionStrategy"] == "UNANIMOUS" + assert len(res["policies"]) >= 0 + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_authz_settings(client_id=client_id) + assert err.match('500: b\'{"error":"HTTP 500 Internal Server Error"}\'') + + # Authz resources + res = admin.get_client_authz_resources(client_id=auth_client_id) + assert len(res) == 1 + assert res[0]["name"] == "Default Resource" + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_authz_resources(client_id=client_id) + assert err.match('500: b\'{"error":"unknown_error"}\'') + + res = admin.create_client_authz_resource( + client_id=auth_client_id, payload={"name": "test-resource"} + ) + assert res["name"] == "test-resource", res + test_resource_id = res["_id"] + + with pytest.raises(KeycloakPostError) as err: + admin.create_client_authz_resource( + client_id=auth_client_id, payload={"name": "test-resource"} + ) + assert err.match('409: b\'{"error":"invalid_request"') + assert admin.create_client_authz_resource( + client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True + ) == {"msg": "Already exists"} + + res = admin.get_client_authz_resources(client_id=auth_client_id) + assert len(res) == 2 + assert {x["name"] for x in res} == {"Default Resource", "test-resource"} + + # Authz policies + res = admin.get_client_authz_policies(client_id=auth_client_id) + assert len(res) == 1, res + assert res[0]["name"] == "Default Policy" + assert len(admin.get_client_authz_policies(client_id=client_id)) == 1 + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_authz_policies(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + role_id = admin.get_realm_role(role_name="offline_access")["id"] + res = admin.create_client_authz_role_based_policy( + client_id=auth_client_id, + payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]}, + ) + assert res["name"] == "test-authz-rb-policy", res + + with pytest.raises(KeycloakPostError) as err: + admin.create_client_authz_role_based_policy( + client_id=auth_client_id, + payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]}, + ) + assert err.match('409: b\'{"error":"Policy with name') + assert admin.create_client_authz_role_based_policy( + client_id=auth_client_id, + payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]}, + skip_exists=True, + ) == {"msg": "Already exists"} + assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2 + + # Test authz permissions + res = admin.get_client_authz_permissions(client_id=auth_client_id) + assert len(res) == 1, res + assert res[0]["name"] == "Default Permission" + assert len(admin.get_client_authz_permissions(client_id=client_id)) == 1 + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_authz_permissions(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + res = admin.create_client_authz_resource_based_permission( + client_id=auth_client_id, + payload={"name": "test-permission-rb", "resources": [test_resource_id]}, + ) + assert res, res + assert res["name"] == "test-permission-rb" + assert res["resources"] == [test_resource_id] + + with pytest.raises(KeycloakPostError) as err: + admin.create_client_authz_resource_based_permission( + client_id=auth_client_id, + payload={"name": "test-permission-rb", "resources": [test_resource_id]}, + ) + assert err.match('409: b\'{"error":"Policy with name') + assert admin.create_client_authz_resource_based_permission( + client_id=auth_client_id, + payload={"name": "test-permission-rb", "resources": [test_resource_id]}, + skip_exists=True, + ) == {"msg": "Already exists"} + assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2 + + # Test authz scopes + res = admin.get_client_authz_scopes(client_id=auth_client_id) + assert len(res) == 0, res + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_authz_scopes(client_id=client_id) + assert err.match('500: b\'{"error":"unknown_error"}\'') + + # Test service account user + res = admin.get_client_service_account_user(client_id=auth_client_id) + assert res["username"] == "service-account-authz-client", res + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_service_account_user(client_id=client_id) + assert err.match('400: b\'{"error":"unknown_error"}\'') + + # Test delete client + res = admin.delete_client(client_id=auth_client_id) + assert res == dict(), res + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client(client_id=auth_client_id) + assert err.match('404: b\'{"error":"Could not find client"}\'') + + +def test_realm_roles(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Test get realm roles + roles = admin.get_realm_roles() + assert len(roles) == 3, roles + role_names = [x["name"] for x in roles] + assert "uma_authorization" in role_names, role_names + assert "offline_access" in role_names, role_names + + # Test empty members + with pytest.raises(KeycloakGetError) as err: + admin.get_realm_role_members(role_name="does-not-exist") + assert err.match('404: b\'{"error":"Could not find role"}\'') + members = admin.get_realm_role_members(role_name="offline_access") + assert members == list(), members + + # Test create realm role + role_id = admin.create_realm_role(payload={"name": "test-realm-role"}) + assert role_id, role_id + with pytest.raises(KeycloakPostError) as err: + admin.create_realm_role(payload={"name": "test-realm-role"}) + assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'') + role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True) + assert role_id == role_id_2 + + # Test update realm role + res = admin.update_realm_role( + role_name="test-realm-role", payload={"name": "test-realm-role-update"} + ) + assert res == dict(), res + with pytest.raises(KeycloakPutError) as err: + admin.update_realm_role( + role_name="test-realm-role", payload={"name": "test-realm-role-update"} + ) + assert err.match('404: b\'{"error":"Could not find role"}\''), err + + # Test realm role user assignment + user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"}) + with pytest.raises(KeycloakPostError) as err: + admin.assign_realm_roles(user_id=user_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_realm_roles( + user_id=user_id, + roles=[ + admin.get_realm_role(role_name="offline_access"), + admin.get_realm_role(role_name="test-realm-role-update"), + ], + ) + assert res == dict(), res + assert admin.get_user(user_id=user_id)["username"] in [ + x["username"] for x in admin.get_realm_role_members(role_name="offline_access") + ] + assert admin.get_user(user_id=user_id)["username"] in [ + x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update") + ] + + roles = admin.get_realm_roles_of_user(user_id=user_id) + assert len(roles) == 3 + assert "offline_access" in [x["name"] for x in roles] + assert "test-realm-role-update" in [x["name"] for x in roles] + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_realm_roles_of_user( + user_id=user_id, roles=[admin.get_realm_role(role_name="offline_access")] + ) + assert res == dict(), res + assert admin.get_realm_role_members(role_name="offline_access") == list() + roles = admin.get_realm_roles_of_user(user_id=user_id) + assert len(roles) == 2 + assert "offline_access" not in [x["name"] for x in roles] + assert "test-realm-role-update" in [x["name"] for x in roles] + + roles = admin.get_available_realm_roles_of_user(user_id=user_id) + assert len(roles) == 2 + assert "offline_access" in [x["name"] for x in roles] + assert "uma_authorization" in [x["name"] for x in roles] + + # Test realm role group assignment + group_id = admin.create_group(payload={"name": "test-group"}) + with pytest.raises(KeycloakPostError) as err: + admin.assign_group_realm_roles(group_id=group_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_group_realm_roles( + group_id=group_id, + roles=[ + admin.get_realm_role(role_name="offline_access"), + admin.get_realm_role(role_name="test-realm-role-update"), + ], + ) + assert res == dict(), res + + roles = admin.get_group_realm_roles(group_id=group_id) + assert len(roles) == 2 + assert "offline_access" in [x["name"] for x in roles] + assert "test-realm-role-update" in [x["name"] for x in roles] + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_group_realm_roles(group_id=group_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_group_realm_roles( + group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")] + ) + assert res == dict(), res + roles = admin.get_group_realm_roles(group_id=group_id) + assert len(roles) == 1 + assert "test-realm-role-update" in [x["name"] for x in roles] + + # Test composite realm roles + composite_role = admin.create_realm_role(payload={"name": "test-composite-role"}) + with pytest.raises(KeycloakPostError) as err: + admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.add_composite_realm_roles_to_role( + role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")] + ) + assert res == dict(), res + + res = admin.get_composite_realm_roles_of_role(role_name=composite_role) + assert len(res) == 1 + assert "test-realm-role-update" in res[0]["name"] + with pytest.raises(KeycloakGetError) as err: + admin.get_composite_realm_roles_of_role(role_name="bad") + assert err.match('404: b\'{"error":"Could not find role"}\'') + + res = admin.get_composite_realm_roles_of_user(user_id=user_id) + assert len(res) == 4 + assert "offline_access" in {x["name"] for x in res} + assert "test-realm-role-update" in {x["name"] for x in res} + assert "uma_authorization" in {x["name"] for x in res} + with pytest.raises(KeycloakGetError) as err: + admin.get_composite_realm_roles_of_user(user_id="bad") + assert err.match('404: b\'{"error":"User not found"}\'') + + with pytest.raises(KeycloakDeleteError) as err: + admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.remove_composite_realm_roles_to_role( + role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")] + ) + assert res == dict(), res + + res = admin.get_composite_realm_roles_of_role(role_name=composite_role) + assert len(res) == 0 + + # Test delete realm role + res = admin.delete_realm_role(role_name=composite_role) + assert res == dict(), res + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_realm_role(role_name=composite_role) + assert err.match('404: b\'{"error":"Could not find role"}\'') + + +def test_client_roles(admin: KeycloakAdmin, client: str): + # Test get client roles + res = admin.get_client_roles(client_id=client) + assert len(res) == 0 + with pytest.raises(KeycloakGetError) as err: + admin.get_client_roles(client_id="bad") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + # Test create client role + client_role_id = admin.create_client_role( + client_role_id=client, payload={"name": "client-role-test"} + ) + with pytest.raises(KeycloakPostError) as err: + admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"}) + assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'') + client_role_id_2 = admin.create_client_role( + client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True + ) + assert client_role_id == client_role_id_2 + + # Test get client role + res = admin.get_client_role(client_id=client, role_name="client-role-test") + assert res["name"] == client_role_id + with pytest.raises(KeycloakGetError) as err: + admin.get_client_role(client_id=client, role_name="bad") + assert err.match('404: b\'{"error":"Could not find role"}\'') + + res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test") + assert res_ == res["id"] + with pytest.raises(KeycloakGetError) as err: + admin.get_client_role_id(client_id=client, role_name="bad") + assert err.match('404: b\'{"error":"Could not find role"}\'') + assert len(admin.get_client_roles(client_id=client)) == 1 + + # Test update client role + res = admin.update_client_role( + client_role_id=client, + role_name="client-role-test", + payload={"name": "client-role-test-update"}, + ) + assert res == dict() + with pytest.raises(KeycloakPutError) as err: + res = admin.update_client_role( + client_role_id=client, + role_name="client-role-test", + payload={"name": "client-role-test-update"}, + ) + assert err.match('404: b\'{"error":"Could not find role"}\'') + + # Test user with client role + res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update") + assert len(res) == 0 + with pytest.raises(KeycloakGetError) as err: + admin.get_client_role_members(client_id=client, role_name="bad") + assert err.match('404: b\'{"error":"Could not find role"}\'') + + user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"}) + with pytest.raises(KeycloakPostError) as err: + admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_client_role( + user_id=user_id, + client_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")], + ) + assert res == dict() + assert ( + len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update")) + == 1 + ) + + roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client) + assert len(roles) == 1, roles + with pytest.raises(KeycloakGetError) as err: + admin.get_client_roles_of_user(user_id=user_id, client_id="bad") + assert err.match('404: b\'{"error":"Client not found"}\'') + + roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client) + assert len(roles) == 1, roles + with pytest.raises(KeycloakGetError) as err: + admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad") + assert err.match('404: b\'{"error":"Client not found"}\'') + + roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client) + assert len(roles) == 0, roles + with pytest.raises(KeycloakGetError) as err: + admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad") + assert err.match('404: b\'{"error":"Client not found"}\'') + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + admin.delete_client_roles_of_user( + user_id=user_id, + client_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")], + ) + assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0 + + # Test groups and client roles + res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update") + assert len(res) == 0 + with pytest.raises(KeycloakGetError) as err: + admin.get_client_role_groups(client_id=client, role_name="bad") + assert err.match('404: b\'{"error":"Could not find role"}\'') + + group_id = admin.create_group(payload={"name": "test-group"}) + res = admin.get_group_client_roles(group_id=group_id, client_id=client) + assert len(res) == 0 + with pytest.raises(KeycloakGetError) as err: + admin.get_group_client_roles(group_id=group_id, client_id="bad") + assert err.match('404: b\'{"error":"Client not found"}\'') + + with pytest.raises(KeycloakPostError) as err: + admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_group_client_roles( + group_id=group_id, + client_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")], + ) + assert res == dict() + assert ( + len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")) + == 1 + ) + assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1 + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_group_client_roles( + group_id=group_id, + client_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")], + ) + assert res == dict() + + # Test composite client roles + with pytest.raises(KeycloakPostError) as err: + admin.add_composite_client_roles_to_role( + client_role_id=client, role_name="client-role-test-update", roles=["bad"] + ) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.add_composite_client_roles_to_role( + client_role_id=client, + role_name="client-role-test-update", + roles=[admin.get_realm_role(role_name="offline_access")], + ) + assert res == dict() + assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[ + "composite" + ] + + # Test delete of client role + res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update") + assert res == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client_role(client_role_id=client, role_name="client-role-test-update") + assert err.match('404: b\'{"error":"Could not find role"}\'') + + +def test_email(admin: KeycloakAdmin, user: str): + # Emails will fail as we don't have SMTP test setup + with pytest.raises(KeycloakPutError) as err: + admin.send_update_account(user_id=user, payload=dict()) + assert err.match('500: b\'{"error":"unknown_error"}\'') + + admin.update_user(user_id=user, payload={"enabled": True}) + with pytest.raises(KeycloakPutError) as err: + admin.send_verify_email(user_id=user) + assert err.match('500: b\'{"errorMessage":"Failed to send execute actions email"}\'') + + +def test_get_sessions(admin: KeycloakAdmin): + sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username)) + assert len(sessions) >= 1 + with pytest.raises(KeycloakGetError) as err: + admin.get_sessions(user_id="bad") + assert err.match('404: b\'{"error":"User not found"}\'') + + +def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): + with pytest.raises(KeycloakGetError) as err: + admin.get_client_installation_provider(client_id=client, provider_id="bad") + assert err.match('404: b\'{"error":"Unknown Provider"}\'') + + installation = admin.get_client_installation_provider( + client_id=client, provider_id="keycloak-oidc-keycloak-json" + ) + assert set(installation.keys()) == { + "auth-server-url", + "confidential-port", + "credentials", + "realm", + "resource", + "ssl-required", + } + + +def test_auth_flows(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + res = admin.get_authentication_flows() + assert len(res) == 8, res + assert set(res[0].keys()) == { + "alias", + "authenticationExecutions", + "builtIn", + "description", + "id", + "providerId", + "topLevel", + } + assert {x["alias"] for x in res} == { + "reset credentials", + "browser", + "http challenge", + "registration", + "docker auth", + "direct grant", + "first broker login", + "clients", + } + + with pytest.raises(KeycloakGetError) as err: + admin.get_authentication_flow_for_id(flow_id="bad") + assert err.match('404: b\'{"error":"Could not find flow with id"}\'') + browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"] + res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id) + assert res["alias"] == "browser" + + # Test copying + with pytest.raises(KeycloakPostError) as err: + admin.copy_authentication_flow(payload=dict(), flow_alias="bad") + assert err.match("404: b''") + + res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser") + assert res == b"", res + assert len(admin.get_authentication_flows()) == 9 + + # Test create + res = admin.create_authentication_flow( + payload={"alias": "test-create", "providerId": "basic-flow"} + ) + assert res == b"" + with pytest.raises(KeycloakPostError) as err: + admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False}) + assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'') + assert admin.create_authentication_flow( + payload={"alias": "test-create"}, skip_exists=True + ) == {"msg": "Already exists"} + + # Test flow executions + res = admin.get_authentication_flow_executions(flow_alias="browser") + assert len(res) == 8, res + with pytest.raises(KeycloakGetError) as err: + admin.get_authentication_flow_executions(flow_alias="bad") + assert err.match("404: b''") + exec_id = res[0]["id"] + + res = admin.get_authentication_flow_execution(execution_id=exec_id) + assert set(res.keys()) == { + "alternative", + "authenticator", + "authenticatorFlow", + "conditional", + "disabled", + "enabled", + "id", + "parentFlow", + "priority", + "required", + "requirement", + }, res + with pytest.raises(KeycloakGetError) as err: + admin.get_authentication_flow_execution(execution_id="bad") + assert err.match('404: b\'{"error":"Illegal execution"}\'') + + with pytest.raises(KeycloakPostError) as err: + admin.create_authentication_flow_execution(payload=dict(), flow_alias="browser") + assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow"}\'') + + res = admin.create_authentication_flow_execution( + payload={"provider": "auth-cookie"}, flow_alias="test-create" + ) + assert res == b"" + assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1 + + with pytest.raises(KeycloakPutError) as err: + admin.update_authentication_flow_executions( + payload={"required": "yes"}, flow_alias="test-create" + ) + assert err.match('400: b\'{"error":"Unrecognized field') + payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0] + payload["displayName"] = "test" + res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create") + assert res + + exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"] + res = admin.delete_authentication_flow_execution(execution_id=exec_id) + assert res == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_authentication_flow_execution(execution_id=exec_id) + assert err.match('404: b\'{"error":"Illegal execution"}\'') + + # Test subflows + res = admin.create_authentication_flow_subflow( + payload={ + "alias": "test-subflow", + "provider": "basic-flow", + "type": "something", + "description": "something", + }, + flow_alias="test-browser", + ) + assert res == b"" + with pytest.raises(KeycloakPostError) as err: + admin.create_authentication_flow_subflow( + payload={"alias": "test-subflow", "providerId": "basic-flow"}, + flow_alias="test-browser", + ) + assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'') + res = admin.create_authentication_flow_subflow( + payload={ + "alias": "test-subflow", + "provider": "basic-flow", + "type": "something", + "description": "something", + }, + flow_alias="test-create", + skip_exists=True, + ) + assert res == {"msg": "Already exists"} + + # Test delete auth flow + flow_id = [x for x in admin.get_authentication_flows() if x["alias"] == "test-browser"][0][ + "id" + ] + res = admin.delete_authentication_flow(flow_id=flow_id) + assert res == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_authentication_flow(flow_id=flow_id) + assert err.match('404: b\'{"error":"Could not find flow with id"}\'') diff --git a/tests/test_urls_patterns.py b/tests/test_urls_patterns.py new file mode 100644 index 0000000..6fa5a87 --- /dev/null +++ b/tests/test_urls_patterns.py @@ -0,0 +1,26 @@ +from keycloak import urls_patterns + + +def test_correctness_of_patterns(): + """Test that there are no duplicate url patterns.""" + + # Test that the patterns are present + urls = [x for x in dir(urls_patterns) if not x.startswith("__")] + assert len(urls) >= 0 + + # Test that all patterns start with URL_ + for url in urls: + assert url.startswith("URL_"), f"The url pattern {url} does not begin with URL_" + + # Test that the patterns have unique names + seen_urls = list() + for url in urls: + assert url not in seen_urls, f"The url pattern {url} is present twice." + seen_urls.append(url) + + # Test that the pattern values are unique + seen_url_values = list() + for url in urls: + url_value = urls_patterns.__dict__[url] + assert url_value not in seen_url_values, f"The url {url} has a duplicate value {url_value}" + seen_url_values.append(url_value) diff --git a/tox.env b/tox.env new file mode 100644 index 0000000..49cea83 --- /dev/null +++ b/tox.env @@ -0,0 +1,4 @@ +KEYCLOAK_ADMIN=admin +KEYCLOAK_ADMIN_PASSWORD=admin +KEYCLOAK_HOST={env:KEYCLOAK_HOST:localhost} +KEYCLOAK_PORT=8080 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2726f66 --- /dev/null +++ b/tox.ini @@ -0,0 +1,48 @@ +[tox] +envlist = check, apply-check, docs, tests, build + +[testenv] +install_command = pip install {opts} {packages} + +[testenv:check] +deps = + black + isort + flake8 +commands = + black --check --diff keycloak tests docs + isort -c --df keycloak tests docs + flake8 keycloak tests docs + +[testenv:apply-check] +deps = + black + isort + flake8 +commands = + black -C keycloak tests docs + black keycloak tests docs + isort keycloak tests docs + +[testenv:docs] +deps = + .[docs] +commands = + python -m sphinx -T -E -W -b html -d _build/doctrees -D language=en ./docs/source _build/html + +[testenv:tests] +setenv = file|tox.env +deps = + -rrequirements.txt + -rdev-requirements.txt +commands = + ./test_keycloak_init.sh "pytest -vv --cov=keycloak --cov-report term-missing {posargs}" + +[testenv:build] +deps = + -rdev-requirements.txt +commands = + python setup.py sdist bdist_wheel + +[flake8] +max-line-length = 99 From 8d19ea8180b494832d36126615bbcb9c5c9de159 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:06:04 +0200 Subject: [PATCH 008/222] fix: raise correct errors --- keycloak/__init__.py | 7 +- keycloak/keycloak_admin.py | 164 +++++++++++++++++++------------------ 2 files changed, 89 insertions(+), 82 deletions(-) diff --git a/keycloak/__init__.py b/keycloak/__init__.py index 987ce1c..62c47a8 100644 --- a/keycloak/__init__.py +++ b/keycloak/__init__.py @@ -21,5 +21,8 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -from .keycloak_admin import * -from .keycloak_openid import * +from ._version import __version__ +from .keycloak_admin import KeycloakAdmin +from .keycloak_openid import KeycloakOpenID + +__all__ = ["KeycloakAdmin", "KeycloakOpenID", "__version__"] diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 5725a1a..3e30722 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -29,7 +29,13 @@ from builtins import isinstance from typing import Iterable from .connection import ConnectionManager -from .exceptions import KeycloakGetError, raise_error_from_response +from .exceptions import ( + KeycloakDeleteError, + KeycloakGetError, + KeycloakPostError, + KeycloakPutError, + raise_error_from_response, +) from .keycloak_openid import KeycloakOpenID from .urls_patterns import ( URL_ADMIN_AUTHENTICATOR_CONFIG, @@ -341,7 +347,7 @@ class KeycloakAdmin: """ data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def export_realm(self, export_clients=False, export_groups_and_role=False): """ @@ -361,7 +367,7 @@ class KeycloakAdmin: "export-groups-and-roles": export_groups_and_role, } data_raw = self.raw_post(URL_ADMIN_REALM_EXPORT.format(**params_path), data="") - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def get_realms(self): """ @@ -386,7 +392,7 @@ class KeycloakAdmin: data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def update_realm(self, realm_name, payload): @@ -404,7 +410,7 @@ class KeycloakAdmin: params_path = {"realm-name": realm_name} data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm(self, realm_name): """ @@ -416,7 +422,7 @@ class KeycloakAdmin: params_path = {"realm-name": realm_name} data_raw = self.raw_delete(URL_ADMIN_REALM.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_users(self, query=None): """ @@ -448,7 +454,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def add_mapper_to_idp(self, idp_alias, payload): """ @@ -464,7 +470,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def get_idps(self): """ @@ -487,7 +493,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_user(self, payload, exist_ok=True): """ @@ -510,7 +516,7 @@ class KeycloakAdmin: return str(exists) data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)) - raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") return data_raw.headers["Location"][_last_slash_idx + 1 :] @@ -578,7 +584,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_user(self, user_id): """ @@ -590,7 +596,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete(URL_ADMIN_USER.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def set_user_password(self, user_id, password, temporary=True): """ @@ -611,7 +617,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_credentials(self, user_id): """ @@ -663,7 +669,7 @@ class KeycloakAdmin: "credential_id": credential_id, } data_raw = self.raw_delete(URL_ADMIN_USER_CREDENTIAL.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakDeleteError) def logout(self, user_id): """ @@ -676,7 +682,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post(URL_ADMIN_USER_LOGOUT.format(**params_path), data="") - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def consents_user(self, user_id): """ @@ -719,6 +725,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload) ) + return raise_error_from_response(data_raw, KeycloakPostError) def delete_user_social_login(self, user_id, provider_id): @@ -730,7 +737,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} data_raw = self.raw_delete(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def send_update_account( self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None @@ -754,7 +761,7 @@ class KeycloakAdmin: data=json.dumps(payload), **params_query ) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPutError) def send_verify_email(self, user_id, client_id=None, redirect_uri=None): """ @@ -772,7 +779,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), data={}, **params_query ) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPutError) def get_sessions(self, user_id): """ @@ -931,7 +938,7 @@ class KeycloakAdmin: ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def update_group(self, group_id, payload): @@ -949,7 +956,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_set_permissions(self, group_id, enabled=True): """ @@ -965,7 +972,7 @@ class KeycloakAdmin: URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), data=json.dumps({"enabled": enabled}), ) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPutError) def group_user_add(self, user_id, group_id): """ @@ -978,7 +985,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_user_remove(self, user_id, group_id): """ @@ -991,7 +998,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def delete_group(self, group_id): """ @@ -1003,7 +1010,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete(URL_ADMIN_GROUP.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_clients(self): """ @@ -1063,7 +1070,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path)) - return data_raw + return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_resource(self, client_id, payload, skip_exists=False): """ @@ -1083,7 +1090,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def get_client_authz_resources(self, client_id): @@ -1129,7 +1136,7 @@ class KeycloakAdmin: data=json.dumps(payload), ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): @@ -1163,7 +1170,7 @@ class KeycloakAdmin: data=json.dumps(payload), ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def get_client_authz_scopes(self, client_id): @@ -1177,7 +1184,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) - return data_raw + return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_permissions(self, client_id): """ @@ -1190,7 +1197,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)) - return data_raw + return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policies(self, client_id): """ @@ -1203,7 +1210,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)) - return data_raw + return raise_error_from_response(data_raw, KeycloakGetError) def get_client_service_account_user(self, client_id): """ @@ -1232,7 +1239,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def update_client(self, client_id, payload): @@ -1246,7 +1253,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client(self, client_id): """ @@ -1261,7 +1268,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_delete(URL_ADMIN_CLIENT.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_installation_provider(self, client_id, provider_id): """ @@ -1374,7 +1381,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): @@ -1393,7 +1400,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path), data=json.dumps(payload), ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_client_role(self, client_role_id, role_name): """ @@ -1407,7 +1414,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def assign_client_role(self, user_id, client_id, roles): """ @@ -1424,7 +1431,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_client_role_members(self, client_id, role_name, **query): """ @@ -1451,7 +1458,7 @@ class KeycloakAdmin: URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def get_realm_role(self, role_name): @@ -1479,7 +1486,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm_role(self, role_name): """ @@ -1490,7 +1497,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_composite_realm_roles_to_role(self, role_name, roles): """ @@ -1507,7 +1514,7 @@ class KeycloakAdmin: URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), data=json.dumps(payload), ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def remove_composite_realm_roles_to_role(self, role_name, roles): """ @@ -1524,7 +1531,7 @@ class KeycloakAdmin: URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), data=json.dumps(payload), ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_composite_realm_roles_of_role(self, role_name): """ @@ -1552,7 +1559,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_realm_roles_of_user(self, user_id, roles): """ @@ -1568,7 +1575,7 @@ class KeycloakAdmin: data_raw = self.raw_delete( URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_realm_roles_of_user(self, user_id): """ @@ -1616,7 +1623,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_group_realm_roles(self, group_id, roles): """ @@ -1632,7 +1639,7 @@ class KeycloakAdmin: data_raw = self.raw_delete( URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_group_realm_roles(self, group_id): """ @@ -1660,7 +1667,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_group_client_roles(self, group_id, client_id): """ @@ -1690,7 +1697,7 @@ class KeycloakAdmin: data_raw = self.raw_delete( URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_roles_of_user(self, user_id, client_id): """ @@ -1745,7 +1752,7 @@ class KeycloakAdmin: data_raw = self.raw_delete( URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flows(self): """ @@ -1789,7 +1796,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def copy_authentication_flow(self, payload, flow_alias): @@ -1805,7 +1812,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow(self, flow_id): """ @@ -1819,7 +1826,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": flow_id} data_raw = self.raw_delete(URL_ADMIN_FLOW.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flow_executions(self, flow_alias): """ @@ -1848,7 +1855,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[202, 204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204]) def get_authentication_flow_execution(self, execution_id): """ @@ -1880,7 +1887,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow_execution(self, execution_id): """ @@ -1894,7 +1901,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": execution_id} data_raw = self.raw_delete(URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): """ @@ -1914,7 +1921,7 @@ class KeycloakAdmin: URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def get_authenticator_config(self, config_id): @@ -1943,7 +1950,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_authenticator_config(self, config_id): """ @@ -1956,8 +1963,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_delete(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) - - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def sync_users(self, storage_id, action): """ @@ -1974,7 +1980,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_USER_STORAGE.format(**params_path), data=json.dumps(data), **params_query ) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def get_client_scopes(self): """ @@ -2017,7 +2023,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( - data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists + data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def update_client_scope(self, client_scope_id, payload): @@ -2035,7 +2041,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_client_scope(self, client_scope_id, payload): """ @@ -2053,7 +2059,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id): """ @@ -2072,8 +2078,7 @@ class KeycloakAdmin: } data_raw = self.raw_delete(URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)) - - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload): """ @@ -2097,7 +2102,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_default_client_scopes(self): """ @@ -2118,7 +2123,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_default_client_scope(self, scope_id): """ @@ -2132,7 +2137,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_optional_client_scopes(self): """ @@ -2153,7 +2158,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_optional_client_scope(self, scope_id): """ @@ -2167,7 +2172,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_client(self, client_id, payload): """ @@ -2185,7 +2190,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_client_mapper(self, client_id, mapper_id, payload): """ @@ -2206,7 +2211,7 @@ class KeycloakAdmin: URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def remove_client_mapper(self, client_id, client_mapper_id): """ @@ -2224,8 +2229,7 @@ class KeycloakAdmin: } data_raw = self.raw_delete(URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)) - - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def generate_client_secrets(self, client_id): """ @@ -2239,7 +2243,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post(URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def get_client_secrets(self, client_id): """ @@ -2285,7 +2289,7 @@ class KeycloakAdmin: data_raw = self.raw_post( URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def get_component(self, component_id): """ @@ -2316,7 +2320,7 @@ class KeycloakAdmin: data_raw = self.raw_put( URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_component(self, component_id): """ @@ -2328,7 +2332,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_keys(self): """ @@ -2367,7 +2371,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_put(URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def raw_get(self, *args, **kwargs): """ From aff3051ffa33c2c50a2504fc42b5e176be955918 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:40:15 +0200 Subject: [PATCH 009/222] docs: fix docstrings --- keycloak/authorization/__init__.py | 4 +- keycloak/authorization/permission.py | 14 +++-- keycloak/authorization/policy.py | 7 ++- keycloak/connection.py | 87 +++++++++++++--------------- keycloak/keycloak_openid.py | 51 ++++++++-------- 5 files changed, 79 insertions(+), 84 deletions(-) diff --git a/keycloak/authorization/__init__.py b/keycloak/authorization/__init__.py index dad1078..789656d 100644 --- a/keycloak/authorization/__init__.py +++ b/keycloak/authorization/__init__.py @@ -38,7 +38,7 @@ class Authorization: """ def __init__(self): - self._policies = {} + self.policies = {} @property def policies(self): @@ -53,7 +53,7 @@ class Authorization: Load policies, roles and permissions (scope/resources). :param data: keycloak authorization data (dict) - :return: + :returns: None """ for pol in data["policies"]: if pol["type"] == "role": diff --git a/keycloak/authorization/permission.py b/keycloak/authorization/permission.py index 9988730..a200afe 100644 --- a/keycloak/authorization/permission.py +++ b/keycloak/authorization/permission.py @@ -26,15 +26,19 @@ class Permission: """ Consider this simple and very common permission: - A permission associates the object being protected with the policies that must be evaluated to determine whether access is granted. + A permission associates the object being protected with the policies that must be evaluated to + determine whether access is granted. X CAN DO Y ON RESOURCE Z - where … - X represents one or more users, roles, or groups, or a combination of them. You can + where + + - X represents one or more users, roles, or groups, or a combination of them. You can also use claims and context here. - Y represents an action to be performed, for example, write, view, and so on. - Z represents a protected resource, for example, "/accounts". + + - Y represents an action to be performed, for example, write, view, and so on. + + - Z represents a protected resource, for example, "/accounts". https://keycloak.gitbooks.io/documentation/authorization_services/topics/permission/overview.html diff --git a/keycloak/authorization/policy.py b/keycloak/authorization/policy.py index 4fbe913..4014b7a 100644 --- a/keycloak/authorization/policy.py +++ b/keycloak/authorization/policy.py @@ -29,9 +29,10 @@ class Policy: A policy defines the conditions that must be satisfied to grant access to an object. Unlike permissions, you do not specify the object being protected but rather the conditions that must be satisfied for access to a given object (for example, resource, scope, or both). - Policies are strongly related to the different access control mechanisms (ACMs) that you can use to - protect your resources. With policies, you can implement strategies for attribute-based access control - (ABAC), role-based access control (RBAC), context-based access control, or any combination of these. + Policies are strongly related to the different access control mechanisms (ACMs) that you can + use to protect your resources. With policies, you can implement strategies for attribute-based + access control (ABAC), role-based access control (RBAC), context-based access control, or any + combination of these. https://keycloak.gitbooks.io/documentation/authorization_services/topics/policy/overview.html diff --git a/keycloak/connection.py b/keycloak/connection.py index 8ef45b1..0757377 100644 --- a/keycloak/connection.py +++ b/keycloak/connection.py @@ -33,13 +33,14 @@ from .exceptions import KeycloakConnectionError class ConnectionManager(object): - """Represents a simple server connection. - Args: - base_url (str): The server URL. - headers (dict): The header parameters of the requests to the server. - timeout (int): Timeout to use for requests to the server. - verify (bool): Verify server SSL. - proxies (dict): The proxies servers requests is sent by. + """ + Represents a simple server connection. + + :param base_url: (str) The server URL. + :param headers: (dict) The header parameters of the requests to the server. + :param timeout: (int) Timeout to use for requests to the server. + :param verify: (bool) Verify server SSL. + :param proxies: (dict) The proxies servers requests is sent by. """ def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): @@ -108,11 +109,11 @@ class ConnectionManager(object): self._headers = value def param_headers(self, key): - """Return a specific header parameter. - :arg - key (str): Header parameters key. - :return: - If the header parameters exist, return its value. + """ + Return a specific header parameter. + + :param key: (str) Header parameters key. + :returns: If the header parameters exist, return its value. """ return self.headers.get(key) @@ -122,36 +123,33 @@ class ConnectionManager(object): def exist_param_headers(self, key): """Check if the parameter exists in the header. - :arg - key (str): Header parameters key. - :return: - If the header parameters exist, return True. + + :param key: (str) Header parameters key. + :returns: If the header parameters exist, return True. """ return self.param_headers(key) is not None def add_param_headers(self, key, value): """Add a single parameter inside the header. - :arg - key (str): Header parameters key. - value (str): Value to be added. + + :param key: (str) Header parameters key. + :param value: (str) Value to be added. """ self.headers[key] = value def del_param_headers(self, key): """Remove a specific parameter. - :arg - key (str): Key of the header parameters. + + :param key: (str) Key of the header parameters. """ self.headers.pop(key, None) def raw_get(self, path, **kwargs): """Submit get request to the path. - :arg - path (str): Path for request. - :return - Response the request. - :exception - HttpError: Can't connect to server. + + :param path: (str) Path for request. + :returns: Response the request. + :raises: HttpError Can't connect to server. """ try: @@ -167,13 +165,11 @@ class ConnectionManager(object): def raw_post(self, path, data, **kwargs): """Submit post request to the path. - :arg - path (str): Path for request. - data (dict): Payload for request. - :return - Response the request. - :exception - HttpError: Can't connect to server. + + :param path: (str) Path for request. + :param data: (dict) Payload for request. + :returns: Response the request. + :raises: HttpError Can't connect to server. """ try: return self._s.post( @@ -189,13 +185,11 @@ class ConnectionManager(object): def raw_put(self, path, data, **kwargs): """Submit put request to the path. - :arg - path (str): Path for request. - data (dict): Payload for request. - :return - Response the request. - :exception - HttpError: Can't connect to server. + + :param path: (str) Path for request. + :param data: (dict) Payload for request. + :returns: Response the request. + :raises: HttpError Can't connect to server. """ try: return self._s.put( @@ -212,13 +206,10 @@ class ConnectionManager(object): def raw_delete(self, path, data={}, **kwargs): """Submit delete request to the path. - :arg - path (str): Path for request. - data (dict): Payload for request. - :return - Response the request. - :exception - HttpError: Can't connect to server. + :param path: (str) Path for request. + :param data: (dict) Payload for request. + :returns: Response the request. + :raises: HttpError Can't connect to server. """ try: return self._s.delete( diff --git a/keycloak/keycloak_openid.py b/keycloak/keycloak_openid.py index d9c068f..4205b0b 100644 --- a/keycloak/keycloak_openid.py +++ b/keycloak/keycloak_openid.py @@ -49,6 +49,18 @@ from .urls_patterns import ( class KeycloakOpenID: + """ + Keycloak OpenID client. + + :param server_url: Keycloak server url + :param client_id: client id + :param realm_name: realm name + :param client_secret_key: client secret key + :param verify: True if want check connection SSL + :param custom_headers: dict of custom header to pass to each HTML request + :param proxies: dict of proxies to sent the request by. + """ + def __init__( self, server_url, @@ -59,28 +71,15 @@ class KeycloakOpenID: custom_headers=None, proxies=None, ): - """ - - :param server_url: Keycloak server url - :param client_id: client id - :param realm_name: realm name - :param client_secret_key: client secret key - :param verify: True if want check connection SSL - :param custom_headers: dict of custom header to pass to each HTML request - :param proxies: dict of proxies to sent the request by. - """ - self._client_id = client_id - self._client_secret_key = client_secret_key - self._realm_name = realm_name - headers = dict() - if custom_headers is not None: - # merge custom headers to main headers - headers.update(custom_headers) - self._connection = ConnectionManager( + self.client_id = client_id + self.client_secret_key = client_secret_key + self.realm_name = realm_name + headers = custom_headers if custom_headers is not None else dict() + self.connection = ConnectionManager( base_url=server_url, headers=headers, timeout=60, verify=verify, proxies=proxies ) - self._authorization = Authorization() + self.authorization = Authorization() @property def client_id(self): @@ -206,8 +205,8 @@ class KeycloakOpenID: :param password: :param grant_type: :param code: - :param redirect_uri - :param totp + :param redirect_uri: + :param totp: :return: """ params_path = {"realm-name": self.realm_name} @@ -312,9 +311,9 @@ class KeycloakOpenID: """ Client applications can use a specific endpoint to obtain a special security token called a requesting party token (RPT). This token consists of all the entitlements - (or permissions) for a user as a result of the evaluation of the permissions and authorization - policies associated with the resources being requested. With an RPT, client applications can - gain access to protected resources at the resource server. + (or permissions) for a user as a result of the evaluation of the permissions and + authorization policies associated with the resources being requested. With an RPT, + client applications can gain access to protected resources at the resource server. :return: """ @@ -329,8 +328,8 @@ class KeycloakOpenID: def introspect(self, token, rpt=None, token_type_hint=None): """ - The introspection endpoint is used to retrieve the active state of a token. It is can only be - invoked by confidential clients. + The introspection endpoint is used to retrieve the active state of a token. + It is can only be invoked by confidential clients. https://tools.ietf.org/html/rfc7662 From b911d94db9b44257cb8535457062da33525f7f11 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:41:38 +0200 Subject: [PATCH 010/222] feat: fixed admin client to pass the tests --- keycloak/_version.py | 23 +++++ keycloak/keycloak_admin.py | 166 ++++++++++++++++++++++++++++--------- keycloak/urls_patterns.py | 8 +- 3 files changed, 155 insertions(+), 42 deletions(-) diff --git a/keycloak/_version.py b/keycloak/_version.py index 6c8e6b9..f3403b2 100644 --- a/keycloak/_version.py +++ b/keycloak/_version.py @@ -1 +1,24 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (C) 2017 Marcos Pereira +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + __version__ = "0.0.0" diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 3e30722..8d6463b 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -61,6 +61,7 @@ from .urls_patterns import ( URL_ADMIN_CLIENT_SCOPES_MAPPERS, URL_ADMIN_CLIENT_SECRETS, URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, + URL_ADMIN_CLIENT_ROLE_GROUPS, URL_ADMIN_CLIENTS, URL_ADMIN_COMPONENT, URL_ADMIN_COMPONENTS, @@ -68,7 +69,6 @@ from .urls_patterns import ( URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES, URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE, URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES, - URL_ADMIN_DELETE_USER_ROLE, URL_ADMIN_EVENTS, URL_ADMIN_FLOW, URL_ADMIN_FLOWS, @@ -123,6 +123,23 @@ from .urls_patterns import ( class KeycloakAdmin: + """ + Keycloak Admin client. + + :param server_url: Keycloak server url + :param username: admin username + :param password: admin password + :param totp: Time based OTP + :param realm_name: realm name + :param client_id: client id + :param verify: True if want check connection SSL + :param client_secret_key: client secret key + (optional, required only for access type confidential) + :param custom_headers: dict of custom header to pass to each HTML request + :param user_realm_name: The realm name of the user, if different from realm_name + :param auto_refresh_token: list of methods that allows automatic token refresh. + Ex: ['get', 'put', 'post', 'delete'] + """ PAGE_SIZE = 100 @@ -154,20 +171,6 @@ class KeycloakAdmin: user_realm_name=None, auto_refresh_token=None, ): - """ - - :param server_url: Keycloak server url - :param username: admin username - :param password: admin password - :param totp: Time based OTP - :param realm_name: realm name - :param client_id: client id - :param verify: True if want check connection SSL - :param client_secret_key: client secret key (optional, required only for access type confidential) - :param custom_headers: dict of custom header to pass to each HTML request - :param user_realm_name: The realm name of the user, if different from realm_name - :param auto_refresh_token: list of methods that allows automatic token refresh. ex: ['get', 'put', 'post', 'delete'] - """ self.server_url = server_url self.username = username self.password = password @@ -378,6 +381,20 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_REALMS) return raise_error_from_response(data_raw, KeycloakGetError) + def get_realm(self, realm_name): + """ + Get a specific realm. + + RealmRepresentation: + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation + + :param realm_name: Realm name (not the realm id) + :return: RealmRepresentation + """ + params_path = {"realm-name": realm_name} + data_raw = self.raw_get(URL_ADMIN_REALM.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) + def create_realm(self, payload, skip_exists=False): """ Create a realm @@ -495,7 +512,7 @@ class KeycloakAdmin: data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) - def create_user(self, payload, exist_ok=True): + def create_user(self, payload, exist_ok=False): """ Create a new user. Username must be unique @@ -530,6 +547,33 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + def user_logout(self, user_id): + """ + Logs out user. + + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout + + :param user_id: User id + :return: + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_post(URL_ADMIN_USER_LOGOUT.format(**params_path), data="") + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) + + def user_consents(self, user_id): + """ + Get consents granted by the user + + UserConsentRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation + + :param user_id: User id + :return: List of UserConsentRepresentations + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + def get_user_id(self, username): """ Get internal keycloak user id from username @@ -923,7 +967,7 @@ class KeycloakAdmin: GroupRepresentation https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation - :return: Http response + :return: Group ID for newly created group, otherwise None """ if parent is None: @@ -937,9 +981,14 @@ class KeycloakAdmin: URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response( + raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + try: + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + except KeyError: + return def update_group(self, group_id, payload): """ @@ -1229,18 +1278,27 @@ class KeycloakAdmin: """ Create a client - ClientRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + ClientRepresentation: + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param skip_exists: If true then do not raise an error if client already exists :param payload: ClientRepresentation - :return: Keycloak server response (UserRepresentation) + :return: Client ID """ + if skip_exists: + client_id = self.get_client_id(client_name=payload["name"]) + + if client_id is not None: + return client_id + params_path = {"realm-name": self.realm_name} data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response( + raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client(self, client_id, payload): """ @@ -1368,21 +1426,46 @@ class KeycloakAdmin: Create a client role RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) :param payload: RoleRepresentation :param skip_exists: If true then do not raise an error if client role already exists - :return: Keycloak server response (RoleRepresentation) + :return: Client role name """ + if skip_exists: + res = self.get_client_role(client_id=client_role_id, role_name=payload["name"]) + if res: + return res["name"] + params_path = {"realm-name": self.realm_name, "id": client_role_id} data_raw = self.raw_post( URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response( + raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + + def update_client_role(self, client_role_id, role_name, payload): + """ + Update a client role + + RoleRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + + :param client_role_id: id of client (not client-id) + :param role_name: role's name (not id!) + :param payload: RoleRepresentation + """ + params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} + data_raw = self.raw_put( + URL_ADMIN_CLIENT_ROLE.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): """ @@ -1444,22 +1527,41 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query) + def get_client_role_groups(self, client_id, role_name, **query): + """ + Get group members by client role . + :param client_id: The client id + :param role_name: the name of role to be queried. + :param query: Additional query parameters + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} + return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query) + def create_realm_role(self, payload, skip_exists=False): """ Create a new role for the realm or client :param payload: The role (use RoleRepresentation) :param skip_exists: If true then do not raise an error if realm role already exists - :return Keycloak server response + :return: Realm role name """ + if skip_exists: + role = self.get_realm_role(role_name=payload["name"]) + if role is not None: + return role["name"] + params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response( + raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_realm_role(self, role_name): """ @@ -2506,18 +2608,6 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def delete_user_realm_role(self, user_id, payload): - """ - Delete realm-level role mappings - DELETE admin/realms/{realm-name}/users/{id}/role-mappings/realm - - """ - params_path = {"realm-name": self.realm_name, "id": str(user_id)} - data_raw = self.raw_delete( - URL_ADMIN_DELETE_USER_ROLE.format(**params_path), data=json.dumps(payload) - ) - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def get_client_sessions_stats(self): """ Get current session count for all clients with active sessions diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 43699eb..34d8514 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -65,7 +65,6 @@ URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE = ( ) URL_ADMIN_USER_GROUP = "admin/realms/{realm-name}/users/{id}/groups/{group-id}" URL_ADMIN_USER_GROUPS = "admin/realms/{realm-name}/users/{id}/groups" -URL_ADMIN_USER_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password" URL_ADMIN_USER_CREDENTIALS = "admin/realms/{realm-name}/users/{id}/credentials" URL_ADMIN_USER_CREDENTIAL = "admin/realms/{realm-name}/users/{id}/credentials/{credential_id}" URL_ADMIN_USER_LOGOUT = "admin/realms/{realm-name}/users/{id}/logout" @@ -87,12 +86,15 @@ URL_ADMIN_CLIENT_ROLES = URL_ADMIN_CLIENT + "/roles" URL_ADMIN_CLIENT_ROLE = URL_ADMIN_CLIENT + "/roles/{role-name}" URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE = URL_ADMIN_CLIENT_ROLE + "/composites" URL_ADMIN_CLIENT_ROLE_MEMBERS = URL_ADMIN_CLIENT + "/roles/{role-name}/users" +URL_ADMIN_CLIENT_ROLE_GROUPS = URL_ADMIN_CLIENT + "/roles/{role-name}/groups" URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" URL_ADMIN_CLIENT_AUTHZ_SCOPES = URL_ADMIN_CLIENT + "/authz/resource-server/scope?max=-1" URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS = URL_ADMIN_CLIENT + "/authz/resource-server/permission?max=-1" -URL_ADMIN_CLIENT_AUTHZ_POLICIES = URL_ADMIN_CLIENT + "/authz/resource-server/policy?max=-1" +URL_ADMIN_CLIENT_AUTHZ_POLICIES = ( + URL_ADMIN_CLIENT + "/authz/resource-server/policy?max=-1&permission=false" +) URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = ( URL_ADMIN_CLIENT + "/authz/resource-server/policy/role?max=-1" ) @@ -155,6 +157,4 @@ URL_ADMIN_USER_FEDERATED_IDENTITY = ( ) URL_ADMIN_EVENTS = "admin/realms/{realm-name}/events" - -URL_ADMIN_DELETE_USER_ROLE = "admin/realms/{realm-name}/users/{id}/role-mappings/realm" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" From 6cce29f26b2f0735937f3f6b247dcb6538a3bc05 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:51:07 +0200 Subject: [PATCH 011/222] fix: full tox fix ready --- keycloak/keycloak_admin.py | 826 ++++++++++++++++++------------------- keycloak/urls_patterns.py | 5 +- 2 files changed, 412 insertions(+), 419 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 8d6463b..7b92d80 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -28,6 +28,7 @@ import json from builtins import isinstance from typing import Iterable +from . import urls_patterns from .connection import ConnectionManager from .exceptions import ( KeycloakDeleteError, @@ -37,89 +38,6 @@ from .exceptions import ( raise_error_from_response, ) from .keycloak_openid import KeycloakOpenID -from .urls_patterns import ( - URL_ADMIN_AUTHENTICATOR_CONFIG, - URL_ADMIN_CLIENT, - URL_ADMIN_CLIENT_ALL_SESSIONS, - URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS, - URL_ADMIN_CLIENT_AUTHZ_POLICIES, - URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION, - URL_ADMIN_CLIENT_AUTHZ_RESOURCES, - URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY, - URL_ADMIN_CLIENT_AUTHZ_SCOPES, - URL_ADMIN_CLIENT_AUTHZ_SETTINGS, - URL_ADMIN_CLIENT_INSTALLATION_PROVIDER, - URL_ADMIN_CLIENT_PROTOCOL_MAPPER, - URL_ADMIN_CLIENT_PROTOCOL_MAPPERS, - URL_ADMIN_CLIENT_ROLE, - URL_ADMIN_CLIENT_ROLE_MEMBERS, - URL_ADMIN_CLIENT_ROLES, - URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE, - URL_ADMIN_CLIENT_SCOPE, - URL_ADMIN_CLIENT_SCOPES, - URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, - URL_ADMIN_CLIENT_SCOPES_MAPPERS, - URL_ADMIN_CLIENT_SECRETS, - URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, - URL_ADMIN_CLIENT_ROLE_GROUPS, - URL_ADMIN_CLIENTS, - URL_ADMIN_COMPONENT, - URL_ADMIN_COMPONENTS, - URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE, - URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES, - URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE, - URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES, - URL_ADMIN_EVENTS, - URL_ADMIN_FLOW, - URL_ADMIN_FLOWS, - URL_ADMIN_FLOWS_ALIAS, - URL_ADMIN_FLOWS_COPY, - URL_ADMIN_FLOWS_EXECUTION, - URL_ADMIN_FLOWS_EXECUTIONS, - URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION, - URL_ADMIN_FLOWS_EXECUTIONS_FLOW, - URL_ADMIN_GET_SESSIONS, - URL_ADMIN_GROUP, - URL_ADMIN_GROUP_CHILD, - URL_ADMIN_GROUP_MEMBERS, - URL_ADMIN_GROUP_PERMISSIONS, - URL_ADMIN_GROUPS, - URL_ADMIN_GROUPS_CLIENT_ROLES, - URL_ADMIN_GROUPS_REALM_ROLES, - URL_ADMIN_IDP, - URL_ADMIN_IDP_MAPPERS, - URL_ADMIN_IDPS, - URL_ADMIN_KEYS, - URL_ADMIN_REALM, - URL_ADMIN_REALM_EXPORT, - URL_ADMIN_REALM_ROLES, - URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE, - URL_ADMIN_REALM_ROLES_MEMBERS, - URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, - URL_ADMIN_REALMS, - URL_ADMIN_RESET_PASSWORD, - URL_ADMIN_SEND_UPDATE_ACCOUNT, - URL_ADMIN_SEND_VERIFY_EMAIL, - URL_ADMIN_SERVER_INFO, - URL_ADMIN_USER, - URL_ADMIN_USER_CLIENT_ROLES, - URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, - URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, - URL_ADMIN_USER_CONSENTS, - URL_ADMIN_USER_CREDENTIAL, - URL_ADMIN_USER_CREDENTIALS, - URL_ADMIN_USER_FEDERATED_IDENTITIES, - URL_ADMIN_USER_FEDERATED_IDENTITY, - URL_ADMIN_USER_GROUP, - URL_ADMIN_USER_GROUPS, - URL_ADMIN_USER_LOGOUT, - URL_ADMIN_USER_REALM_ROLES, - URL_ADMIN_USER_REALM_ROLES_AVAILABLE, - URL_ADMIN_USER_REALM_ROLES_COMPOSITE, - URL_ADMIN_USER_STORAGE, - URL_ADMIN_USERS, - URL_ADMIN_USERS_COUNT, -) class KeycloakAdmin: @@ -342,14 +260,14 @@ class KeycloakAdmin: Import a new realm from a RealmRepresentation. Realm name must be unique. RealmRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation :return: RealmRepresentation """ - data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) + data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def export_realm(self, export_clients=False, export_groups_and_role=False): @@ -357,7 +275,7 @@ class KeycloakAdmin: Export the realm configurations in the json format RealmRepresentation - https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_partialexport + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport :param export-clients: Skip if not want to export realm clients :param export-groups-and-roles: Skip if not want to export realm groups and roles @@ -369,7 +287,9 @@ class KeycloakAdmin: "export-clients": export_clients, "export-groups-and-roles": export_groups_and_role, } - data_raw = self.raw_post(URL_ADMIN_REALM_EXPORT.format(**params_path), data="") + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data="" + ) return raise_error_from_response(data_raw, KeycloakPostError) def get_realms(self): @@ -378,7 +298,7 @@ class KeycloakAdmin: :return: realms list """ - data_raw = self.raw_get(URL_ADMIN_REALMS) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALMS) return raise_error_from_response(data_raw, KeycloakGetError) def get_realm(self, realm_name): @@ -392,7 +312,7 @@ class KeycloakAdmin: :return: RealmRepresentation """ params_path = {"realm-name": realm_name} - data_raw = self.raw_get(URL_ADMIN_REALM.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def create_realm(self, payload, skip_exists=False): @@ -400,14 +320,14 @@ class KeycloakAdmin: Create a realm RealmRepresentation: - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation :param skip_exists: Skip if Realm already exist. :return: Keycloak server response (RealmRepresentation) """ - data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload)) + data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) @@ -418,7 +338,7 @@ class KeycloakAdmin: role, or client information in the payload. RealmRepresentation: - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param realm_name: Realm name (not the realm id) :param payload: RealmRepresentation @@ -426,7 +346,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": realm_name} - data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm(self, realm_name): @@ -438,7 +360,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": realm_name} - data_raw = self.raw_delete(URL_ADMIN_REALM.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_users(self, query=None): @@ -446,14 +368,14 @@ class KeycloakAdmin: Return a list of users, filtered according to query parameters UserRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param query: Query parameters (optional) :return: users list """ query = query or {} params_path = {"realm-name": self.realm_name} - url = URL_ADMIN_USERS.format(**params_path) + url = urls_patterns.URL_ADMIN_USERS.format(**params_path) if "first" in query or "max" in query: return self.__fetch_paginated(url, query) @@ -465,12 +387,14 @@ class KeycloakAdmin: Create an ID Provider, IdentityProviderRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :param: payload: IdentityProviderRepresentation """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def add_mapper_to_idp(self, idp_alias, payload): @@ -478,14 +402,14 @@ class KeycloakAdmin: Create an ID Provider, IdentityProviderRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityprovidermapperrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation :param: idp_alias: alias for Idp to add mapper in :param: payload: IdentityProviderMapperRepresentation """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} data_raw = self.raw_post( - URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -494,12 +418,12 @@ class KeycloakAdmin: Returns a list of ID Providers, IdentityProviderRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :return: array IdentityProviderRepresentation """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_IDPS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_idp(self, idp_alias): @@ -509,7 +433,7 @@ class KeycloakAdmin: :param: idp_alias: idp alias name """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} - data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_user(self, payload, exist_ok=False): @@ -517,10 +441,11 @@ class KeycloakAdmin: Create a new user. Username must be unique UserRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param payload: UserRepresentation - :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID. + :param exist_ok: If False, raise KeycloakGetError if username already exists. + Otherwise, return existing user ID. :return: UserRepresentation """ @@ -532,10 +457,12 @@ class KeycloakAdmin: if exists is not None: return str(exists) - data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload) + ) raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def users_count(self): """ @@ -544,34 +471,7 @@ class KeycloakAdmin: :return: counter """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) - - def user_logout(self, user_id): - """ - Logs out user. - - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout - - :param user_id: User id - :return: - """ - params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_post(URL_ADMIN_USER_LOGOUT.format(**params_path), data="") - return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) - - def user_consents(self, user_id): - """ - Get consents granted by the user - - UserConsentRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation - - :param user_id: User id - :return: List of UserConsentRepresentations - """ - params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_user_id(self, username): @@ -580,7 +480,7 @@ class KeycloakAdmin: This is required for further actions against this user. UserRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param username: id in UserRepresentation @@ -597,12 +497,12 @@ class KeycloakAdmin: :param user_id: User id UserRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :return: UserRepresentation """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_user_groups(self, user_id): @@ -614,7 +514,7 @@ class KeycloakAdmin: :return: user groups list """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_GROUPS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def update_user(self, user_id, payload): @@ -627,7 +527,9 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_USER.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_user(self, user_id): @@ -639,7 +541,7 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_delete(URL_ADMIN_USER.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def set_user_password(self, user_id, password, temporary=True): @@ -647,8 +549,8 @@ class KeycloakAdmin: Set up a password for the user. If temporary is True, the user will have to reset the temporary password next time they log in. - https://www.keycloak.org/docs-api/8.0/rest-api/#_users_resource - https://www.keycloak.org/docs-api/8.0/rest-api/#_credentialrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_users_resource + https://www.keycloak.org/docs-api/18.0/rest-api/#_credentialrepresentation :param user_id: User id :param password: New password @@ -659,7 +561,7 @@ class KeycloakAdmin: payload = {"type": "password", "temporary": temporary, "value": password} params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_put( - URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -668,32 +570,13 @@ class KeycloakAdmin: Returns a list of credential belonging to the user. CredentialRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id :return: Keycloak server response (CredentialRepresentation) """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_CREDENTIALS.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) - - def get_credential(self, user_id, credential_id): - """ - Get credential of the user. - - CredentialRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation - - :param: user_id: user id - :param: credential_id: credential id - :return: Keycloak server response (ClientRepresentation) - """ - params_path = { - "realm-name": self.realm_name, - "id": user_id, - "credential_id": credential_id, - } - data_raw = self.raw_get(URL_ADMIN_USER_CREDENTIAL.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_credential(self, user_id, credential_id): @@ -701,7 +584,7 @@ class KeycloakAdmin: Delete credential of the user. CredentialRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id :param: credential_id: credential id @@ -712,42 +595,49 @@ class KeycloakAdmin: "id": user_id, "credential_id": credential_id, } - data_raw = self.raw_delete(URL_ADMIN_USER_CREDENTIAL.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER_CREDENTIAL.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError) - def logout(self, user_id): + def user_logout(self, user_id): """ Logs out user. - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_logout + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout :param user_id: User id :return: """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_post(URL_ADMIN_USER_LOGOUT.format(**params_path), data="") + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_USER_LOGOUT.format(**params_path), data="" + ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) - def consents_user(self, user_id): + def user_consents(self, user_id): """ Get consents granted by the user - :param user_id: User id + UserConsentRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation - :return: consents + :param user_id: User id + :return: List of UserConsentRepresentations """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_user_social_logins(self, user_id): """ - Returns a list of federated identities/social logins of which the user has been associated with + Returns a list of federated identities/social logins of which the user has been associated + with :param user_id: User id :return: federated identities list """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username): @@ -767,9 +657,10 @@ class KeycloakAdmin: } params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} data_raw = self.raw_post( - URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), + data=json.dumps(payload), ) - return raise_error_from_response(data_raw, KeycloakPostError) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204]) def delete_user_social_login(self, user_id, provider_id): @@ -780,7 +671,9 @@ class KeycloakAdmin: :return: """ params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} - data_raw = self.raw_delete(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def send_update_account( @@ -801,7 +694,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} data_raw = self.raw_put( - URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), + urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), data=json.dumps(payload), **params_query ) @@ -821,7 +714,9 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "redirect_uri": redirect_uri} data_raw = self.raw_put( - URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), data={}, **params_query + urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), + data={}, + **params_query ) return raise_error_from_response(data_raw, KeycloakPutError) @@ -832,12 +727,12 @@ class KeycloakAdmin: :param user_id: id of user UserSessionRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_usersessionrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation :return: UserSessionRepresentation """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_server_info(self): @@ -845,11 +740,11 @@ class KeycloakAdmin: Get themes, social providers, auth providers, and event listeners available on this server ServerInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_serverinforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :return: ServerInfoRepresentation """ - data_raw = self.raw_get(URL_ADMIN_SERVER_INFO) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_SERVER_INFO) return raise_error_from_response(data_raw, KeycloakGetError) def get_groups(self, query=None): @@ -857,13 +752,13 @@ class KeycloakAdmin: Returns a list of groups belonging to the realm GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :return: array GroupRepresentation """ query = query or {} params_path = {"realm-name": self.realm_name} - url = URL_ADMIN_GROUPS.format(**params_path) + url = urls_patterns.URL_ADMIN_GROUPS.format(**params_path) if "first" in query or "max" in query: return self.__fetch_paginated(url, query) @@ -875,13 +770,13 @@ class KeycloakAdmin: Get group by id. Returns full group details GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param group_id: The group id :return: Keycloak server response (GroupRepresentation) """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_get(URL_ADMIN_GROUP.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_subgroups(self, group, path): @@ -889,7 +784,7 @@ class KeycloakAdmin: Utility function to iterate through nested group structures GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param name: group (GroupRepresentation) :param path: group path (string) @@ -908,19 +803,21 @@ class KeycloakAdmin: # went through the tree without hits return None - def get_group_members(self, group_id, **query): + def get_group_members(self, group_id, query=None): """ Get members by group id. Returns group members GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_userrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_userrepresentation :param group_id: The group id - :param query: Additional query parameters (see https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getmembers) + :param query: Additional query parameters + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmembers) :return: Keycloak server response (UserRepresentation) """ + query = query or {} params_path = {"realm-name": self.realm_name, "id": group_id} - url = URL_ADMIN_GROUP_MEMBERS.format(**params_path) + url = urls_patterns.URL_ADMIN_GROUP_MEMBERS.format(**params_path) if "first" in query or "max" in query: return self.__fetch_paginated(url, query) @@ -934,7 +831,7 @@ class KeycloakAdmin: Subgroups are traversed, the first to match path (or name with path) is returned. GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param path: group path :param search_in_subgroups: True if want search in the subgroups @@ -952,7 +849,7 @@ class KeycloakAdmin: if group["path"] == path: return group res = self.get_subgroups(group, path) - if res != None: + if res is not None: return res return None @@ -965,20 +862,20 @@ class KeycloakAdmin: :param skip_exists: If true then do not raise an error if it already exists GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation - :return: Group ID for newly created group, otherwise None + :return: Group id for newly created group or None for an existing group """ if parent is None: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( - URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload) ) else: params_path = {"realm-name": self.realm_name, "id": parent} data_raw = self.raw_post( - URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload) ) raise_error_from_response( @@ -998,13 +895,15 @@ class KeycloakAdmin: :param payload: GroupRepresentation with updated information. GroupRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :return: Http response """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_set_permissions(self, group_id, enabled=True): @@ -1018,7 +917,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( - URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), + urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), data=json.dumps({"enabled": enabled}), ) return raise_error_from_response(data_raw, KeycloakPutError) @@ -1033,7 +932,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} - data_raw = self.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_user_remove(self, user_id, group_id): @@ -1046,7 +947,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} - data_raw = self.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def delete_group(self, group_id): @@ -1058,7 +959,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_delete(URL_ADMIN_GROUP.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_clients(self): @@ -1066,13 +967,13 @@ class KeycloakAdmin: Returns a list of clients belonging to the realm ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response (ClientRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_CLIENTS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client(self, client_id): @@ -1080,14 +981,14 @@ class KeycloakAdmin: Get representation of the client ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_id(self, client_name): @@ -1096,7 +997,7 @@ class KeycloakAdmin: This is required for further actions against this client. :param client_name: name in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: client_id (uuid as string) """ @@ -1113,12 +1014,14 @@ class KeycloakAdmin: Get authorization json from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_resource(self, client_id, payload, skip_exists=False): @@ -1126,9 +1029,9 @@ class KeycloakAdmin: Create resources of client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param payload: ResourceRepresentation - https://www.keycloak.org/docs-api/12.0/rest-api/index.html#_resourcerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_resourcerepresentation :return: Keycloak server response """ @@ -1136,7 +1039,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists @@ -1147,12 +1051,14 @@ class KeycloakAdmin: Get resources from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False): @@ -1160,28 +1066,30 @@ class KeycloakAdmin: Create role-based policy of client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param payload: No Document - payload example: - payload={ - "type": "role", - "logic": "POSITIVE", - "decisionStrategy": "UNANIMOUS", - "name": "Policy-1", - "roles": [ - { - "id": id - } - ] - } - :return: Keycloak server response + + Payload example:: + + payload={ + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "name": "Policy-1", + "roles": [ + { + "id": id + } + ] + } + """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path), + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path), data=json.dumps(payload), ) return raise_error_from_response( @@ -1193,29 +1101,31 @@ class KeycloakAdmin: Create resource-based permission of client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param payload: PolicyRepresentation - https://www.keycloak.org/docs-api/12.0/rest-api/index.html#_policyrepresentation - payload example: - payload={ - "type": "resource", - "logic": "POSITIVE", - "decisionStrategy": "UNANIMOUS", - "name": "Permission-Name", - "resources": [ - resource_id - ], - "policies": [ - policy_id - ] - + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_policyrepresentation :return: Keycloak server response + + Payload example:: + + payload={ + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "name": "Permission-Name", + "resources": [ + resource_id + ], + "policies": [ + policy_id + ] + """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path), + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path), data=json.dumps(payload), ) return raise_error_from_response( @@ -1227,12 +1137,12 @@ class KeycloakAdmin: Get scopes from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_permissions(self, client_id): @@ -1240,12 +1150,14 @@ class KeycloakAdmin: Get permissions from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policies(self, client_id): @@ -1253,12 +1165,14 @@ class KeycloakAdmin: Get policies from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_service_account_user(self, client_id): @@ -1266,12 +1180,14 @@ class KeycloakAdmin: Get service account user from client. :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: UserRepresentation """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def create_client(self, payload, skip_exists=False): @@ -1293,7 +1209,9 @@ class KeycloakAdmin: return client_id params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload) + ) raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) @@ -1310,7 +1228,9 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client(self, client_id): @@ -1318,14 +1238,14 @@ class KeycloakAdmin: Get representation of the client ClientRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: keycloak client id (not oauth client-id) :return: Keycloak server response (ClientRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_delete(URL_ADMIN_CLIENT.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_installation_provider(self, client_id, provider_id): @@ -1333,17 +1253,19 @@ class KeycloakAdmin: Get content for given installation provider Related documentation: - https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_clients_resource + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource Possible provider_id list available in the ServerInfoRepresentation#clientInstallations - https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_serverinforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :param client_id: Client id :param provider_id: provider id to specify response format """ params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def get_realm_roles(self): @@ -1351,24 +1273,28 @@ class KeycloakAdmin: Get all roles for the realm or client RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def get_realm_role_members(self, role_name, **query): + def get_realm_role_members(self, role_name, query=None): """ Get role members of realm by role name. :param role_name: Name of the role. - :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_roles_resource) + :param query: Additional Query parameters + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) :return: Keycloak Server Response (UserRepresentation) """ + query = query or dict() params_path = {"realm-name": self.realm_name, "role-name": role_name} - return self.__fetch_all(URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query) + return self.__fetch_all( + urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query + ) def get_client_roles(self, client_id): """ @@ -1377,13 +1303,13 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role(self, client_id, role_name): @@ -1395,12 +1321,12 @@ class KeycloakAdmin: :param role_name: role’s name (not id!) RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: role_id """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} - data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLE.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role_id(self, client_id, role_name): @@ -1414,7 +1340,7 @@ class KeycloakAdmin: :param role_name: role’s name (not id!) RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: role_id """ @@ -1441,7 +1367,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_role_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) ) raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists @@ -1449,24 +1375,6 @@ class KeycloakAdmin: _last_slash_idx = data_raw.headers["Location"].rindex("/") return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 - def update_client_role(self, client_role_id, role_name, payload): - """ - Update a client role - - RoleRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - - :param client_role_id: id of client (not client-id) - :param role_name: role's name (not id!) - :param payload: RoleRepresentation - """ - params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} - data_raw = self.raw_put( - URL_ADMIN_CLIENT_ROLE.format(**params_path), - data=json.dumps(payload), - ) - return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) - def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): """ Add composite roles to client role @@ -1474,29 +1382,46 @@ class KeycloakAdmin: :param client_role_id: id of client (not client-id) :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_post( - URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path), + urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path), data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) + def update_client_role(self, client_role_id, role_name, payload): + """ + Update a client role + + RoleRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + + :param client_role_id: id of client (not client-id) + :param role_name: role's name (not id!) + :param payload: RoleRepresentation + """ + params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def delete_client_role(self, client_role_id, role_name): """ Delete a client role RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) - :param role_name: role’s name (not id!) + :param role_name: role's name (not id!) """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} - data_raw = self.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def assign_client_role(self, user_id, client_id, roles): @@ -1506,13 +1431,14 @@ class KeycloakAdmin: :param user_id: id of user :param client_id: id of client (not client-id) :param roles: roles list or role (use RoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_post( - URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) @@ -1521,11 +1447,14 @@ class KeycloakAdmin: Get members by client role . :param client_id: The client id :param role_name: the name of role to be queried. - :param query: Additional query parameters ( see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_clients_resource) + :param query: Additional query parameters + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) :return: Keycloak server response (UserRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} - return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query) + return self.__fetch_all( + urls_patterns.URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query + ) def get_client_role_groups(self, client_id, role_name, **query): """ @@ -1537,7 +1466,9 @@ class KeycloakAdmin: :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} - return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query) + return self.__fetch_all( + urls_patterns.URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query + ) def create_realm_role(self, payload, skip_exists=False): """ @@ -1555,7 +1486,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( - URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload) ) raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists @@ -1569,11 +1500,13 @@ class KeycloakAdmin: :param role_name: role's name, not id! RoleRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: role_id """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def update_realm_role(self, role_name, payload): @@ -1586,7 +1519,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( - URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -1598,7 +1532,9 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_delete(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_composite_realm_roles_to_role(self, role_name, roles): @@ -1607,13 +1543,13 @@ class KeycloakAdmin: :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_post( - URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), + urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) @@ -1624,13 +1560,13 @@ class KeycloakAdmin: :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be removed - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( - URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), + urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) @@ -1640,11 +1576,13 @@ class KeycloakAdmin: Get composite roles of the role :param role_name: The name of the role - :return Keycloak server response (array RoleRepresentation) + :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "role-name": role_name} - data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def assign_realm_roles(self, user_id, roles): @@ -1653,13 +1591,14 @@ class KeycloakAdmin: :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post( - URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) @@ -1669,13 +1608,14 @@ class KeycloakAdmin: :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete( - URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) @@ -1688,7 +1628,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_available_realm_roles_of_user(self, user_id): @@ -1698,7 +1638,9 @@ class KeycloakAdmin: :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_composite_realm_roles_of_user(self, user_id): @@ -1708,7 +1650,9 @@ class KeycloakAdmin: :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_realm_roles(self, group_id, roles): @@ -1717,13 +1661,14 @@ class KeycloakAdmin: :param group_id: id of groupp :param roles: roles list or role (use GroupRoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_post( - URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) @@ -1733,13 +1678,14 @@ class KeycloakAdmin: :param group_id: id of group :param roles: roles list or role (use GroupRoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete( - URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) @@ -1751,7 +1697,7 @@ class KeycloakAdmin: :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_get(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_client_roles(self, group_id, client_id, roles): @@ -1761,13 +1707,14 @@ class KeycloakAdmin: :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_post( - URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) @@ -1777,11 +1724,11 @@ class KeycloakAdmin: :param group_id: id of group :param client_id: id of client (not client-id) - :return Keycloak server response + :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} - data_raw = self.raw_get(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_group_client_roles(self, group_id, client_id, roles): @@ -1791,13 +1738,14 @@ class KeycloakAdmin: :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) - :return Keycloak server response (array RoleRepresentation) + :return: Keycloak server response (array RoleRepresentation) """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_delete( - URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) @@ -1809,7 +1757,9 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) :return: Keycloak server response (array RoleRepresentation) """ - return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id) + return self._get_client_roles_of_user( + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id + ) def get_available_client_roles_of_user(self, user_id, client_id): """ @@ -1820,7 +1770,7 @@ class KeycloakAdmin: :return: Keycloak server response (array RoleRepresentation) """ return self._get_client_roles_of_user( - URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id ) def get_composite_client_roles_of_user(self, user_id, client_id): @@ -1832,7 +1782,7 @@ class KeycloakAdmin: :return: Keycloak server response (array RoleRepresentation) """ return self._get_client_roles_of_user( - URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id ) def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id): @@ -1852,7 +1802,8 @@ class KeycloakAdmin: payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_delete( - URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) @@ -1861,26 +1812,26 @@ class KeycloakAdmin: Get authentication flows. Returns all flow details AuthenticationFlowRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :return: Keycloak server response (AuthenticationFlowRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_FLOWS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_authentication_flow_for_id(self, flow_id): """ - Get one authentication flow by it's id/alias. Returns all flow details + Get one authentication flow by it's id. Returns all flow details AuthenticationFlowRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param flow_id: the id of a flow NOT it's alias :return: Keycloak server response (AuthenticationFlowRepresentation) """ params_path = {"realm-name": self.realm_name, "flow-id": flow_id} - data_raw = self.raw_get(URL_ADMIN_FLOWS_ALIAS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow(self, payload, skip_exists=False): @@ -1888,22 +1839,25 @@ class KeycloakAdmin: Create a new authentication flow AuthenticationFlowRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation - :param skip_exists: If true then do not raise an error if authentication flow already exists + :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def copy_authentication_flow(self, payload, flow_alias): """ - Copy existing authentication flow under a new name. The new name is given as 'newName' attribute of the passed payload. + Copy existing authentication flow under a new name. The new name is given as 'newName' + attribute of the passed payload. :param payload: JSON containing 'newName' attribute :param flow_alias: the flow alias @@ -1912,7 +1866,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( - URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -1921,13 +1875,13 @@ class KeycloakAdmin: Delete authentication flow AuthenticationInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationinforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationinforepresentation :param flow_id: authentication flow id :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": flow_id} - data_raw = self.raw_delete(URL_ADMIN_FLOW.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOW.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flow_executions(self, flow_alias): @@ -1938,7 +1892,7 @@ class KeycloakAdmin: :return: Response(json) """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_get(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def update_authentication_flow_executions(self, payload, flow_alias): @@ -1946,7 +1900,7 @@ class KeycloakAdmin: Update an authentication flow execution AuthenticationExecutionInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation :param flow_alias: The flow alias @@ -1955,7 +1909,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put( - URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204]) @@ -1964,13 +1919,13 @@ class KeycloakAdmin: Get authentication flow execution. AuthenticationExecutionInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: the execution ID :return: Response(json) """ params_path = {"realm-name": self.realm_name, "id": execution_id} - data_raw = self.raw_get(URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow_execution(self, payload, flow_alias): @@ -1978,7 +1933,7 @@ class KeycloakAdmin: Create an authentication flow execution AuthenticationExecutionInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation :param flow_alias: The flow alias @@ -1987,7 +1942,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( - URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -1996,13 +1952,13 @@ class KeycloakAdmin: Delete authentication flow execution AuthenticationExecutionInfoRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: keycloak client id (not oauth client-id) :return: Keycloak server response (json) """ params_path = {"realm-name": self.realm_name, "id": execution_id} - data_raw = self.raw_delete(URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): @@ -2010,17 +1966,18 @@ class KeycloakAdmin: Create a new sub authentication flow for a given authentication flow AuthenticationFlowRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation :param flow_alias: The flow alias - :param skip_exists: If true then do not raise an error if authentication flow already exists + :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( - URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists @@ -2034,7 +1991,7 @@ class KeycloakAdmin: :return: Response(json) """ params_path = {"realm-name": self.realm_name, "id": config_id} - data_raw = self.raw_get(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def update_authenticator_config(self, payload, config_id): @@ -2042,7 +1999,7 @@ class KeycloakAdmin: Update an authenticator configuration. AuthenticatorConfigRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticatorconfigrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfigrepresentation :param payload: AuthenticatorConfigRepresentation :param config_id: Authenticator config id @@ -2050,21 +2007,24 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_put( - URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_authenticator_config(self, config_id): """ Delete a authenticator configuration. - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authentication_management_resource + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authentication_management_resource :param config_id: Authenticator config id :return: Keycloak server Response """ params_path = {"realm-name": self.realm_name, "id": config_id} - data_raw = self.raw_delete(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def sync_users(self, storage_id, action): @@ -2080,40 +2040,43 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": storage_id} data_raw = self.raw_post( - URL_ADMIN_USER_STORAGE.format(**params_path), data=json.dumps(data), **params_query + urls_patterns.URL_ADMIN_USER_STORAGE.format(**params_path), + data=json.dumps(data), + **params_query ) return raise_error_from_response(data_raw, KeycloakPostError) def get_client_scopes(self): """ Get representation of the client scopes for the realm where we are connected to - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :return: Keycloak server response Array of (ClientScopeRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPES.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scope(self, client_scope_id): """ Get representation of the client scopes for the realm where we are connected to - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_id: The id of the client scope :return: Keycloak server response (ClientScopeRepresentation) """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPE.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def create_client_scope(self, payload, skip_exists=False): """ Create a client scope - ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes + ClientScopeRepresentation: + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param payload: ClientScopeRepresentation :param skip_exists: If true then do not raise an error if client scope already exists @@ -2122,7 +2085,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( - URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists @@ -2132,7 +2095,8 @@ class KeycloakAdmin: """ Update a client scope - ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_client_scopes_resource + ClientScopeRepresentation: + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource :param client_scope_id: The id of the client scope :param payload: ClientScopeRepresentation @@ -2141,14 +2105,14 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_put( - URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_client_scope(self, client_scope_id, payload): """ Add a mapper to a client scope - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_scope_id: The id of the client scope :param payload: ProtocolMapperRepresentation @@ -2158,7 +2122,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -2166,7 +2131,7 @@ class KeycloakAdmin: def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id): """ Delete a mapper from a client scope - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_delete_mapper + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope :param payload: ProtocolMapperRepresentation @@ -2179,13 +2144,15 @@ class KeycloakAdmin: "protocol-mapper-id": protocol_mppaer_id, } - data_raw = self.raw_delete(URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload): """ Update an existing protocol mapper in a client scope - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_protocol_mappers_resource + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: The id of the client scope :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope @@ -2201,7 +2168,8 @@ class KeycloakAdmin: } data_raw = self.raw_put( - URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -2213,7 +2181,9 @@ class KeycloakAdmin: :return: Keycloak server response """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_default_client_scope(self, scope_id): @@ -2224,7 +2194,9 @@ class KeycloakAdmin: :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": scope_id} - data_raw = self.raw_delete(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_default_client_scope(self, scope_id): @@ -2237,7 +2209,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} data_raw = self.raw_put( - URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -2248,7 +2221,9 @@ class KeycloakAdmin: :return: Keycloak server response """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_optional_client_scope(self, scope_id): @@ -2259,7 +2234,9 @@ class KeycloakAdmin: :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": scope_id} - data_raw = self.raw_delete(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_optional_client_scope(self, scope_id): @@ -2272,14 +2249,15 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} data_raw = self.raw_put( - URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_client(self, client_id, payload): """ Add a mapper to a client - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_id: The id of the client :param payload: ProtocolMapperRepresentation @@ -2289,7 +2267,8 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( - URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -2310,7 +2289,8 @@ class KeycloakAdmin: } data_raw = self.raw_put( - URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), + data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -2330,35 +2310,39 @@ class KeycloakAdmin: "protocol-mapper-id": client_mapper_id, } - data_raw = self.raw_delete(URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)) + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def generate_client_secrets(self, client_id): """ Generate a new secret for the client - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_regeneratesecret + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_regeneratesecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_post(URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None + ) return raise_error_from_response(data_raw, KeycloakPostError) def get_client_secrets(self, client_id): """ Get representation of the client secrets - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientsecret + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_SECRETS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_components(self, query=None): @@ -2366,13 +2350,15 @@ class KeycloakAdmin: Return a list of components, filtered according to query parameters ComponentRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param query: Query parameters (optional) :return: components list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query + ) return raise_error_from_response(data_raw, KeycloakGetError) def create_component(self, payload): @@ -2380,7 +2366,7 @@ class KeycloakAdmin: Create a new component. ComponentRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param payload: ComponentRepresentation @@ -2389,7 +2375,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( - URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -2400,12 +2386,12 @@ class KeycloakAdmin: :param component_id: Component id ComponentRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :return: ComponentRepresentation """ params_path = {"realm-name": self.realm_name, "component-id": component_id} - data_raw = self.raw_get(URL_ADMIN_COMPONENT.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def update_component(self, component_id, payload): @@ -2414,13 +2400,13 @@ class KeycloakAdmin: :param component_id: Component id :param payload: ComponentRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :return: Http response """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_put( - URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) @@ -2433,7 +2419,7 @@ class KeycloakAdmin: :return: Http response """ params_path = {"realm-name": self.realm_name, "component-id": component_id} - data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path)) + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_keys(self): @@ -2441,12 +2427,12 @@ class KeycloakAdmin: Return a list of keys, filtered according to query parameters KeysMetadataRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_key_resource + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_key_resource :return: keys list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path), data=None) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None) return raise_error_from_response(data_raw, KeycloakGetError) def get_events(self, query=None): @@ -2454,12 +2440,14 @@ class KeycloakAdmin: Return a list of events, filtered according to query parameters EventRepresentation array - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_eventrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_eventrepresentation :return: events list """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(URL_ADMIN_EVENTS.format(**params_path), data=None, **query) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_EVENTS.format(**params_path), data=None, **query + ) return raise_error_from_response(data_raw, KeycloakGetError) def set_events(self, payload): @@ -2467,12 +2455,14 @@ class KeycloakAdmin: Set realm events configuration RealmEventsConfigRepresentation - https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmeventsconfigrepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmeventsconfigrepresentation :return: Http response """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_put(URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload)) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload) + ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def raw_get(self, *args, **kwargs): @@ -2518,8 +2508,8 @@ class KeycloakAdmin: """ Calls connection.raw_delete. - If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token - and try *delete* once more. + If auto_refresh is set for *delete* and *access_token* is expired, + it will refresh the token and try *delete* once more. """ r = self.connection.raw_delete(*args, **kwargs) if "delete" in self.auto_refresh_token and r.status_code == 401: @@ -2551,7 +2541,7 @@ class KeycloakAdmin: self.realm_name = self.user_realm_name if self.username and self.password: - self._token = self.keycloak_openid.token( + self.token = self.keycloak_openid.token( self.username, self.password, grant_type=grant_type, totp=self.totp ) @@ -2560,14 +2550,14 @@ class KeycloakAdmin: "Content-Type": "application/json", } else: - self._token = None + self.token = None headers = {} if self.custom_headers is not None: # merge custom headers to main headers headers.update(self.custom_headers) - self._connection = ConnectionManager( + self.connection = ConnectionManager( base_url=self.server_url, headers=headers, timeout=60, verify=self.verify ) @@ -2600,22 +2590,22 @@ class KeycloakAdmin: :param client_id: id of client UserSessionRepresentation - http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation + http://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation :return: UserSessionRepresentation """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_sessions_stats(self): """ Get current session count for all clients with active sessions - https://www.keycloak.org/docs-api/16.1/rest-api/index.html#_getclientsessionstats + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsessionstats :return: Dict of clients and session count """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(self.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 34d8514..7450f2e 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -124,7 +124,10 @@ URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE = ( "admin/realms/{realm-name}/roles/{role-name}/composites" ) -URL_ADMIN_REALM_EXPORT = "admin/realms/{realm-name}/partial-export?exportClients={export-clients}&exportGroupsAndRoles={export-groups-and-roles}" +URL_ADMIN_REALM_EXPORT = ( + "admin/realms/{realm-name}/partial-export?exportClients={export-clients}&" + + "exportGroupsAndRoles={export-groups-and-roles}" +) URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES = URL_ADMIN_REALM + "/default-default-client-scopes" URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE = URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES + "/{id}" From 3636de017762b5ca48c5bdb9ccc02894d6bd8014 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:57:49 +0200 Subject: [PATCH 012/222] docs: added autoapi to docs --- .gitignore | 3 +- README.md | 70 +++++----- docs/source/conf.py | 19 ++- docs/source/index.rst | 303 +---------------------------------------- docs/source/readme.rst | 1 + 5 files changed, 58 insertions(+), 338 deletions(-) create mode 100644 docs/source/readme.rst diff --git a/.gitignore b/.gitignore index 4c8d46d..24f085b 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,5 @@ ENV/ main.py main2.py s3air-authz-config.json -.vscode \ No newline at end of file +.vscode +_build \ No newline at end of file diff --git a/README.md b/README.md index 68a2dc5..01d6e0f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ -[![CircleCI](https://circleci.com/gh/marcospereirampj/python-keycloak/tree/master.svg?style=svg)](https://circleci.com/gh/marcospereirampj/python-keycloak/tree/master) +[![CircleCI](https://github.com/marcospereirampj/python-keycloak/actions/workflows/daily.yaml/badge.svg)](https://github.com/marcospereirampj/python-keycloak/) [![Documentation Status](https://readthedocs.org/projects/python-keycloak/badge/?version=latest)](http://python-keycloak.readthedocs.io/en/latest/?badge=latest) - -Python Keycloak -==================== +# Python Keycloak For review- see https://github.com/marcospereirampj/python-keycloak @@ -13,24 +11,27 @@ For review- see https://github.com/marcospereirampj/python-keycloak ### Via Pypi Package: -``` $ pip install python-keycloak ``` +`$ pip install python-keycloak` ### Manually -``` $ python setup.py install ``` +`$ python setup.py install` ## Dependencies python-keycloak depends on: -* Python 3 -* [requests](https://requests.readthedocs.io) -* [python-jose](http://python-jose.readthedocs.io/en/latest/) +- Python 3 +- [requests](https://requests.readthedocs.io) +- [python-jose](http://python-jose.readthedocs.io/en/latest/) +- [urllib3](https://urllib3.readthedocs.io/en/stable/) ### Tests Dependencies -* unittest -* [httmock](https://github.com/patrys/httmock) +- [tox](https://tox.readthedocs.io/) +- [pytest](https://docs.pytest.org/en/latest/) +- [pytest-cov](https://github.com/pytest-dev/pytest-cov) +- [wheel](https://github.com/pypa/wheel) ## Bug reports @@ -43,18 +44,19 @@ The documentation for python-keycloak is available on [readthedocs](http://pytho ## Contributors -* [Agriness Team](http://www.agriness.com/pt/) -* [Marcos Pereira](marcospereira.mpj@gmail.com) -* [Martin Devlin](https://bitbucket.org/devlinmpearson/) -* [Shon T. Urbas](https://bitbucket.org/surbas/) -* [Markus Spanier](https://bitbucket.org/spanierm/) -* [Remco Kranenburg](https://bitbucket.org/Remco47/) -* [Armin](https://bitbucket.org/arminfelder/) -* [njordr](https://bitbucket.org/njordr/) -* [Josha Inglis](https://bitbucket.org/joshainglis/) -* [Alex](https://bitbucket.org/alex_zel/) -* [Ewan Jone](https://bitbucket.org/kisamoto/) -* [Lukas Martini](https://github.com/lutoma) +- [Agriness Team](http://www.agriness.com/pt/) +- [Marcos Pereira](marcospereira.mpj@gmail.com) +- [Martin Devlin](https://bitbucket.org/devlinmpearson/) +- [Shon T. Urbas](https://bitbucket.org/surbas/) +- [Markus Spanier](https://bitbucket.org/spanierm/) +- [Remco Kranenburg](https://bitbucket.org/Remco47/) +- [Armin](https://bitbucket.org/arminfelder/) +- [njordr](https://bitbucket.org/njordr/) +- [Josha Inglis](https://bitbucket.org/joshainglis/) +- [Alex](https://bitbucket.org/alex_zel/) +- [Ewan Jone](https://bitbucket.org/kisamoto/) +- [Lukas Martini](https://github.com/lutoma) +- [Adamatics](https://www.adamatics.com) ## Usage @@ -119,13 +121,13 @@ keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", user_realm_name="only_if_other_realm_than_master", client_secret_key="client-secret", verify=True) - -# Add user + +# Add user new_user = keycloak_admin.create_user({"email": "example@example.com", "username": "example@example.com", "enabled": True, "firstName": "Example", - "lastName": "Example"}) + "lastName": "Example"}) # Add user and raise exception if username already exists # exist_ok currently defaults to True for backwards compatibility reasons @@ -135,8 +137,8 @@ new_user = keycloak_admin.create_user({"email": "example@example.com", "firstName": "Example", "lastName": "Example"}, exist_ok=False) - -# Add user and set password + +# Add user and set password new_user = keycloak_admin.create_user({"email": "example@example.com", "username": "example@example.com", "enabled": True, @@ -144,7 +146,7 @@ new_user = keycloak_admin.create_user({"email": "example@example.com", "lastName": "Example", "credentials": [{"value": "secret","type": "password",}]}) -# Add user and specify a locale +# Add user and specify a locale new_user = keycloak_admin.create_user({"email": "example@example.fr", "username": "example@example.fr", "enabled": True, @@ -152,7 +154,7 @@ new_user = keycloak_admin.create_user({"email": "example@example.fr", "lastName": "Example", "attributes": { "locale": ["fr"] - }) + }) # User counter count_users = keycloak_admin.users_count() @@ -167,7 +169,7 @@ user_id_keycloak = keycloak_admin.get_user_id("example@example.com") user = keycloak_admin.get_user("user-id-keycloak") # Update User -response = keycloak_admin.update_user(user_id="user-id-keycloak", +response = keycloak_admin.update_user(user_id="user-id-keycloak", payload={'firstName': 'Example Update'}) # Update User Password @@ -181,7 +183,7 @@ credential = keycloak_admin.get_credential(user_id='user_id', credential_id='cre # Delete User Credential response = keycloak_admin.delete_credential(user_id='user_id', credential_id='credential_id') - + # Delete User response = keycloak_admin.delete_user(user_id="user-id-keycloak") @@ -189,7 +191,7 @@ response = keycloak_admin.delete_user(user_id="user-id-keycloak") consents = keycloak_admin.consents_user(user_id="user-id-keycloak") # Send User Action -response = keycloak_admin.send_update_account(user_id="user-id-keycloak", +response = keycloak_admin.send_update_account(user_id="user-id-keycloak", payload=json.dumps(['UPDATE_PASSWORD'])) # Send Verify Email @@ -260,7 +262,7 @@ group = keycloak_admin.create_group({"name": "Example Group"}) # Get all groups groups = keycloak_admin.get_groups() -# Get group +# Get group group = keycloak_admin.get_group(group_id='group_id') # Get group by name diff --git a/docs/source/conf.py b/docs/source/conf.py index 23e9bbf..7d0b77d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,6 +21,7 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) import sphinx_rtd_theme +from keycloak import __version__ # -- General configuration ------------------------------------------------ @@ -36,8 +37,16 @@ extensions = [ "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.viewcode", + "m2r2", + "autoapi.extension", ] +autoapi_type = "python" +autoapi_dirs = ["../../keycloak"] +autoapi_root = "reference" +autoapi_keep_files = False +autoapi_add_toctree_entry = False + # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -60,9 +69,9 @@ author = "Marcos Pereira" # built documents. # # The short X.Y version. -version = "0.27.1" +version = __version__ # The full version, including alpha/beta/rc tags. -release = "0.27.1" +release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -103,7 +112,7 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +# html_static_path = ["_static"] html_use_smartypants = False @@ -160,7 +169,7 @@ latex_documents = [ "python-keycloak Documentation", "Marcos Pereira", "manual", - ) + ), ] @@ -185,5 +194,5 @@ texinfo_documents = [ "python-keycloak", "One line description of project.", "Miscellaneous", - ) + ), ] diff --git a/docs/source/index.rst b/docs/source/index.rst index 6675352..6dff08d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,305 +3,12 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. image:: https://readthedocs.org/projects/adamatics-keycloak/badge/?version=latest + :target: https://adamatics-keycloak.readthedocs.io/en/latest/?badge=latest +.. mdinclude:: ../../README.md .. toctree:: :maxdepth: 2 :caption: Contents: - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. image:: https://readthedocs.org/projects/python-keycloak/badge/?version=latest - :target: http://python-keycloak.readthedocs.io/en/latest/?badge=latest - - -Welcome to python-keycloak's documentation! -=========================================== - -**python-keycloak** is a Python package providing access to the Keycloak API. - -Installation -================== - -Via Pypi Package:: - - $ pip install python-keycloak - -Manually:: - - $ python setup.py install - -Dependencies -================== - -python-keycloak depends on: - -* Python 3 -* `requests `_ -* `python-jose `_ - -Tests Dependencies ------------------- - -* unittest -* `httmock `_ - -Bug reports -================== - -Please report bugs and feature requests at -`https://github.com/marcospereirampj/python-keycloak/issues `_ - -Documentation -================== - -The documentation for python-keycloak is available on `readthedocs `_. - -Contributors -================== - -* `Agriness Team `_ -* `Marcos Pereira `_ -* `Martin Devlin `_ -* `Shon T. Urbas `_ -* `Markus Spanier `_ -* `Remco Kranenburg `_ -* `Armin `_ -* `Njordr `_ -* `Josha Inglis `_ -* `Alex `_ -* `Ewan Jone `_ - -Usage -===== - -Main methods:: - - # KEYCLOAK OPENID - - from keycloak import KeycloakOpenID - - # Configure client - keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", - client_id="example_client", - realm_name="example_realm", - client_secret_key="secret", - verify=True) - - # Optionally, you can pass custom headers that will be added to all HTTP calls - # keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", - # client_id="example_client", - # realm_name="example_realm", - # client_secret_key="secret", - # verify=True, - # custom_headers={'CustomHeader': 'value'}) - - # Optionally, you can pass proxies as well that will be used in all HTTP calls. See requests documentation for more details_ - # `requests-proxies `_. - # keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", - # client_id="example_client", - # realm_name="example_realm", - # client_secret_key="secret", - # verify=True, - # proxies={'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080'}) - - # Get WellKnow - config_well_know = keycloak_openid.well_know() - - # Get Token - token = keycloak_openid.token("user", "password") - token = keycloak_openid.token("user", "password", totp="012345") - - # Get Userinfo - userinfo = keycloak_openid.userinfo(token['access_token']) - - # Refresh token - token = keycloak_openid.refresh_token(token['refresh_token']) - - # Logout - keycloak_openid.logout(token['refresh_token']) - - # Get Certs - certs = keycloak_openid.certs() - - # Get RPT (Entitlement) - token = keycloak_openid.token("user", "password") - rpt = keycloak_openid.entitlement(token['access_token'], "resource_id") - - # Instropect RPT - token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['access_token'], rpt=rpt['rpt'], - token_type_hint="requesting_party_token")) - - # Introspect Token - token_info = keycloak_openid.introspect(token['access_token'])) - - # Decode Token - KEYCLOAK_PUBLIC_KEY = "secret" - options = {"verify_signature": True, "verify_aud": True, "verify_exp": True} - token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options) - - # Get permissions by token - token = keycloak_openid.token("user", "password") - keycloak_openid.load_authorization_config("example-authz-config.json") - policies = keycloak_openid.get_policies(token['access_token'], method_token_info='decode', key=KEYCLOAK_PUBLIC_KEY) - permissions = keycloak_openid.get_permissions(token['access_token'], method_token_info='introspect') - - # KEYCLOAK ADMIN - - from keycloak import KeycloakAdmin - - keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", - username='example-admin', - password='secret', - realm_name="example_realm", - verify=True) - - # Optionally, you can pass custom headers that will be added to all HTTP calls - #keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", - # username='example-admin', - # password='secret', - # realm_name="example_realm", - # verify=True, - # custom_headers={'CustomHeader': 'value'}) - # - # You can also authenticate with client_id and client_secret - #keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", - # client_id="example_client", - # client_secret_key="secret", - # realm_name="example_realm", - # verify=True, - # custom_headers={'CustomHeader': 'value'}) - - # Add user - new_user = keycloak_admin.create_user({"email": "example@example.com", - "username": "example@example.com", - "enabled": True, - "firstName": "Example", - "lastName": "Example", - "realmRoles": ["user_default", ], - "attributes": {"example": "1,2,3,3,"}}) - - - # Add user and set password - new_user = keycloak_admin.create_user({"email": "example@example.com", - "username": "example@example.com", - "enabled": True, - "firstName": "Example", - "lastName": "Example", - "credentials": [{"value": "secret","type": "password",}], - "realmRoles": ["user_default", ], - "attributes": {"example": "1,2,3,3,"}}) - - # User counter - count_users = keycloak_admin.users_count() - - # Get users Returns a list of users, filtered according to query parameters - users = keycloak_admin.get_users({}) - - # Get user ID from name - user-id-keycloak = keycloak_admin.get_user_id("example@example.com") - - # Get User - user = keycloak_admin.get_user("user-id-keycloak") - - # Update User - response = keycloak_admin.update_user(user_id="user-id-keycloak", - payload={'firstName': 'Example Update'}) - - # Update User Password - response = set_user_password(user_id="user-id-keycloak", password="secret", temporary=True) - - # Delete User - response = keycloak_admin.delete_user(user_id="user-id-keycloak") - - # Get consents granted by the user - consents = keycloak_admin.consents_user(user_id="user-id-keycloak") - - # Send User Action - response = keycloak_admin.send_update_account(user_id="user-id-keycloak", - payload=json.dumps(['UPDATE_PASSWORD'])) - - # Send Verify Email - response = keycloak_admin.send_verify_email(user_id="user-id-keycloak") - - # Get sessions associated with the user - sessions = keycloak_admin.get_sessions(user_id="user-id-keycloak") - - # Get themes, social providers, auth providers, and event listeners available on this server - server_info = keycloak_admin.get_server_info() - - # Get clients belonging to the realm Returns a list of clients belonging to the realm - clients = keycloak_admin.get_clients() - - # Get client - id (not client-id) from client by name - client_id=keycloak_admin.get_client_id("my-client") - - # Get representation of the client - id of client (not client-id) - client = keycloak_admin.get_client(client_id="client_id") - - # Get all roles for the realm or client - realm_roles = keycloak_admin.get_realm_roles() - - # Get all roles for the client - client_roles = keycloak_admin.get_client_roles(client_id="client_id") - - # Get client role - role = keycloak_admin.get_client_role(client_id="client_id", role_name="role_name") - - # Warning: Deprecated - # Get client role id from name - role_id = keycloak_admin.get_client_role_id(client_id="client_id", role_name="test") - - # Create client role - keycloak_admin.create_client_role(client_id="client_id", {'name': 'roleName', 'clientRole': True}) - - # Get client role id from name - role_id = keycloak_admin.get_client_role_id(client_id=client_id, role_name="test") - - # Get all roles for the realm or client - realm_roles = keycloak_admin.get_roles() - - # Assign client role to user. Note that BOTH role_name and role_id appear to be required. - keycloak_admin.assign_client_role(client_id="client_id", user_id="user_id", role_id="role_id", role_name="test") - - # Assign realm roles to user. Note that BOTH role_name and role_id appear to be required. - keycloak_admin.assign_realm_roles(client_id="client_id", user_id="user_id", roles=[{"roles_representation"}]) - - # Delete realm roles of user. Note that BOTH role_name and role_id appear to be required. - keycloak_admin.deletes_realm_roles_of_user(user_id="user_id", roles=[{"roles_representation"}]) - - # Create new group - group = keycloak_admin.create_group(name="Example Group") - - # Get all groups - groups = keycloak_admin.get_groups() - - # Get group - group = keycloak_admin.get_group(group_id='group_id') - - # Get group by path - group = keycloak_admin.get_group_by_path(path='/group/subgroup', search_in_subgroups=True) - - # Function to trigger user sync from provider - sync_users(storage_id="storage_di", action="action") - - # List public RSA keys - components = keycloak_admin.keys - - # List all keys - components = keycloak_admin.get_components(query={"parent":"example_realm", "type":"org.keycloak.keys.KeyProvider"}) - - # Create a new RSA key - component = keycloak_admin.create_component({"name":"rsa-generated","providerId":"rsa-generated","providerType":"org.keycloak.keys.KeyProvider","parentId":"example_realm","config":{"priority":["100"],"enabled":["true"],"active":["true"],"algorithm":["RS256"],"keySize":["2048"]}}) - - # Update the key - component_details['config']['active'] = ["false"] - keycloak_admin.update_component(component['id']) - - # Delete the key - keycloak_admin.delete_component(component['id']) - + readme + reference/keycloak/index diff --git a/docs/source/readme.rst b/docs/source/readme.rst new file mode 100644 index 0000000..3bd447c --- /dev/null +++ b/docs/source/readme.rst @@ -0,0 +1 @@ +.. mdinclude:: ../../README.md From 51cb44fe3aca1ed91e4d2da0ecbc2498f8217bc9 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 12:59:25 +0200 Subject: [PATCH 013/222] refactor: isort conf.py --- docs/source/conf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7d0b77d..403d465 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,6 +21,7 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) import sphinx_rtd_theme + from keycloak import __version__ # -- General configuration ------------------------------------------------ @@ -169,7 +170,7 @@ latex_documents = [ "python-keycloak Documentation", "Marcos Pereira", "manual", - ), + ) ] @@ -194,5 +195,5 @@ texinfo_documents = [ "python-keycloak", "One line description of project.", "Miscellaneous", - ), + ) ] From fa9e56ef42102f34986ead5365cc7854e4c1ca63 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 14:45:06 +0200 Subject: [PATCH 014/222] feat: added authenticator providers getters --- keycloak/keycloak_admin.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 7b92d80..df53859 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1983,6 +1983,34 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + def get_authenticator_providers(self): + """ + Get authenticator providers list. + + :return: Response(json) + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_AUTHENTICATOR_PROVIDERS.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_authenticator_provider_config_description(self, provider_id): + """ + Get authenticator's provider configuration description. + + AuthenticatorConfigInfoRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfiginforepresentation + + :param provider_id: Provider Id + :return: AuthenticatorConfigInfoRepresentation + """ + params_path = {"realm-name": self.realm_name, "provider-id": provider_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG_DESCRIPTION.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + def get_authenticator_config(self, config_id): """ Get authenticator configuration. Returns all configuration details. From 7ae0442370a86c821e1bf8f2e2f4c2f6abe55e16 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 14:45:21 +0200 Subject: [PATCH 015/222] test: test authenticator and configurations --- keycloak/urls_patterns.py | 6 ++++++ tests/test_keycloak_admin.py | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 7450f2e..071c733 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -148,6 +148,12 @@ URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION = ( URL_ADMIN_FLOWS_EXECUTIONS_FLOW = ( "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" ) +URL_ADMIN_AUTHENTICATOR_PROVIDERS = ( + "admin/realms/{realm-name}/authentication/authenticator-providers" +) +URL_ADMIN_AUTHENTICATOR_CONFIG_DESCRIPTION = ( + "admin/realms/{realm-name}/authentication/config-description/{provider-id}" +) URL_ADMIN_AUTHENTICATOR_CONFIG = "admin/realms/{realm-name}/authentication/config/{id}" URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 29d3b15..6b04af7 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1199,3 +1199,43 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakDeleteError) as err: admin.delete_authentication_flow(flow_id=flow_id) assert err.match('404: b\'{"error":"Could not find flow with id"}\'') + + +def test_authentication_configs(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Test list of auth providers + res = admin.get_authenticator_providers() + assert len(res) == 39 + + res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie") + assert res == { + "helpText": "Validates the SSO cookie set by the auth server.", + "name": "Cookie", + "properties": [], + "providerId": "auth-cookie", + } + + # Test authenticator config + # Currently unable to find a sustainable way to fetch the config id, + # therefore testing only failures + with pytest.raises(KeycloakGetError) as err: + admin.get_authenticator_config(config_id="bad") + assert err.match('404: b\'{"error":"Could not find authenticator config"}\'') + + with pytest.raises(KeycloakPutError) as err: + admin.update_authenticator_config(payload=dict(), config_id="bad") + assert err.match('404: b\'{"error":"Could not find authenticator config"}\'') + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_authenticator_config(config_id="bad") + assert err.match('404: b\'{"error":"Could not find authenticator config"}\'') + + +def test_sync_users(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Only testing the error message + with pytest.raises(KeycloakPostError) as err: + admin.sync_users(storage_id="does-not-exist", action="triggerFullSync") + assert err.match('404: b\'{"error":"Could not find component"}\'') From f5f4de2bfa6a4b89651a762262ea82a16673f7bf Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 16:41:41 +0200 Subject: [PATCH 016/222] chore: fix email format --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8f3b7fc..7b3903b 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( url="https://github.com/marcospereirampj/python-keycloak", license="The MIT License", author="Marcos Pereira, Richard Nemeth", - author_email="marcospereira.mpj@gmail.com; ryshoooo@gmail.com", + author_email="marcospereira.mpj@gmail.com, ryshoooo@gmail.com", keywords="keycloak openid oidc", description="python-keycloak is a Python package providing access to the Keycloak API.", long_description=long_description, From 0b35ddc66b974ad34e2a3e8037c3256a9ff7a635 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 17:08:44 +0200 Subject: [PATCH 017/222] docs: fix the docs build --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 7aa6ce5..09965df 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,4 +7,4 @@ build: python: install: - - requirements: docs-requirements.txt + - requirements: .[docs] \ No newline at end of file From 23b943f301cbcb50c8e4aae41ce3be2ef5ba5827 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 17:12:26 +0200 Subject: [PATCH 018/222] docs: fix the docs conf --- .readthedocs.yaml | 2 +- docs/source/conf.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 09965df..7aa6ce5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,4 +7,4 @@ build: python: install: - - requirements: .[docs] \ No newline at end of file + - requirements: docs-requirements.txt diff --git a/docs/source/conf.py b/docs/source/conf.py index 403d465..3a70cb6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,8 +22,6 @@ # sys.path.insert(0, os.path.abspath('.')) import sphinx_rtd_theme -from keycloak import __version__ - # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -70,9 +68,9 @@ author = "Marcos Pereira" # built documents. # # The short X.Y version. -version = __version__ +version = "0.0.0" # The full version, including alpha/beta/rc tags. -release = __version__ +release = "0.0.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 6154a2d5f75df45eaa5b322d4198e9f9066ae4a5 Mon Sep 17 00:00:00 2001 From: Sam Mesterton-Gibbons Date: Thu, 19 May 2022 17:40:41 +0100 Subject: [PATCH 019/222] fix: Add missing keycloak.authorization package Re-adds `keycloak.authorization` to the list of packages in setup.py so it gets included in wheels. Fixes #311 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7b3903b..86a6fb2 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup( description="python-keycloak is a Python package providing access to the Keycloak API.", long_description=long_description, long_description_content_type="text/markdown", - packages=["keycloak"], + packages=["keycloak", "keycloak.authorization"], install_requires=reqs, tests_require=dev_reqs, extras_require={"docs": docs_reqs}, From 54beb51fba930314c66b18ee01a328fab544237a Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 19:24:50 +0200 Subject: [PATCH 020/222] chore: move source files into src folder Moved all of the source files into the src folder --- {keycloak => src/keycloak}/__init__.py | 0 {keycloak => src/keycloak}/_version.py | 0 {keycloak => src/keycloak}/authorization/__init__.py | 0 {keycloak => src/keycloak}/authorization/permission.py | 0 {keycloak => src/keycloak}/authorization/policy.py | 0 {keycloak => src/keycloak}/authorization/role.py | 0 {keycloak => src/keycloak}/connection.py | 0 {keycloak => src/keycloak}/exceptions.py | 0 {keycloak => src/keycloak}/keycloak_admin.py | 0 {keycloak => src/keycloak}/keycloak_openid.py | 0 {keycloak => src/keycloak}/urls_patterns.py | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename {keycloak => src/keycloak}/__init__.py (100%) rename {keycloak => src/keycloak}/_version.py (100%) rename {keycloak => src/keycloak}/authorization/__init__.py (100%) rename {keycloak => src/keycloak}/authorization/permission.py (100%) rename {keycloak => src/keycloak}/authorization/policy.py (100%) rename {keycloak => src/keycloak}/authorization/role.py (100%) rename {keycloak => src/keycloak}/connection.py (100%) rename {keycloak => src/keycloak}/exceptions.py (100%) rename {keycloak => src/keycloak}/keycloak_admin.py (100%) rename {keycloak => src/keycloak}/keycloak_openid.py (100%) rename {keycloak => src/keycloak}/urls_patterns.py (100%) diff --git a/keycloak/__init__.py b/src/keycloak/__init__.py similarity index 100% rename from keycloak/__init__.py rename to src/keycloak/__init__.py diff --git a/keycloak/_version.py b/src/keycloak/_version.py similarity index 100% rename from keycloak/_version.py rename to src/keycloak/_version.py diff --git a/keycloak/authorization/__init__.py b/src/keycloak/authorization/__init__.py similarity index 100% rename from keycloak/authorization/__init__.py rename to src/keycloak/authorization/__init__.py diff --git a/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py similarity index 100% rename from keycloak/authorization/permission.py rename to src/keycloak/authorization/permission.py diff --git a/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py similarity index 100% rename from keycloak/authorization/policy.py rename to src/keycloak/authorization/policy.py diff --git a/keycloak/authorization/role.py b/src/keycloak/authorization/role.py similarity index 100% rename from keycloak/authorization/role.py rename to src/keycloak/authorization/role.py diff --git a/keycloak/connection.py b/src/keycloak/connection.py similarity index 100% rename from keycloak/connection.py rename to src/keycloak/connection.py diff --git a/keycloak/exceptions.py b/src/keycloak/exceptions.py similarity index 100% rename from keycloak/exceptions.py rename to src/keycloak/exceptions.py diff --git a/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py similarity index 100% rename from keycloak/keycloak_admin.py rename to src/keycloak/keycloak_admin.py diff --git a/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py similarity index 100% rename from keycloak/keycloak_openid.py rename to src/keycloak/keycloak_openid.py diff --git a/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py similarity index 100% rename from keycloak/urls_patterns.py rename to src/keycloak/urls_patterns.py From 04cc2feeeefe8bb2b83d175c72b79b11c8215ec0 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 19:26:19 +0200 Subject: [PATCH 021/222] chore: update setup py to find source files in src folder --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 86a6fb2..27a188d 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import re -from setuptools import setup + +from setuptools import find_packages, setup with open("README.md", "r") as fh: long_description = fh.read() @@ -15,7 +16,7 @@ with open("docs-requirements.txt", "r") as fh: docs_reqs = fh.read().split("\n") -VERSIONFILE = "keycloak/_version.py" +VERSIONFILE = "src/keycloak/_version.py" verstrline = open(VERSIONFILE, "rt").read() VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" mo = re.search(VSRE, verstrline, re.M) @@ -35,7 +36,8 @@ setup( description="python-keycloak is a Python package providing access to the Keycloak API.", long_description=long_description, long_description_content_type="text/markdown", - packages=["keycloak", "keycloak.authorization"], + packages=find_packages("src"), + package_dir={"": "src"}, install_requires=reqs, tests_require=dev_reqs, extras_require={"docs": docs_reqs}, From bc82de7989efc880cf1946e161a6747a73418e74 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 19:27:08 +0200 Subject: [PATCH 022/222] test: update the test framework Updated the tox framework to operate on files in src for checks, but in toxenv for tests --- docs/source/conf.py | 2 +- tox.ini | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3a70cb6..2b67d12 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -41,7 +41,7 @@ extensions = [ ] autoapi_type = "python" -autoapi_dirs = ["../../keycloak"] +autoapi_dirs = ["../../src/keycloak"] autoapi_root = "reference" autoapi_keep_files = False autoapi_add_toctree_entry = False diff --git a/tox.ini b/tox.ini index 2726f66..54b2c2b 100644 --- a/tox.ini +++ b/tox.ini @@ -10,9 +10,9 @@ deps = isort flake8 commands = - black --check --diff keycloak tests docs - isort -c --df keycloak tests docs - flake8 keycloak tests docs + black --check --diff src/keycloak tests docs setup.py + isort -c --df src/keycloak tests docs setup.py + flake8 src/keycloak tests docs setup.py [testenv:apply-check] deps = @@ -20,9 +20,9 @@ deps = isort flake8 commands = - black -C keycloak tests docs - black keycloak tests docs - isort keycloak tests docs + black -C src/keycloak tests docs setup.py + black src/keycloak tests docs setup.py + isort src/keycloak tests docs setup.py [testenv:docs] deps = From 960af199b43efab5f305599892b436ff71c06759 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 22:01:33 +0200 Subject: [PATCH 023/222] fix: escape when get role fails --- src/keycloak/keycloak_admin.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index df53859..ffc3cde 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1361,9 +1361,11 @@ class KeycloakAdmin: """ if skip_exists: - res = self.get_client_role(client_id=client_role_id, role_name=payload["name"]) - if res: + try: + res = self.get_client_role(client_id=client_role_id, role_name=payload["name"]) return res["name"] + except KeycloakGetError: + pass params_path = {"realm-name": self.realm_name, "id": client_role_id} data_raw = self.raw_post( @@ -1480,9 +1482,11 @@ class KeycloakAdmin: """ if skip_exists: - role = self.get_realm_role(role_name=payload["name"]) - if role is not None: + try: + role = self.get_realm_role(role_name=payload["name"]) return role["name"] + except KeycloakGetError: + pass params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( From b0bc470e33c274ecbe41a774fd37067d146c05ba Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 22:01:49 +0200 Subject: [PATCH 024/222] test: cover the cases with unit tests --- tests/test_keycloak_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 6b04af7..2990621 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -718,7 +718,7 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): assert members == list(), members # Test create realm role - role_id = admin.create_realm_role(payload={"name": "test-realm-role"}) + role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True) assert role_id, role_id with pytest.raises(KeycloakPostError) as err: admin.create_realm_role(payload={"name": "test-realm-role"}) @@ -865,7 +865,7 @@ def test_client_roles(admin: KeycloakAdmin, client: str): # Test create client role client_role_id = admin.create_client_role( - client_role_id=client, payload={"name": "client-role-test"} + client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True ) with pytest.raises(KeycloakPostError) as err: admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"}) From fbbdebb14abf4e8f8a4ac27e48fffd4d3d2c4085 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 22:02:00 +0200 Subject: [PATCH 025/222] docs: fixed the readme examples --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01d6e0f..8704e1d 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ new_user = keycloak_admin.create_user({"email": "example@example.fr", "lastName": "Example", "attributes": { "locale": ["fr"] - }) + }}) # User counter count_users = keycloak_admin.users_count() @@ -226,7 +226,7 @@ role = keycloak_admin.get_client_role(client_id="client_id", role_name="role_nam role_id = keycloak_admin.get_client_role_id(client_id="client_id", role_name="test") # Create client role -keycloak_admin.create_client_role(client_role_id='client_id', {'name': 'roleName', 'clientRole': True}) +keycloak_admin.create_client_role(client_role_id='client_id', payload={'name': 'roleName', 'clientRole': True}) # Assign client role to user. Note that BOTH role_name and role_id appear to be required. keycloak_admin.assign_client_role(client_id="client_id", user_id="user_id", role_id="role_id", role_name="test") From f95a0c055fce0fd20ae52a787dc8239d835ed65e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 19 May 2022 22:15:41 +0200 Subject: [PATCH 026/222] ci: fix tag application --- .github/workflows/publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 9519829..531a8e1 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -21,7 +21,7 @@ jobs: - name: Apply the tag version run: | version=${{ github.ref_name }} - sed -i 's/__version__ = .*/__version__ = "'${version:1}'"/' keycloak/_version.py + sed -i 's/__version__ = .*/__version__ = "'${version:1}'"/' src/keycloak/_version.py - name: Run build run: | tox -e build From 8dafb4ec30b7203f2c918a6b52c3e53f19ec72ab Mon Sep 17 00:00:00 2001 From: Merle Nerger Date: Tue, 12 Apr 2022 11:38:16 +0200 Subject: [PATCH 027/222] feat: added UMA-permission request functionality --- README.md | 9 ++ src/keycloak/exceptions.py | 8 ++ src/keycloak/keycloak_openid.py | 76 +++++++++++++++++ src/keycloak/uma_permissions.py | 145 ++++++++++++++++++++++++++++++++ tests/test_uma_permissions.py | 143 +++++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+) create mode 100644 src/keycloak/uma_permissions.py create mode 100644 tests/test_uma_permissions.py diff --git a/README.md b/README.md index 8704e1d..42a4824 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,15 @@ keycloak_openid.load_authorization_config("example-authz-config.json") policies = keycloak_openid.get_policies(token['access_token'], method_token_info='decode', key=KEYCLOAK_PUBLIC_KEY) permissions = keycloak_openid.get_permissions(token['access_token'], method_token_info='introspect') +# Get UMA-permissions by token +token = keycloak_openid.token("user", "password") +permissions = keycloak_openid.uma_permissions(token['access_token']) + +# Get auth status for a specific resource and scope by token +token = keycloak_openid.token("user", "password") +auth_status = keycloak_openid.has_uma_access(token['access_token'], "Resource#Scope") + + # KEYCLOAK ADMIN from keycloak import KeycloakAdmin diff --git a/src/keycloak/exceptions.py b/src/keycloak/exceptions.py index a9c1b2b..925c937 100644 --- a/src/keycloak/exceptions.py +++ b/src/keycloak/exceptions.py @@ -88,6 +88,14 @@ class KeycloakInvalidTokenError(KeycloakOperationError): pass +class KeycloakPermissionFormatError(KeycloakOperationError): + pass + + +class PermissionDefinitionError(Exception): + pass + + def raise_error_from_response(response, error, expected_codes=None, skip_exists=False): if expected_codes is None: expected_codes = [200, 201, 204] diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 4205b0b..e4378da 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -28,6 +28,7 @@ from jose import jwt from .authorization import Authorization from .connection import ConnectionManager from .exceptions import ( + KeycloakAuthenticationError, KeycloakAuthorizationConfigError, KeycloakDeprecationError, KeycloakGetError, @@ -35,6 +36,7 @@ from .exceptions import ( KeycloakRPTNotFound, raise_error_from_response, ) +from .uma_permissions import AuthStatus, build_permission_param from .urls_patterns import ( URL_AUTH, URL_CERTS, @@ -47,6 +49,8 @@ from .urls_patterns import ( URL_WELL_KNOWN, ) +SAME_AS_CLIENT = object() + class KeycloakOpenID: """ @@ -452,3 +456,75 @@ class KeycloakOpenID: permissions += policy.permissions return list(set(permissions)) + + def uma_permissions( + self, token, permissions, audience=SAME_AS_CLIENT, response_mode="permissions" + ): + """ + The token endpoint is used to retrieve UMA permissions from Keycloak. It can only be + invoked by confidential clients. + + http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint + + + :param token: user token + :param permissions: + :return: permissions list + """ + + if audience is SAME_AS_CLIENT: + audience = self.client_id + + permission = build_permission_param(permissions) + + params_path = {"realm-name": self.realm_name} + payload = { + "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket", + "permission": permission, + "response_mode": response_mode, + "audience": audience, + "Authorization": "Bearer " + token, + } + + self.connection.add_param_headers("Authorization", "Bearer " + token) + try: + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) + finally: + self.connection.del_param_headers("Authorization") + + return raise_error_from_response(data_raw, KeycloakGetError) + + def has_uma_access(self, token, permissions): + """ + Get permission by user token + + :param token: user token + :param permissions: dict(resource:scope) + :return: result bool + """ + needed = build_permission_param(permissions) + try: + granted = self.uma_permissions(token, permissions) + except (KeycloakGetError, KeycloakAuthenticationError) as e: + if e.response_code == 403: + return AuthStatus( + is_logged_in=True, is_authorized=False, missing_permissions=needed + ) + elif e.response_code == 401: + return AuthStatus( + is_logged_in=False, is_authorized=False, missing_permissions=needed + ) + raise + + for resource_struct in granted: + resource = resource_struct["rsname"] + scopes = resource_struct.get("scopes", None) + if not scopes: + needed.discard(resource) + continue + for scope in scopes: + needed.discard("{}#{}".format(resource, scope)) + + return AuthStatus( + is_logged_in=True, is_authorized=len(needed) == 0, missing_permissions=needed + ) diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py new file mode 100644 index 0000000..5d023dc --- /dev/null +++ b/src/keycloak/uma_permissions.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (C) 2017 Marcos Pereira +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError + + +class UMAPermission: + """A class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + + Usage example: + + >>> r = Resource("Users") + >>> s = Scope("delete") + >>> permission = r(s) + >>> print(permission) + 'Users#delete' + + """ + + def __init__(self, *, resource="", scope=""): + self.resource = resource + self.scope = scope + + def __str__(self): + scope = self.scope + if scope: + scope = "#" + scope + return "{}{}".format(self.resource, scope) + + def __eq__(self, __o: object) -> bool: + return str(self) == str(__o) + + def __repr__(self) -> str: + return self.__str__() + + def __hash__(self) -> int: + return hash(str(self)) + + def __call__(self, *args, resource="", scope="") -> object: + result_resource = self.resource + result_scope = self.scope + + for arg in args: + if not isinstance(arg, UMAPermission): + raise PermissionDefinitionError( + "can't determine if '{}' is a resource or scope".format(arg) + ) + if arg.resource: + result_resource = str(arg.resource) + if arg.scope: + result_scope = str(arg.scope) + + if resource: + result_resource = str(resource) + if scope: + result_scope = str(scope) + + return UMAPermission(resource=result_resource, scope=result_scope) + + +class Resource(UMAPermission): + def __init__(self, resource): + super().__init__(resource=resource) + + +class Scope(UMAPermission): + def __init__(self, scope): + super().__init__(scope=scope) + + +class AuthStatus: + """A class that represents the authorization/login status of a user associated with a token. + This has to evaluate to True if and only if the user is properly authorized for the requested resource.""" + + def __init__(self, is_logged_in, is_authorized, missing_permissions): + self.is_logged_in = is_logged_in + self.is_authorized = is_authorized + self.missing_permissions = missing_permissions + + def __bool__(self): + return self.is_authorized + + def __repr__(self): + return f"AuthStatus(is_authorized={self.is_authorized}, is_logged_in={self.is_logged_in}, missing_permissions={self.missing_permissions})" + + +def build_permission_param(permissions): + """ + Transform permissions to a set, so they are usable for requests + + :param permissions: either str (resource#scope), + iterable[str] (resource#scope), + dict[str,str] (resource: scope), + dict[str,iterable[str]] (resource: scopes) + :return: result bool + """ + if permissions is None or permissions == "": + return set() + if isinstance(permissions, (str, UMAPermission)): + return set((permissions,)) + + try: # treat as dictionary of permissions + result = set() + for resource, scopes in permissions.items(): + if scopes is None: + result.add(resource) + elif isinstance(scopes, (str, UMAPermission)): + result.add("{}#{}".format(resource, scopes)) + else: + for scope in scopes: + if not isinstance(scope, (str, UMAPermission)): + raise KeycloakPermissionFormatError( + "misbuilt permission {}".format(permissions) + ) + result.add("{}#{}".format(resource, scope)) + return result + except AttributeError: + pass + + try: # treat as any other iterable of permissions + return set(permissions) + except TypeError: + pass + raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py new file mode 100644 index 0000000..d26830b --- /dev/null +++ b/tests/test_uma_permissions.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +import pytest +import re +from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError +from keycloak.uma_permissions import build_permission_param, Resource, Scope + + + +def test_resource_with_scope_obj(): + r = Resource("Resource1") + s = Scope("Scope1") + assert r(s) == "Resource1#Scope1" + + +def test_scope_with_resource_obj(): + r = Resource("Resource1") + s = Scope("Scope1") + assert s(r) == "Resource1#Scope1" + + +def test_resource_scope_str(): + r = Resource("Resource1") + s = "Scope1" + assert r(scope=s) == "Resource1#Scope1" + + +def test_scope_resource_str(): + r = "Resource1" + s = Scope("Scope1") + assert s(resource=r) == "Resource1#Scope1" + + +def test_resource_scope_dict(): + r = Resource("Resource1") + s = {"scope": "Scope1"} + assert r(**s) == "Resource1#Scope1" + + +def test_scope_resource_dict(): + r = {"resource": "Resource1"} + s = Scope("Scope1") + assert s(**r) == "Resource1#Scope1" + + +def test_resource_scope_list(): + r = Resource("Resource1") + s = ["Scope1"] + with pytest.raises(PermissionDefinitionError) as err: + r(*s) + assert err.match("can't determine if 'Scope1' is a resource or scope") + + +def test_build_permission_none(): + assert build_permission_param(None) == set() + + +def test_build_permission_empty_str(): + assert build_permission_param("") == set() + + +def test_build_permission_empty_list(): + assert build_permission_param([]) == set() + + +def test_build_permission_empty_tuple(): + assert build_permission_param(()) == set() + + +def test_build_permission_empty_set(): + assert build_permission_param(set()) == set() + + +def test_build_permission_empty_dict(): + assert build_permission_param({}) == set() + + +def test_build_permission_str(): + assert build_permission_param("resource1") == {"resource1"} + + +def test_build_permission_list_str(): + assert build_permission_param(["res1#scope1", "res1#scope2"]) == {"res1#scope1", "res1#scope2"} + + +def test_build_permission_tuple_str(): + assert build_permission_param(("res1#scope1", "res1#scope2")) == {"res1#scope1", "res1#scope2"} + + +def test_build_permission_set_str(): + assert build_permission_param({"res1#scope1", "res1#scope2"}) == {"res1#scope1", "res1#scope2"} + + +def test_build_permission_tuple_dict_str_str(): + assert build_permission_param({"res1": "scope1"}) == {"res1#scope1"} + + +def test_build_permission_tuple_dict_str_list_str(): + assert build_permission_param({"res1": ["scope1", "scope2"]}) == {"res1#scope1", "res1#scope2"} + + +def test_build_permission_tuple_dict_str_list_str2(): + assert build_permission_param( + {"res1": ["scope1", "scope2"], "res2": ["scope2", "scope3"]} + ) == {"res1#scope1", "res1#scope2", "res2#scope2", "res2#scope3"} + + +def test_build_permission_misbuilt_dict_str_list_list_str(): + with pytest.raises(KeycloakPermissionFormatError) as err: + build_permission_param({"res1": [["scope1", "scope2"]]}) + assert err.match(re.escape("misbuilt permission {'res1': [['scope1', 'scope2']]}")) + + +def test_build_permission_misbuilt_list_list_str(): + with pytest.raises(KeycloakPermissionFormatError) as err: + build_permission_param([["scope1", "scope2"]]) + assert err.match(re.escape("misbuilt permission [['scope1', 'scope2']]")) + + +def test_build_permission_misbuilt_list_set_str(): + with pytest.raises(KeycloakPermissionFormatError) as err: + build_permission_param([{"scope1", "scope2"}]) + assert err.match(re.escape("misbuilt permission [{'scope1', 'scope2'}]")) + + +def test_build_permission_misbuilt_set_set_str(): + with pytest.raises(KeycloakPermissionFormatError) as err: + build_permission_param([{"scope1"}]) + assert err.match(re.escape("misbuilt permission [{'scope1'}]")) From 62e8d667798313a03a811b3d8fb36680de7a247c Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Mon, 23 May 2022 20:39:42 +0200 Subject: [PATCH 028/222] fix: import classes in the base module --- src/keycloak/__init__.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/keycloak/__init__.py b/src/keycloak/__init__.py index 62c47a8..2c7f70f 100644 --- a/src/keycloak/__init__.py +++ b/src/keycloak/__init__.py @@ -22,7 +22,41 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from ._version import __version__ +from .connection import ConnectionManager +from .exceptions import ( + KeycloakAuthenticationError, + KeycloakAuthorizationConfigError, + KeycloakConnectionError, + KeycloakDeleteError, + KeycloakDeprecationError, + KeycloakError, + KeycloakGetError, + KeycloakInvalidTokenError, + KeycloakOperationError, + KeycloakPostError, + KeycloakPutError, + KeycloakRPTNotFound, + KeycloakSecretNotFound, +) from .keycloak_admin import KeycloakAdmin from .keycloak_openid import KeycloakOpenID -__all__ = ["KeycloakAdmin", "KeycloakOpenID", "__version__"] +__all__ = [ + "__version__", + "ConnectionManager", + "KeycloakAuthenticationError", + "KeycloakAuthorizationConfigError", + "KeycloakConnectionError", + "KeycloakDeleteError", + "KeycloakDeprecationError", + "KeycloakError", + "KeycloakGetError", + "KeycloakInvalidTokenError", + "KeycloakOperationError", + "KeycloakPostError", + "KeycloakPutError", + "KeycloakRPTNotFound", + "KeycloakSecretNotFound", + "KeycloakAdmin", + "KeycloakOpenID", +] From 8c3b1b62ca4a2feca1a57de461de8a551927f87d Mon Sep 17 00:00:00 2001 From: Jackson Kwok Date: Mon, 23 May 2022 15:14:56 -0400 Subject: [PATCH 029/222] fix: added fixes based on feedback --- README.md | 4 ++ src/keycloak/keycloak_openid.py | 35 ++++++-------- src/keycloak/uma_permissions.py | 83 ++++++++++++++++++++++++--------- tests/test_uma_permissions.py | 43 +++++++++-------- 4 files changed, 101 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 42a4824..692ce48 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,10 @@ permissions = keycloak_openid.get_permissions(token['access_token'], method_toke token = keycloak_openid.token("user", "password") permissions = keycloak_openid.uma_permissions(token['access_token']) +# Get UMA-permissions by token with specific resource and scope requested +token = keycloak_openid.token("user", "password") +permissions = keycloak_openid.uma_permissions(token['access_token'], permissions="Resource#Scope") + # Get auth status for a specific resource and scope by token token = keycloak_openid.token("user", "password") auth_status = keycloak_openid.has_uma_access(token['access_token'], "Resource#Scope") diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index e4378da..f0b73a6 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -33,6 +33,7 @@ from .exceptions import ( KeycloakDeprecationError, KeycloakGetError, KeycloakInvalidTokenError, + KeycloakPostError, KeycloakRPTNotFound, raise_error_from_response, ) @@ -49,8 +50,6 @@ from .urls_patterns import ( URL_WELL_KNOWN, ) -SAME_AS_CLIENT = object() - class KeycloakOpenID: """ @@ -457,55 +456,47 @@ class KeycloakOpenID: return list(set(permissions)) - def uma_permissions( - self, token, permissions, audience=SAME_AS_CLIENT, response_mode="permissions" - ): + def uma_permissions(self, token, permissions=""): """ + Get UMA permissions by user token with requested permissions + The token endpoint is used to retrieve UMA permissions from Keycloak. It can only be invoked by confidential clients. http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - :param token: user token - :param permissions: + :param permissions: list of uma permissions list(resource:scope) requested by the user :return: permissions list """ - if audience is SAME_AS_CLIENT: - audience = self.client_id - permission = build_permission_param(permissions) params_path = {"realm-name": self.realm_name} payload = { "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket", "permission": permission, - "response_mode": response_mode, - "audience": audience, - "Authorization": "Bearer " + token, + "response_mode": "permissions", + "audience": self.client_id, } self.connection.add_param_headers("Authorization", "Bearer " + token) - try: - data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - finally: - self.connection.del_param_headers("Authorization") + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def has_uma_access(self, token, permissions): """ - Get permission by user token + Determine whether user has uma permissions with specified user token :param token: user token - :param permissions: dict(resource:scope) - :return: result bool + :param permissions: list of uma permissions (resource:scope) + :return: auth status """ needed = build_permission_param(permissions) try: granted = self.uma_permissions(token, permissions) - except (KeycloakGetError, KeycloakAuthenticationError) as e: + except (KeycloakPostError, KeycloakAuthenticationError) as e: if e.response_code == 403: return AuthStatus( is_logged_in=True, is_authorized=False, missing_permissions=needed diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 5d023dc..5653c76 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -38,10 +38,20 @@ class UMAPermission: """ - def __init__(self, *, resource="", scope=""): + def __init__(self, permission=None, resource="", scope=""): self.resource = resource self.scope = scope + if permission: + if not isinstance(permission, UMAPermission): + raise PermissionDefinitionError( + "can't determine if '{}' is a resource or scope".format(permission) + ) + if permission.resource: + self.resource = str(permission.resource) + if permission.scope: + self.scope = str(permission.scope) + def __str__(self): scope = self.scope if scope: @@ -57,41 +67,50 @@ class UMAPermission: def __hash__(self) -> int: return hash(str(self)) - def __call__(self, *args, resource="", scope="") -> object: + def __call__(self, permission=None, resource="", scope="") -> object: result_resource = self.resource result_scope = self.scope - for arg in args: - if not isinstance(arg, UMAPermission): - raise PermissionDefinitionError( - "can't determine if '{}' is a resource or scope".format(arg) - ) - if arg.resource: - result_resource = str(arg.resource) - if arg.scope: - result_scope = str(arg.scope) - if resource: result_resource = str(resource) if scope: result_scope = str(scope) + if permission: + if not isinstance(permission, UMAPermission): + raise PermissionDefinitionError( + "can't determine if '{}' is a resource or scope".format(permission) + ) + if permission.resource: + result_resource = str(permission.resource) + if permission.scope: + result_scope = str(permission.scope) + return UMAPermission(resource=result_resource, scope=result_scope) class Resource(UMAPermission): + """An UMAPermission Resource class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + """ + def __init__(self, resource): super().__init__(resource=resource) class Scope(UMAPermission): + """An UMAPermission Scope class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + """ + def __init__(self, scope): super().__init__(scope=scope) class AuthStatus: """A class that represents the authorization/login status of a user associated with a token. - This has to evaluate to True if and only if the user is properly authorized for the requested resource.""" + This has to evaluate to True if and only if the user is properly authorized + for the requested resource.""" def __init__(self, is_logged_in, is_authorized, missing_permissions): self.is_logged_in = is_logged_in @@ -102,7 +121,12 @@ class AuthStatus: return self.is_authorized def __repr__(self): - return f"AuthStatus(is_authorized={self.is_authorized}, is_logged_in={self.is_logged_in}, missing_permissions={self.missing_permissions})" + return ( + f"AuthStatus(" + f"is_authorized={self.is_authorized}, " + f"is_logged_in={self.is_logged_in}, " + f"missing_permissions={self.missing_permissions})" + ) def build_permission_param(permissions): @@ -117,29 +141,42 @@ def build_permission_param(permissions): """ if permissions is None or permissions == "": return set() - if isinstance(permissions, (str, UMAPermission)): + if isinstance(permissions, str): return set((permissions,)) + if isinstance(permissions, UMAPermission): + return set((str(permissions),)) try: # treat as dictionary of permissions result = set() for resource, scopes in permissions.items(): + print(f"resource={resource}scopes={scopes}") if scopes is None: result.add(resource) - elif isinstance(scopes, (str, UMAPermission)): + elif isinstance(scopes, str): result.add("{}#{}".format(resource, scopes)) else: - for scope in scopes: - if not isinstance(scope, (str, UMAPermission)): - raise KeycloakPermissionFormatError( - "misbuilt permission {}".format(permissions) - ) - result.add("{}#{}".format(resource, scope)) + try: + for scope in scopes: + if not isinstance(scope, str): + raise KeycloakPermissionFormatError( + "misbuilt permission {}".format(permissions) + ) + result.add("{}#{}".format(resource, scope)) + except TypeError: + raise KeycloakPermissionFormatError( + "misbuilt permission {}".format(permissions) + ) return result except AttributeError: pass try: # treat as any other iterable of permissions - return set(permissions) + result = set() + for permission in permissions: + if not isinstance(permission, (str, UMAPermission)): + raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) + result.add(str(permission)) + return result except TypeError: pass raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py index d26830b..09d7147 100644 --- a/tests/test_uma_permissions.py +++ b/tests/test_uma_permissions.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import pytest import re -from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError -from keycloak.uma_permissions import build_permission_param, Resource, Scope +import pytest + +from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError +from keycloak.uma_permissions import Resource, Scope, build_permission_param def test_resource_with_scope_obj(): @@ -45,24 +46,12 @@ def test_scope_resource_str(): assert s(resource=r) == "Resource1#Scope1" -def test_resource_scope_dict(): - r = Resource("Resource1") - s = {"scope": "Scope1"} - assert r(**s) == "Resource1#Scope1" - - -def test_scope_resource_dict(): - r = {"resource": "Resource1"} - s = Scope("Scope1") - assert s(**r) == "Resource1#Scope1" - - def test_resource_scope_list(): r = Resource("Resource1") s = ["Scope1"] with pytest.raises(PermissionDefinitionError) as err: - r(*s) - assert err.match("can't determine if 'Scope1' is a resource or scope") + r(s) + assert err.match(re.escape("can't determine if '['Scope1']' is a resource or scope")) def test_build_permission_none(): @@ -119,6 +108,16 @@ def test_build_permission_tuple_dict_str_list_str2(): ) == {"res1#scope1", "res1#scope2", "res2#scope2", "res2#scope3"} +def test_build_permission_uma(): + assert build_permission_param(Resource("res1")(Scope("scope1"))) == {"res1#scope1"} + + +def test_build_permission_uma_list(): + assert build_permission_param( + [Resource("res1")(Scope("scope1")), Resource("res1")(Scope("scope2"))] + ) == {"res1#scope1", "res1#scope2"} + + def test_build_permission_misbuilt_dict_str_list_list_str(): with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": [["scope1", "scope2"]]}) @@ -127,17 +126,23 @@ def test_build_permission_misbuilt_dict_str_list_list_str(): def test_build_permission_misbuilt_list_list_str(): with pytest.raises(KeycloakPermissionFormatError) as err: - build_permission_param([["scope1", "scope2"]]) + print(build_permission_param([["scope1", "scope2"]])) assert err.match(re.escape("misbuilt permission [['scope1', 'scope2']]")) def test_build_permission_misbuilt_list_set_str(): with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1", "scope2"}]) - assert err.match(re.escape("misbuilt permission [{'scope1', 'scope2'}]")) + assert err.match("misbuilt permission.*") def test_build_permission_misbuilt_set_set_str(): with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1"}]) assert err.match(re.escape("misbuilt permission [{'scope1'}]")) + + +def test_build_permission_misbuilt_dict_non_iterable(): + with pytest.raises(KeycloakPermissionFormatError) as err: + build_permission_param({"res1": 5}) + assert err.match(re.escape("misbuilt permission {'res1': 5}")) From fe160531f478178d159de6daec63966219433438 Mon Sep 17 00:00:00 2001 From: Jackson Kwok Date: Tue, 24 May 2022 16:22:08 -0400 Subject: [PATCH 030/222] fix: allow client_credentials token if username and password not specified --- src/keycloak/keycloak_admin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ffc3cde..b3336e3 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2566,13 +2566,15 @@ class KeycloakAdmin: custom_headers=self.custom_headers, ) - grant_type = ["password"] + grant_type = [] if self.client_secret_key: - grant_type = ["client_credentials"] if self.user_realm_name: self.realm_name = self.user_realm_name + grant_type.append("client_credentials") + elif self.username and self.password: + grant_type.append("password") - if self.username and self.password: + if grant_type: self.token = self.keycloak_openid.token( self.username, self.password, grant_type=grant_type, totp=self.totp ) From 6bfbd0d15fa5981f35e5a6866b3efd62ef0dc968 Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Wed, 25 May 2022 08:32:57 +0200 Subject: [PATCH 031/222] fix: correct spelling of public API method BREAKING CHANGE: Renames `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` --- CHANGELOG.md | 4 ++++ README.md | 2 +- src/keycloak/keycloak_openid.py | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4492c3d..72b757b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,3 +42,7 @@ All notable changes to this project will be documented in this file. - Add get_idps - Rework group functions + + ## [master] + + * Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` diff --git a/README.md b/README.md index 692ce48..85e3d34 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", client_secret_key="secret") # Get WellKnow -config_well_know = keycloak_openid.well_know() +config_well_known = keycloak_openid.well_known() # Get Token token = keycloak_openid.token("user", "password") diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index f0b73a6..e73e963 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -159,7 +159,7 @@ class KeycloakOpenID: return token_info - def well_know(self): + def well_known(self): """The most important endpoint to understand is the well-known configuration endpoint. It lists endpoints and other configuration options relevant to the OpenID Connect implementation in Keycloak. @@ -180,7 +180,7 @@ class KeycloakOpenID: :return: """ params_path = { - "authorization-endpoint": self.well_know()["authorization_endpoint"], + "authorization-endpoint": self.well_known()["authorization_endpoint"], "client-id": self.client_id, "redirect-uri": redirect_uri, } From 5560799191e3cbcb0a7ec3b549cc1e5e388a7ef5 Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Wed, 25 May 2022 08:36:19 +0200 Subject: [PATCH 032/222] chore: add missing newline --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 24f085b..0c17079 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,4 @@ main.py main2.py s3air-authz-config.json .vscode -_build \ No newline at end of file +_build From d9c3326fd1f40c8e453762bf6279f320fffb9304 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 25 May 2022 20:16:18 +0200 Subject: [PATCH 033/222] fix: allow query parameters for users count --- src/keycloak/keycloak_admin.py | 9 +++++++-- tests/test_keycloak_admin.py | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ffc3cde..766159a 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -464,14 +464,19 @@ class KeycloakAdmin: _last_slash_idx = data_raw.headers["Location"].rindex("/") return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 - def users_count(self): + def users_count(self, query=None): """ User counter + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource + + :param query: (dict) Query parameters for users count + :return: counter """ + query = query or dict() params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path)) + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path), **query) return raise_error_from_response(data_raw, KeycloakGetError) def get_user_id(self, username): diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 2990621..b0f1948 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -184,6 +184,10 @@ def test_users(admin: KeycloakAdmin, realm: str): count = admin.users_count() assert count == 1, count + # Test users count with query + count = admin.users_count(query={"username": "notpresent"}) + assert count == 0 + # Test user groups groups = admin.get_user_groups(user_id=user["id"]) assert len(groups) == 0 From 1029e46a68c5013ae160f69d58f3ae69e371a42f Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 26 May 2022 21:56:55 +0200 Subject: [PATCH 034/222] feat: added new methods for client scopes --- src/keycloak/keycloak_admin.py | 62 +++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 025d9e8..fba28c2 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2108,6 +2108,22 @@ class KeycloakAdmin: data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + def get_client_scope_by_name(self, client_scope_name): + """ + Get representation of the client scope identified by the client scope name. + + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes + :param client_scope_name: (str) Name of the client scope + :returns: ClientScopeRepresentation or None + """ + + client_scopes = self.get_client_scopes() + for client_scope in client_scopes: + if client_scope["name"] == client_scope_name: + return client_scope + + return None + def create_client_scope(self, payload, skip_exists=False): """ Create a client scope @@ -2117,16 +2133,24 @@ class KeycloakAdmin: :param payload: ClientScopeRepresentation :param skip_exists: If true then do not raise an error if client scope already exists - :return: Keycloak server response (ClientScopeRepresentation) + :return: Client scope id """ + if skip_exists: + exists = self.get_client_scope_by_name(client_scope_name=payload["name"]) + + if exists is not None: + return exists["id"] + params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response( + raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client_scope(self, client_scope_id, payload): """ @@ -2146,6 +2170,34 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def delete_client_scope(self, client_scope_id): + """ + Delete existing client scope. + + ClientScopeRepresentation: + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource + + :param client_scope_id: The id of the client scope + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) + + def get_mappers_from_client_scope(self, client_scope_id): + """ + Get a list of all mappers connected to the client scope + + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource + :param client_scope_id: Client scope id + :returns: Keycloak server response (ProtocolMapperRepresentation) + """ + params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) + def add_mapper_to_client_scope(self, client_scope_id, payload): """ Add a mapper to a client scope @@ -2165,20 +2217,20 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) - def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id): + def delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id): """ Delete a mapper from a client scope https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope - :param payload: ProtocolMapperRepresentation + :param protocol_mapper_id: Protocol mapper id :return: Keycloak server Response """ params_path = { "realm-name": self.realm_name, "scope-id": client_scope_id, - "protocol-mapper-id": protocol_mppaer_id, + "protocol-mapper-id": protocol_mapper_id, } data_raw = self.raw_delete( From 2e5d75198557b4c6658d1e2ad7e5c23970d9f0d6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 26 May 2022 21:57:08 +0200 Subject: [PATCH 035/222] test: added tests for client scopes --- tests/test_keycloak_admin.py | 139 +++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index b0f1948..58d298f 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1243,3 +1243,142 @@ def test_sync_users(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakPostError) as err: admin.sync_users(storage_id="does-not-exist", action="triggerFullSync") assert err.match('404: b\'{"error":"Could not find component"}\'') + + +def test_client_scopes(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Test get client scopes + res = admin.get_client_scopes() + scope_names = {x["name"] for x in res} + assert len(res) == 10 + assert "email" in scope_names + assert "profile" in scope_names + assert "offline_access" in scope_names + + with pytest.raises(KeycloakGetError) as err: + admin.get_client_scope(client_scope_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client scope"}\'') + + scope = admin.get_client_scope(client_scope_id=res[0]["id"]) + assert res[0] == scope + + scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"]) + assert res[0] == scope + + # Test create client scope + res = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True) + assert res + res2 = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True) + assert res == res2 + with pytest.raises(KeycloakPostError) as err: + admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=False) + assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'') + + # Test update client scope + with pytest.raises(KeycloakPutError) as err: + admin.update_client_scope(client_scope_id="does-not-exist", payload=dict()) + assert err.match('404: b\'{"error":"Could not find client scope"}\'') + + res_update = admin.update_client_scope( + client_scope_id=res, payload={"name": "test-scope-update"} + ) + assert res_update == dict() + admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update" + + # Test get mappers + mappers = admin.get_mappers_from_client_scope(client_scope_id=res) + assert mappers == list() + + # Test add mapper + with pytest.raises(KeycloakPostError) as err: + admin.add_mapper_to_client_scope(client_scope_id=res, payload=dict()) + assert err.match('404: b\'{"error":"ProtocolMapper provider not found"}\'') + + res_add = admin.add_mapper_to_client_scope( + client_scope_id=res, + payload={ + "name": "test-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + }, + ) + assert res_add == b"" + assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1 + + # Test update mapper + test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0] + with pytest.raises(KeycloakPutError) as err: + admin.update_mapper_in_client_scope( + client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict() + ) + assert err.match('404: b\'{"error":"Could not find client scope"}\'') + test_mapper["config"]["user.attribute"] = "test" + res_update = admin.update_mapper_in_client_scope( + client_scope_id=res, + protocol_mapper_id=test_mapper["id"], + payload=test_mapper, + ) + assert res_update == dict() + assert ( + admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"] + == "test" + ) + + # Test delete mapper + res_del = admin.delete_mapper_from_client_scope( + client_scope_id=res, protocol_mapper_id=test_mapper["id"] + ) + assert res_del == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_mapper_from_client_scope( + client_scope_id=res, protocol_mapper_id=test_mapper["id"] + ) + assert err.match('404: b\'{"error":"Model not found"}\'') + + # Test default default scopes + res_defaults = admin.get_default_default_client_scopes() + assert len(res_defaults) == 6 + + with pytest.raises(KeycloakPutError) as err: + admin.add_default_default_client_scope(scope_id="does-not-exist") + assert err.match('404: b\'{"error":"Client scope not found"}\'') + + res_add = admin.add_default_default_client_scope(scope_id=res) + assert res_add == dict() + assert len(admin.get_default_default_client_scopes()) == 7 + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_default_default_client_scope(scope_id="does-not-exist") + assert err.match('404: b\'{"error":"Client scope not found"}\'') + + res_del = admin.delete_default_default_client_scope(scope_id=res) + assert res_del == dict() + assert len(admin.get_default_default_client_scopes()) == 6 + + # Test default optional scopes + res_defaults = admin.get_default_optional_client_scopes() + assert len(res_defaults) == 4 + + with pytest.raises(KeycloakPutError) as err: + admin.add_default_optional_client_scope(scope_id="does-not-exist") + assert err.match('404: b\'{"error":"Client scope not found"}\'') + + res_add = admin.add_default_optional_client_scope(scope_id=res) + assert res_add == dict() + assert len(admin.get_default_optional_client_scopes()) == 5 + + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_default_optional_client_scope(scope_id="does-not-exist") + assert err.match('404: b\'{"error":"Client scope not found"}\'') + + res_del = admin.delete_default_optional_client_scope(scope_id=res) + assert res_del == dict() + assert len(admin.get_default_optional_client_scopes()) == 4 + + # Test client scope delete + res_del = admin.delete_client_scope(client_scope_id=res) + assert res_del == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client_scope(client_scope_id=res) + assert err.match('404: b\'{"error":"Could not find client scope"}\'') From 8c8c0e81410977c34bde767d167430ff2bb010ca Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 08:56:11 +0200 Subject: [PATCH 036/222] fix: use param for update client mapper --- src/keycloak/keycloak_admin.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index fba28c2..c9712e0 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2343,6 +2343,23 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def get_mappers_from_client(self, client_id): + """ + List of all client mappers. + + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocolmapperrepresentation + + :param client_id: Client id + :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation) + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path) + ) + + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200]) + def add_mapper_to_client(self, client_id, payload): """ Add a mapper to a client @@ -2373,7 +2390,7 @@ class KeycloakAdmin: params_path = { "realm-name": self.realm_name, - "id": self.client_id, + "id": client_id, "protocol-mapper-id": mapper_id, } From dc1c4be115549a026abd562f0019b57483fc0824 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 08:56:30 +0200 Subject: [PATCH 037/222] test: added missing tests for clients --- tests/test_keycloak_admin.py | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 58d298f..c58de87 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -573,6 +573,39 @@ def test_clients(admin: KeycloakAdmin, realm: str): admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"}) assert err.match('404: b\'{"error":"Could not find client"}\'') + # Test client mappers + res = admin.get_mappers_from_client(client_id=client_id) + assert len(res) == 0 + + with pytest.raises(KeycloakPostError) as err: + admin.add_mapper_to_client(client_id="does-not-exist", payload=dict()) + assert err.match('404: b\'{"error":"Could not find client"}\'') + + res = admin.add_mapper_to_client( + client_id=client_id, + payload={ + "name": "test-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + }, + ) + assert res == b"" + assert len(admin.get_mappers_from_client(client_id=client_id)) == 1 + + mapper = admin.get_mappers_from_client(client_id=client_id)[0] + with pytest.raises(KeycloakPutError) as err: + admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload=dict()) + assert err.match('404: b\'{"error":"Model not found"}\'') + mapper["config"]["user.attribute"] = "test" + res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper) + assert res == dict() + + res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"]) + assert res == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"]) + assert err.match('404: b\'{"error":"Model not found"}\'') + # Test authz auth_client_id = admin.create_client( payload={ @@ -703,6 +736,42 @@ def test_clients(admin: KeycloakAdmin, realm: str): admin.delete_client(client_id=auth_client_id) assert err.match('404: b\'{"error":"Could not find client"}\'') + # Test client credentials + admin.create_client( + payload={ + "name": "test-confidential", + "enabled": True, + "protocol": "openid-connect", + "publicClient": False, + "redirectUris": ["http://localhost/*"], + "webOrigins": ["+"], + "clientId": "test-confidential", + "secret": "test-secret", + "clientAuthenticatorType": "client-secret", + } + ) + with pytest.raises(KeycloakGetError) as err: + admin.get_client_secrets(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + secrets = admin.get_client_secrets( + client_id=admin.get_client_id(client_name="test-confidential") + ) + assert secrets == {"type": "secret", "value": "test-secret"} + + with pytest.raises(KeycloakPostError) as err: + admin.generate_client_secrets(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + res = admin.generate_client_secrets( + client_id=admin.get_client_id(client_name="test-confidential") + ) + assert res + assert ( + admin.get_client_secrets(client_id=admin.get_client_id(client_name="test-confidential")) + == res + ) + def test_realm_roles(admin: KeycloakAdmin, realm: str): admin.realm_name = realm From e56889e5dbbe9208a1ea76d5bb53ee4b039ee8f6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 14:12:05 +0200 Subject: [PATCH 038/222] fix: fixed components bugs --- src/keycloak/keycloak_admin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index c9712e0..ab0dad6 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2461,6 +2461,7 @@ class KeycloakAdmin: :param query: Query parameters (optional) :return: components list """ + query = query or dict() params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query @@ -2475,15 +2476,15 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param payload: ComponentRepresentation - - :return: UserRepresentation + :return: Component id """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_post( urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload) ) - return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + _last_slash_idx = data_raw.headers["Location"].rindex("/") + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_component(self, component_id): """ From 73ff9785a3559d87e8018d446748f2244675874e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 14:12:18 +0200 Subject: [PATCH 039/222] test: added components tests --- tests/test_keycloak_admin.py | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index c58de87..1c91d94 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1451,3 +1451,53 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakDeleteError) as err: admin.delete_client_scope(client_scope_id=res) assert err.match('404: b\'{"error":"Could not find client scope"}\'') + + +def test_components(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + # Test get components + res = admin.get_components() + assert len(res) == 12 + + with pytest.raises(KeycloakGetError) as err: + admin.get_component(component_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find component"}\'') + + res_get = admin.get_component(component_id=res[0]["id"]) + assert res_get == res[0] + + # Test create component + with pytest.raises(KeycloakPostError) as err: + admin.create_component(payload={"bad": "dict"}) + assert err.match('400: b\'{"error":"Unrecognized field') + + res = admin.create_component( + payload={ + "name": "Test Component", + "providerId": "max-clients", + "providerType": "org.keycloak.services.clientregistration." + + "policy.ClientRegistrationPolicy", + "config": {"max-clients": ["1000"]}, + } + ) + assert res + assert admin.get_component(component_id=res)["name"] == "Test Component" + + # Test update component + component = admin.get_component(component_id=res) + component["name"] = "Test Component Update" + + with pytest.raises(KeycloakPutError) as err: + admin.update_component(component_id="does-not-exist", payload=dict()) + assert err.match('404: b\'{"error":"Could not find component"}\'') + res_upd = admin.update_component(component_id=res, payload=component) + assert res_upd == dict() + assert admin.get_component(component_id=res)["name"] == "Test Component Update" + + # Test delete component + res_del = admin.delete_component(component_id=res) + assert res_del == dict() + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_component(component_id=res) + assert err.match('404: b\'{"error":"Could not find component"}\'') From e95649a93ce7c058ca60b38f41fa65ff441c55ef Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 22:10:12 +0200 Subject: [PATCH 040/222] fix: fixed bugs in events methods --- src/keycloak/keycloak_admin.py | 3 ++- src/keycloak/urls_patterns.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ab0dad6..d754cd4 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2551,6 +2551,7 @@ class KeycloakAdmin: :return: events list """ + query = query or dict() params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( urls_patterns.URL_ADMIN_EVENTS.format(**params_path), data=None, **query @@ -2568,7 +2569,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_put( - urls_patterns.URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload) + urls_patterns.URL_ADMIN_EVENTS_CONFIG.format(**params_path), data=json.dumps(payload) ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 071c733..d836ed4 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -166,4 +166,5 @@ URL_ADMIN_USER_FEDERATED_IDENTITY = ( ) URL_ADMIN_EVENTS = "admin/realms/{realm-name}/events" +URL_ADMIN_EVENTS_CONFIG = URL_ADMIN_EVENTS + "/config" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" From 02625a8af479168effb537f4b55b0bab8242d04b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 27 May 2022 22:10:33 +0200 Subject: [PATCH 041/222] test: completed admin unit tests --- tests/test_keycloak_admin.py | 175 +++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1c91d94..d927aae 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -4,6 +4,7 @@ import keycloak from keycloak import KeycloakAdmin from keycloak.connection import ConnectionManager from keycloak.exceptions import ( + KeycloakAuthenticationError, KeycloakDeleteError, KeycloakGetError, KeycloakPostError, @@ -54,6 +55,59 @@ def test_keycloak_admin_init(env): assert admin.auto_refresh_token == list(), admin.auto_refresh_token assert admin.user_realm_name is None, admin.user_realm_name assert admin.custom_headers is None, admin.custom_headers + assert admin.token + + admin = KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + realm_name=None, + user_realm_name="master", + ) + assert admin.token + admin = KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=env.KEYCLOAK_ADMIN, + password=env.KEYCLOAK_ADMIN_PASSWORD, + realm_name=None, + user_realm_name=None, + ) + assert admin.token + + admin.create_realm(payload={"realm": "authz", "enabled": True}) + admin.realm_name = "authz" + admin.create_client( + payload={ + "name": "authz-client", + "clientId": "authz-client", + "authorizationServicesEnabled": True, + "serviceAccountsEnabled": True, + "clientAuthenticatorType": "client-secret", + "directAccessGrantsEnabled": False, + "enabled": True, + "implicitFlowEnabled": False, + "publicClient": False, + } + ) + secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client")) + assert KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + user_realm_name="authz", + client_id="authz-client", + client_secret_key=secret["value"], + ).token + admin.delete_realm(realm_name="authz") + + assert ( + KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + username=None, + password=None, + client_secret_key=None, + custom_headers={"custom": "header"}, + ).token + is None + ) def test_realms(admin: KeycloakAdmin): @@ -606,6 +660,14 @@ def test_clients(admin: KeycloakAdmin, realm: str): admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"]) assert err.match('404: b\'{"error":"Model not found"}\'') + # Test client sessions + with pytest.raises(KeycloakGetError) as err: + admin.get_client_all_sessions(client_id="does-not-exist") + assert err.match('404: b\'{"error":"Could not find client"}\'') + + assert admin.get_client_all_sessions(client_id=client_id) == list() + assert admin.get_client_sessions_stats() == list() + # Test authz auth_client_id = admin.create_client( payload={ @@ -1501,3 +1563,116 @@ def test_components(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakDeleteError) as err: admin.delete_component(component_id=res) assert err.match('404: b\'{"error":"Could not find component"}\'') + + +def test_keys(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} + assert {k["algorithm"] for k in admin.get_keys()["keys"]} == { + "HS256", + "RSA-OAEP", + "AES", + "RS256", + } + + +def test_events(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + + events = admin.get_events() + assert events == list() + + with pytest.raises(KeycloakPutError) as err: + admin.set_events(payload={"bad": "conf"}) + assert err.match('400: b\'{"error":"Unrecognized field') + + res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True}) + assert res == dict() + + admin.create_client(payload={"name": "test", "clientId": "test"}) + + events = admin.get_events() + assert events == list() + + +def test_auto_refresh(admin: KeycloakAdmin, realm: str): + # Test get refresh + admin.auto_refresh_token = list() + admin.connection = ConnectionManager( + base_url=admin.server_url, + headers={"Authorization": "Bearer bad", "Content-Type": "application/json"}, + timeout=60, + verify=admin.verify, + ) + + with pytest.raises(KeycloakAuthenticationError) as err: + admin.get_realm(realm_name=realm) + assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'') + + admin.auto_refresh_token = ["get"] + del admin.token["refresh_token"] + assert admin.get_realm(realm_name=realm) + + # Test bad refresh token + admin.connection = ConnectionManager( + base_url=admin.server_url, + headers={"Authorization": "Bearer bad", "Content-Type": "application/json"}, + timeout=60, + verify=admin.verify, + ) + admin.token["refresh_token"] = "bad" + with pytest.raises(KeycloakGetError) as err: + admin.get_realm(realm_name="test-refresh") + assert err.match( + '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'' + ) + admin.realm_name = "master" + admin.get_token() + admin.realm_name = realm + + # Test post refresh + admin.connection = ConnectionManager( + base_url=admin.server_url, + headers={"Authorization": "Bearer bad", "Content-Type": "application/json"}, + timeout=60, + verify=admin.verify, + ) + with pytest.raises(KeycloakAuthenticationError) as err: + admin.create_realm(payload={"realm": "test-refresh"}) + assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'') + + admin.auto_refresh_token = ["get", "post"] + admin.realm_name = "master" + admin.user_logout(user_id=admin.get_user_id(username=admin.username)) + assert admin.create_realm(payload={"realm": "test-refresh"}) == b"" + admin.realm_name = realm + + # Test update refresh + admin.connection = ConnectionManager( + base_url=admin.server_url, + headers={"Authorization": "Bearer bad", "Content-Type": "application/json"}, + timeout=60, + verify=admin.verify, + ) + with pytest.raises(KeycloakAuthenticationError) as err: + admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"}) + assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'') + + admin.auto_refresh_token = ["get", "post", "put"] + assert ( + admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"}) == dict() + ) + + # Test delete refresh + admin.connection = ConnectionManager( + base_url=admin.server_url, + headers={"Authorization": "Bearer bad", "Content-Type": "application/json"}, + timeout=60, + verify=admin.verify, + ) + with pytest.raises(KeycloakAuthenticationError) as err: + admin.delete_realm(realm_name="test-refresh") + assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'') + + admin.auto_refresh_token = ["get", "post", "put", "delete"] + assert admin.delete_realm(realm_name="test-refresh") == dict() From 94ef46b29b6f6d400bc4dc07702b931284781b0b Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Tue, 31 May 2022 12:53:49 +0200 Subject: [PATCH 042/222] feat: Support Token Exchange. Fixes #305 --- CHANGELOG.md | 1 + README.md | 3 +++ src/keycloak/keycloak_openid.py | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b757b..4916c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,3 +46,4 @@ All notable changes to this project will be documented in this file. ## [master] * Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` + * Add `KeycloakOpenID.token_exchange` to support Token Exchange diff --git a/README.md b/README.md index 85e3d34..d3572f5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,9 @@ config_well_known = keycloak_openid.well_known() token = keycloak_openid.token("user", "password") token = keycloak_openid.token("user", "password", totp="012345") +# Get token using Token Exchange +token = keycloak_openid.exchange_token(token['access_token'], "my_client", "other_client", "some_user") + # Get Userinfo userinfo = keycloak_openid.userinfo(token['access_token']) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index e73e963..a1f1f0a 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -254,6 +254,30 @@ class KeycloakOpenID: data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError) + def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: + """ + Use a token to obtain an entirely different token. See + https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange + + :param token: + :param client_id: + :param audience: + :param subject: + :return: + """ + params_path = {"realm-name": self.realm_name} + payload = { + "grant_type": ["urn:ietf:params:oauth:grant-type:token-exchange"], + "client_id": client_id, + "subject_token": token, + "requested_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "audience": audience, + "requested_subject": subject, + } + payload = self._add_secret_key(payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) + return raise_error_from_response(data_raw, KeycloakGetError) + def userinfo(self, token): """ The userinfo endpoint returns standard claims about the authenticated user, From b6990276c36fab64ba0a5406a6f22e68c51868c2 Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Tue, 31 May 2022 13:02:22 +0200 Subject: [PATCH 043/222] ci: fix docs generation --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 2b67d12..b60a1c9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -77,7 +77,7 @@ release = "0.0.0" # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. From 9a13f67fea8b69991ae0cd1ab1cb5dd205290fcc Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Tue, 31 May 2022 13:34:13 +0200 Subject: [PATCH 044/222] feat: Add get_idp_mappers, fix #329 --- src/keycloak/keycloak_admin.py | 14 ++++++++++++++ tests/test_keycloak_admin.py | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index d754cd4..e31102a 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -413,6 +413,20 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + def get_idp_mappers(self, idp_alias): + """ + Returns a list of ID Providers mappers + + IdentityProviderMapperRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmappers + + :param: idp_alias: alias for Idp to fetch mappers + :return: array IdentityProviderMapperRepresentation + """ + params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} + data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + def get_idps(self): """ Returns a list of ID Providers, diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index d927aae..87c093d 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -336,6 +336,12 @@ def test_idps(admin: KeycloakAdmin, realm: str): admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict()) assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') + # Test IdP mappers listing + idp_mappers = admin.get_idp_mappers( + idp_alias="github", + ) + assert len(idp_mappers) == 1 + # Test delete res = admin.delete_idp(idp_alias="github") assert res == dict(), res From f3de47e1b374a1035d59c1fbcab54af12069982a Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Thu, 2 Jun 2022 11:37:29 +0200 Subject: [PATCH 045/222] feat: Add update_mapper_in_idp --- src/keycloak/keycloak_admin.py | 27 ++++++++++++++++++++++++++- src/keycloak/urls_patterns.py | 1 + tests/test_keycloak_admin.py | 21 ++++++++++++++++----- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index e31102a..57dfb47 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -413,6 +413,31 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + def update_mapper_in_idp(self, idp_alias, mapper_id, payload): + """ + Update an IdP mapper + + IdentityProviderMapperRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update + + :param: idp_alias: alias for Idp to fetch mappers + :param: mapper_id: Mapper Id to update + :param: payload: IdentityProviderMapperRepresentation + :return: Http response + """ + params_path = { + "realm-name": self.realm_name, + "idp-alias": idp_alias, + "mapper-id": mapper_id, + } + + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_IDP_MAPPER_UPDATE.format(**params_path), + data=json.dumps(payload), + ) + + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def get_idp_mappers(self, idp_alias): """ Returns a list of ID Providers mappers @@ -2208,7 +2233,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get( - urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), + urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path) ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index d836ed4..16f348a 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -119,6 +119,7 @@ URL_ADMIN_REALMS = "admin/realms" URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_IDP_MAPPERS = "admin/realms/{realm-name}/identity-provider/instances/{idp-alias}/mappers" +URL_ADMIN_IDP_MAPPER_UPDATE = URL_ADMIN_IDP_MAPPERS + "/{mapper-id}" URL_ADMIN_IDP = "admin/realms//{realm-name}/identity-provider/instances/{alias}" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE = ( diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 87c093d..a2cd5d7 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -337,10 +337,23 @@ def test_idps(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') # Test IdP mappers listing - idp_mappers = admin.get_idp_mappers( + idp_mappers = admin.get_idp_mappers(idp_alias="github") + assert len(idp_mappers) == 1 + + # Test IdP mapper update + res = admin.update_mapper_in_idp( idp_alias="github", + mapper_id=idp_mappers[0]["id"], + # For an obscure reason, keycloak expect all fields + payload={ + "id": idp_mappers[0]["id"], + "identityProviderAlias": "github-alias", + "identityProviderMapper": "github-user-attribute-mapper", + "name": "test", + "config": idp_mappers[0]["config"], + }, ) - assert len(idp_mappers) == 1 + assert res == dict(), res # Test delete res = admin.delete_idp(idp_alias="github") @@ -1452,9 +1465,7 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"Could not find client scope"}\'') test_mapper["config"]["user.attribute"] = "test" res_update = admin.update_mapper_in_client_scope( - client_scope_id=res, - protocol_mapper_id=test_mapper["id"], - payload=test_mapper, + client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper ) assert res_update == dict() assert ( From 35f1f4630b23a7ddbd3df9cd030ad1e83db1389d Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Thu, 2 Jun 2022 11:47:19 +0200 Subject: [PATCH 046/222] test: Make curl silent --- test_keycloak_init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index bee8830..bd4c30a 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -14,7 +14,7 @@ function keycloak_start() { echo "Starting keycloak docker container" docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev SECONDS=0 - until curl localhost:$KEYCLOAK_PORT; do + until curl --silent --output /dev/null localhost:$KEYCLOAK_PORT; do sleep 5; if [ ${SECONDS} -gt 180 ]; then echo "Timeout exceeded"; From bcdf1b825bd1c7e9c0c0cdf91bcb957ac538cf4a Mon Sep 17 00:00:00 2001 From: Bruno Bonfils Date: Fri, 3 Jun 2022 14:28:08 +0200 Subject: [PATCH 047/222] feat: Add update_idp --- src/keycloak/keycloak_admin.py | 16 ++++++++++++++++ tests/test_keycloak_admin.py | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 57dfb47..7f82679 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -397,6 +397,22 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + def update_idp(self, idp_alias, payload): + """ + Update an ID Provider + + IdentityProviderRepresentation + https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource + + :param: alias: alias for IdP to update + :param: payload: The IdentityProviderRepresentation + """ + params_path = {"realm-name": self.realm_name, "alias": idp_alias} + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_IDP.format(**params_path), data=json.dumps(payload) + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def add_mapper_to_idp(self, idp_alias, payload): """ Create an ID Provider, diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index a2cd5d7..ad4e2ce 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -320,6 +320,11 @@ def test_idps(admin: KeycloakAdmin, realm: str): assert len(idps) == 1 assert "github" == idps[0]["alias"] + # Test IdP update + res = admin.update_idp(idp_alias="github", payload=idps[0]) + + assert res == {}, res + # Test adding a mapper res = admin.add_mapper_to_idp( idp_alias="github", From 667d1e088e352f4262cc5cc35d6f69b337d9d513 Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Mon, 13 Jun 2022 12:50:17 +0200 Subject: [PATCH 048/222] feat: support token exchange config via admin API This adds support for the basic endpoints necessary to configure client-to-client token exchange. The /authz API is lacking official documentation. Basic docs added to docstrings instead. --- src/keycloak/keycloak_admin.py | 143 +++++++++++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 11 +++ 2 files changed, 154 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 7f82679..942edc9 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2774,3 +2774,146 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_management_permissions(self, client_id): + """ + Get management permissions for a client. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def update_client_management_permissions(self, payload, client_id): + """ + Update management permissions for a client. + + ManagementPermissionReference + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_managementpermissionreference + + :param payload: ManagementPermissionReference + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :return: Keycloak server response + + + Payload example:: + + payload={ + "enabled": true + } + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200]) + + def get_client_authz_policy_scopes(self, client_id, policy_id): + """ + Get scopes for a given policy. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :param policy_id: No Document + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_authz_policy_resources(self, client_id, policy_id): + """ + Get resources for a given policy. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :param policy_id: No Document + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_authz_scope_permission(self, client_id, scope_id): + """ + Get permissions for a given scope. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :param scope_id: No Document + :return: Keycloak server response + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def update_client_authz_scope_permission(self, payload, client_id, scope_id): + """ + Update permissions for a given scope. + + :param payload: No Document + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :param scope_id: No Document + :return: Keycloak server response + + + Payload example:: + + payload={ + "id": scope_id, + "name": "My Permission Name", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "resources": [some_resource_id], + "scopes": [some_scope_id], + "policies": [some_policy_id], + } + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201]) + + def create_client_authz_client_policy(self, payload, client_id): + """ + Create a new policy for a given client. + + :param payload: No Document + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :return: Keycloak server response (RoleRepresentation) + + + Payload example:: + + payload={ + "type": "client", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "name": "My Policy", + "clients": [other_client_id], + } + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 16f348a..3ec134c 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -87,6 +87,7 @@ URL_ADMIN_CLIENT_ROLE = URL_ADMIN_CLIENT + "/roles/{role-name}" URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE = URL_ADMIN_CLIENT_ROLE + "/composites" URL_ADMIN_CLIENT_ROLE_MEMBERS = URL_ADMIN_CLIENT + "/roles/{role-name}/users" URL_ADMIN_CLIENT_ROLE_GROUPS = URL_ADMIN_CLIENT + "/roles/{role-name}/groups" +URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS = URL_ADMIN_CLIENT + "/management/permissions" URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" @@ -101,6 +102,16 @@ URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = ( URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = ( URL_ADMIN_CLIENT + "/authz/resource-server/permission/resource?max=-1" ) +URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES = ( + URL_ADMIN_CLIENT + "/authz/resource-server/policy/{policy-id}/scopes" +) +URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES = ( + URL_ADMIN_CLIENT + "/authz/resource-server/policy/{policy-id}/resources" +) +URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION = ( + URL_ADMIN_CLIENT + "/authz/resource-server/permission/scope/{scope-id}" +) +URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY = URL_ADMIN_CLIENT + "/authz/resource-server/policy/client" URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER = URL_ADMIN_CLIENT + "/service-account-user" URL_ADMIN_CLIENT_CERTS = URL_ADMIN_CLIENT + "/certificates/{attr}" From 39706bcc68df8ad3c54e4611dfbb95fd80ebb3ed Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Mon, 13 Jun 2022 14:17:38 +0200 Subject: [PATCH 049/222] ci: add test case for token exchange setup --- tests/test_keycloak_admin.py | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index ad4e2ce..74cdc14 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1176,6 +1176,86 @@ def test_client_roles(admin: KeycloakAdmin, client: str): assert err.match('404: b\'{"error":"Could not find role"}\'') +def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): + # Test enabling token exchange between two confidential clients + admin.realm_name = realm + + # Create test clients + source_client_id = admin.create_client( + payload={"name": "Source Client", "clientId": "source-client"} + ) + target_client_id = admin.create_client( + payload={"name": "Target Client", "clientId": "target-client"} + ) + for c in admin.get_clients(): + if c["clientId"] == "realm-management": + realm_management_id = c["id"] + break + else: + raise AssertionError("Missing realm management client") + + # Enable permissions on the Superset client + admin.update_client_management_permissions( + payload={"enabled": True}, client_id=target_client_id + ) + + # Fetch various IDs and strings needed when creating the permission + token_exchange_permission_id = admin.get_client_management_permissions( + client_id=target_client_id + )["scopePermissions"]["token-exchange"] + scopes = admin.get_client_authz_policy_scopes( + client_id=realm_management_id, policy_id=token_exchange_permission_id + ) + + for s in scopes: + if s["name"] == "token-exchange": + token_exchange_scope_id = s["id"] + break + else: + raise AssertionError("Missing token-exchange scope") + + resources = admin.get_client_authz_policy_resources( + client_id=realm_management_id, policy_id=token_exchange_permission_id + ) + for r in resources: + if r["name"] == f"client.resource.{target_client_id}": + token_exchange_resource_id = r["_id"] + break + else: + raise AssertionError("Missing client resource") + + # Create a client policy for source client + client_policy_id = admin.create_client_authz_client_policy( + payload={ + "type": "client", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "name": "Exchange source client token with target client token", + "clients": [source_client_id], + }, + client_id=realm_management_id, + )["id"] + + # Update permissions on the target client to reference this policy + permission_name = admin.get_client_authz_scope_permission( + client_id=realm_management_id, scope_id=token_exchange_permission_id + )["name"] + admin.update_client_authz_scope_permission( + payload={ + "id": token_exchange_permission_id, + "name": permission_name, + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "resources": [token_exchange_resource_id], + "scopes": [token_exchange_scope_id], + "policies": [client_policy_id], + }, + client_id=realm_management_id, + scope_id=token_exchange_permission_id, + ) + + def test_email(admin: KeycloakAdmin, user: str): # Emails will fail as we don't have SMTP test setup with pytest.raises(KeycloakPutError) as err: From 2f212c1350b0e87d34ae3bdd80be599327abb154 Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Thu, 16 Jun 2022 10:43:55 +0200 Subject: [PATCH 050/222] feat: Allow fetching existing policies before calling create_client_authz_client_policy() --- src/keycloak/keycloak_admin.py | 14 ++++++++++++++ tests/test_keycloak_admin.py | 10 +++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 942edc9..099f9fc 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2891,6 +2891,20 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201]) + def get_client_authz_client_policies(self, client_id): + """ + Get policies for a given client. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :return: Keycloak server response (RoleRepresentation) + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path), + ) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) + def create_client_authz_client_policy(self, payload, client_id): """ Create a new policy for a given client. diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 74cdc14..6f33e03 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1225,16 +1225,24 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): raise AssertionError("Missing client resource") # Create a client policy for source client + policy_name = "Exchange source client token with target client token" client_policy_id = admin.create_client_authz_client_policy( payload={ "type": "client", "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", - "name": "Exchange source client token with target client token", + "name": policy_name, "clients": [source_client_id], }, client_id=realm_management_id, )["id"] + policies = admin.get_client_authz_client_policies(client_id=realm_management_id) + for policy in policies: + if policy["name"] == policy_name: + assert policy["clients"] == [source_client_id] + break + else: + raise AssertionError("Missing client policy") # Update permissions on the target client to reference this policy permission_name = admin.get_client_authz_scope_permission( From d2a6262d6152106b6d3f1a2ba5e8b99322966ffe Mon Sep 17 00:00:00 2001 From: Fredrik Lindner Date: Wed, 22 Jun 2022 11:57:28 +0200 Subject: [PATCH 051/222] feat: Ability to set custom timeout for KCOpenId and KCAdmin --- src/keycloak/keycloak_admin.py | 3 +++ src/keycloak/keycloak_openid.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 099f9fc..ed86877 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -88,6 +88,7 @@ class KeycloakAdmin: custom_headers=None, user_realm_name=None, auto_refresh_token=None, + timeout=60, ): self.server_url = server_url self.username = username @@ -100,6 +101,7 @@ class KeycloakAdmin: self.auto_refresh_token = auto_refresh_token or [] self.user_realm_name = user_realm_name self.custom_headers = custom_headers + self.timeout = timeout # Get token Admin self.get_token() @@ -2695,6 +2697,7 @@ class KeycloakAdmin: verify=self.verify, client_secret_key=self.client_secret_key, custom_headers=self.custom_headers, + timeout=self.timeout, ) grant_type = [] diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index a1f1f0a..7216b5d 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -73,13 +73,14 @@ class KeycloakOpenID: verify=True, custom_headers=None, proxies=None, + timeout=60, ): self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name headers = custom_headers if custom_headers is not None else dict() self.connection = ConnectionManager( - base_url=server_url, headers=headers, timeout=60, verify=verify, proxies=proxies + base_url=server_url, headers=headers, timeout=timeout, verify=verify, proxies=proxies ) self.authorization = Authorization() From e7152e5c7403c037deec1b8f12e60a2909b70597 Mon Sep 17 00:00:00 2001 From: Chuma Umenze Date: Mon, 27 Jun 2022 14:38:27 +0100 Subject: [PATCH 052/222] build: use poetry for package management --- .github/workflows/publish.yaml | 2 +- CONTRIBUTING.md | 25 +- Pipfile | 15 - Pipfile.lock | 107 --- dev-requirements.txt | 5 - docs-requirements.txt | 9 - poetry.lock | 1433 ++++++++++++++++++++++++++++++++ pyproject.toml | 71 ++ requirements.txt | 3 - setup.cfg | 2 - setup.py | 58 -- src/keycloak/keycloak_admin.py | 2 +- tox.ini | 42 +- 13 files changed, 1542 insertions(+), 232 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock delete mode 100644 dev-requirements.txt delete mode 100644 docs-requirements.txt create mode 100644 poetry.lock delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 531a8e1..693f67b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -21,7 +21,7 @@ jobs: - name: Apply the tag version run: | version=${{ github.ref_name }} - sed -i 's/__version__ = .*/__version__ = "'${version:1}'"/' src/keycloak/_version.py + sed -Ei '/^version = /s|= "[0-9.]+"$|= "'${version:-1}'"|' pyproject.toml - name: Run build run: | tox -e build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 683f7ee..645b3ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,11 +10,17 @@ The development environment is mainly up to the developer. Our recommendations a virtual environment and install the necessary requirements. Example ```sh -python -m venv venv -source venv/bin/activate -python -m pip install -U pip -python -m pip install -r requirements.txt -python -m pip install -r dev-requirements.txt +# Install and upgrade pip & poetry +python -m pip install --upgrade pip poetry + +# Create virtualenv +python -m poetry env use + +# install package dependencies including dev dependencies +python -m poetry install --dev + +# Activate virtualenv +python -m poetry shell ``` ## Running checks and tests @@ -67,9 +73,12 @@ After cloning this repository, you must install the pre-commit hook for conventional commits (this is included in the `dev-requirements.txt`) ```sh -python3 -m venv .venv -source .venv/bin/activate -python3 -m pip install pre-commit +# Create virtualenv +python -m poetry env use + +# Activate virtualenv +python -m poetry shell + pre-commit install --install-hooks -t pre-commit -t pre-push -t commit-msg ``` diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 828d298..0000000 --- a/Pipfile +++ /dev/null @@ -1,15 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -requests = ">=2.20.0" -httmock = ">=1.2.5" -python-jose = ">=1.4.0" -urllib3 = ">=1.26.5" - -[dev-packages] - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 0430b1e..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,107 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "8c12705e89c665da92fc69ef0d312a9ca313703c839c15d18fcc833dcb87d7f7" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "certifi": { - "hashes": [ - "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", - "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" - ], - "version": "==2020.12.5" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "ecdsa": { - "hashes": [ - "sha256:881fa5e12bb992972d3d1b3d4dfbe149ab76a89f13da02daa5ea1ec7dea6e747", - "sha256:cfc046a2ddd425adbd1a78b3c46f0d1325c657811c0f45ecc3a0a6236c1e50ff" - ], - "version": "==0.16.1" - }, - "future": { - "hashes": [ - "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" - ], - "version": "==0.18.2" - }, - "httmock": { - "hashes": [ - "sha256:4696306d1ff835c3ca865fdef2684d7e130b4120cc00126f862ba4797b1602ac" - ], - "index": "pypi", - "version": "==1.2.6" - }, - "idna": { - "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" - ], - "version": "==2.7" - }, - "pyasn1": { - "hashes": [ - "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", - "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba" - ], - "version": "==0.4.8" - }, - "python-jose": { - "hashes": [ - "sha256:29701d998fe560e52f17246c3213a882a4a39da7e42c7015bcc1f7823ceaff1c", - "sha256:ed7387f0f9af2ea0ddc441d83a6eb47a5909bd0c8a72ac3250e75afec2cc1371" - ], - "index": "pypi", - "version": "==3.0.1" - }, - "requests": { - "hashes": [ - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" - ], - "index": "pypi", - "version": "==2.19.1" - }, - "rsa": { - "hashes": [ - "sha256:69805d6b69f56eb05b62daea3a7dbd7aa44324ad1306445e05da8060232d00f4", - "sha256:a8774e55b59fd9fc893b0d05e9bfc6f47081f46ff5b46f39ccf24631b7be356b" - ], - "index": "pypi", - "version": "==4.7" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "version": "==1.15.0" - }, - "urllib3": { - "hashes": [ - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" - ], - "version": "==1.23" - } - }, - "develop": {} -} diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index d2f6981..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -tox -pytest -pytest-cov -wheel -pre-commit diff --git a/docs-requirements.txt b/docs-requirements.txt deleted file mode 100644 index 343bf89..0000000 --- a/docs-requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -mock -alabaster -commonmark -recommonmark -sphinx -sphinx-rtd-theme -readthedocs-sphinx-ext -m2r2 -sphinx-autoapi diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..cbad183 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1433 @@ +[[package]] +name = "alabaster" +version = "0.7.12" +description = "A configurable sidebar-enabled Sphinx theme" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "astroid" +version = "2.11.6" +description = "An abstract syntax tree for Python with inference support." +category = "main" +optional = true +python-versions = ">=3.6.2" + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} +wrapt = ">=1.11,<2" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "babel" +version = "2.10.3" +description = "Internationalization utilities" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +pytz = ">=2015.7" + +[[package]] +name = "black" +version = "22.3.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2022.6.15" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.5" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "main" +optional = true +python-versions = "*" + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "coverage" +version = "6.4.1" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "distlib" +version = "0.3.4" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "ecdsa" +version = "0.17.0" +description = "ECDSA cryptographic signature library (pure python)" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "filelock" +version = "3.7.1" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] +testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] + +[[package]] +name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[[package]] +name = "identify" +version = "2.5.1" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "imagesize" +version = "1.3.0" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "main" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.12.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = true +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.7.1" +description = "A fast and thorough lazy object proxy." +category = "main" +optional = true +python-versions = ">=3.6" + +[[package]] +name = "m2r2" +version = "0.3.2" +description = "Markdown and reStructuredText in a single file." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +docutils = "*" +mistune = "0.8.4" + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = true +python-versions = ">=3.7" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "mock" +version = "4.0.3" +description = "Rolling backport of unittest.mock for all Pythons" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.extras] +build = ["twine", "wheel", "blurb"] +docs = ["sphinx"] +test = ["pytest (<5.4)", "pytest-cov"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "nodeenv" +version = "1.7.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.19.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.12.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = true +python-versions = ">=3.6" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pytest" +version = "7.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pycrypto (>=2.6.0,<2.7.0)", "pyasn1"] +pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"] + +[[package]] +name = "pytz" +version = "2022.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "readthedocs-sphinx-ext" +version = "2.1.8" +description = "Sphinx extension for Read the Docs overrides" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +Jinja2 = ">=2.9" +packaging = "*" +requests = "*" + +[[package]] +name = "recommonmark" +version = "0.7.1" +description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +commonmark = ">=0.8.1" +docutils = ">=0.11" +sphinx = ">=1.3.1" + +[[package]] +name = "requests" +version = "2.28.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2.0.0,<2.1.0" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "rsa" +version = "4.8" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "sphinx" +version = "5.0.2" +description = "Python documentation generator" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.19" +imagesize = "*" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.950)", "docutils-stubs", "types-typed-ast", "types-requests"] +test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"] + +[[package]] +name = "sphinx-autoapi" +version = "1.8.4" +description = "Sphinx API documentation generator" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +astroid = ">=2.7" +Jinja2 = "*" +PyYAML = "*" +sphinx = ">=3.0" +unidecode = "*" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +dotnet = ["sphinxcontrib-dotnetdomain"] +go = ["sphinxcontrib-golangdomain"] + +[[package]] +name = "sphinx-rtd-theme" +version = "1.0.0" +description = "Read the Docs theme for Sphinx" +category = "main" +optional = true +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" + +[package.dependencies] +docutils = "<0.18" +sphinx = ">=1.6" + +[package.extras] +dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "main" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "main" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest", "html5lib"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "main" +optional = true +python-versions = ">=3.5" + +[package.extras] +test = ["pytest", "flake8", "mypy"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "main" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "main" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tox" +version = "3.25.0" +description = "tox is a generic virtualenv management and test command line tool" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} +filelock = ">=3.0.0" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +packaging = ">=14" +pluggy = ">=0.12.0" +py = ">=1.4.17" +six = ">=1.14.0" +toml = ">=0.9.4" +virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" + +[package.extras] +docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "psutil (>=5.6.1)", "pathlib2 (>=2.3.3)"] + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.2.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "unidecode" +version = "1.3.4" +description = "ASCII transliterations of Unicode text" +category = "main" +optional = true +python-versions = ">=3.5" + +[[package]] +name = "urllib3" +version = "1.26.9" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.15.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +distlib = ">=0.3.1,<1" +filelock = ">=3.2,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +platformdirs = ">=2,<3" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] + +[[package]] +name = "wrapt" +version = "1.14.1" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "zipp" +version = "3.8.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] + +[extras] +docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "58ad1dfa1c2cdbb232bc53ceb2c1a9d0767a3db7fd8e6d0baae3e753f1c570dc" + +[metadata.files] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] +astroid = [ + {file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"}, + {file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +babel = [ + {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, + {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +] +black = [ + {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, + {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, + {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, + {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, + {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, + {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, + {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, + {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, + {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, + {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, + {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, + {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, + {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, + {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, + {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, + {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, + {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, + {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, + {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, +] +certifi = [ + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, +] +cfgv = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +commonmark = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] +coverage = [ + {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, + {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, + {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, + {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, + {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, + {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, + {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, + {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, + {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, + {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, + {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, + {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, + {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, +] +distlib = [ + {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, + {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, +] +docutils = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] +ecdsa = [ + {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, + {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"}, +] +filelock = [ + {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, + {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, +] +flake8 = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] +identify = [ + {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, + {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +imagesize = [ + {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, + {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, + {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +lazy-object-proxy = [ + {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, + {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, +] +m2r2 = [ + {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, + {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +mock = [ + {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, + {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +nodeenv = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +pre-commit = [ + {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, + {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] +pygments = [ + {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, + {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +python-jose = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] +pytz = [ + {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, + {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +readthedocs-sphinx-ext = [ + {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, + {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, +] +recommonmark = [ + {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, + {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, +] +requests = [ + {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, + {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, +] +rsa = [ + {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, + {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] +sphinx = [ + {file = "Sphinx-5.0.2-py3-none-any.whl", hash = "sha256:d3e57663eed1d7c5c50895d191fdeda0b54ded6f44d5621b50709466c338d1e8"}, + {file = "Sphinx-5.0.2.tar.gz", hash = "sha256:b18e978ea7565720f26019c702cd85c84376e948370f1cd43d60265010e1c7b0"}, +] +sphinx-autoapi = [ + {file = "sphinx-autoapi-1.8.4.tar.gz", hash = "sha256:8c4ec5fbedc1e6e8f4692bcc4fcd1abcfb9e8dfca8a4ded60ad811a743c22ccc"}, + {file = "sphinx_autoapi-1.8.4-py2.py3-none-any.whl", hash = "sha256:007bf9e24cd2aa0ac0561f67e8bcd6a6e2e8911ef4b4fd54aaba799d8832c8d0"}, +] +sphinx-rtd-theme = [ + {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, + {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, +] +sphinxcontrib-applehelp = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] +sphinxcontrib-devhelp = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] +sphinxcontrib-htmlhelp = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] +sphinxcontrib-jsmath = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] +sphinxcontrib-qthelp = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] +sphinxcontrib-serializinghtml = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tox = [ + {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"}, + {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"}, +] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] +typing-extensions = [ + {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, + {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, +] +unidecode = [ + {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, + {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, +] +urllib3 = [ + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, +] +virtualenv = [ + {file = "virtualenv-20.15.0-py2.py3-none-any.whl", hash = "sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336"}, + {file = "virtualenv-20.15.0.tar.gz", hash = "sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc"}, +] +wrapt = [ + {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, + {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, + {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, + {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, + {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, + {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, + {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, + {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, + {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, + {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, + {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, + {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, + {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, + {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, + {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, + {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, +] +zipp = [ + {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, + {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, +] diff --git a/pyproject.toml b/pyproject.toml index f947450..d6012fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,74 @@ +[tool.poetry] +name = "python-keycloak" +version = "1.9.0" +description = "python-keycloak is a Python package providing access to the Keycloak API." +license = "MIT" +readme = "README.md" +keywords = [ "keycloak", "openid", "oidc" ] +authors = [ + "Marcos Pereira ", + "Richard Nemeth " +] +classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Development Status :: 3 - Alpha", + "Operating System :: MacOS", + "Operating System :: Unix", + "Operating System :: Microsoft :: Windows", + "Topic :: Utilities", +] +packages = [ + { include = "keycloak", from = "src/" }, + { include = "keycloak/**/*.py", from = "src/" }, +] + +[tool.poetry.urls] +Documentation = "https://python-keycloak.readthedocs.io/en/latest/" +"Issue tracker" = "https://github.com/marcospereirampj/python-keycloak/issues" + +[tool.poetry.dependencies] +python = "^3.7" +requests = "^2.20.0" +python-jose = "^3.3.0" +urllib3 = "^1.26.0" +mock = {version = "^4.0.3", optional = true} +alabaster = {version = "^0.7.12", optional = true} +commonmark = {version = "^0.9.1", optional = true} +recommonmark = {version = "^0.7.1", optional = true} +Sphinx = {version = "^5.0.2", optional = true} +sphinx-rtd-theme = {version = "^1.0.0", optional = true} +readthedocs-sphinx-ext = {version = "^2.1.8", optional = true} +m2r2 = {version = "^0.3.2", optional = true} +sphinx-autoapi = {version = "^1.8.4", optional = true} + +[tool.poetry.dev-dependencies] +tox = "^3.25.0" +pytest = "^7.1.2" +pytest-cov = "^3.0.0" +wheel = "^0.37.1" +pre-commit = "^2.19.0" +isort = "^5.10.1" +black = "^22.3.0" +flake8 = "^3.5.0" + +[tool.poetry.extras] +docs = [ + "mock", + "alabaster", + "commonmark", + "recommonmark", + "sphinx", + "sphinx-rtd-theme", + "readthedocs-sphinx-ext", + "m2r2", + "sphinx-autoapi", +] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + [tool.black] line-length = 99 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 5474982..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -requests>=2.20.0 -python-jose>=1.4.0 -urllib3>=1.26.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 224a779..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -description-file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 27a188d..0000000 --- a/setup.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -import re - -from setuptools import find_packages, setup - -with open("README.md", "r") as fh: - long_description = fh.read() - -with open("requirements.txt", "r") as fh: - reqs = fh.read().split("\n") - -with open("dev-requirements.txt", "r") as fh: - dev_reqs = fh.read().split("\n") - -with open("docs-requirements.txt", "r") as fh: - docs_reqs = fh.read().split("\n") - - -VERSIONFILE = "src/keycloak/_version.py" -verstrline = open(VERSIONFILE, "rt").read() -VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" -mo = re.search(VSRE, verstrline, re.M) -if mo: - verstr = mo.group(1) -else: - raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) - -setup( - name="python-keycloak", - version=verstr, - url="https://github.com/marcospereirampj/python-keycloak", - license="The MIT License", - author="Marcos Pereira, Richard Nemeth", - author_email="marcospereira.mpj@gmail.com, ryshoooo@gmail.com", - keywords="keycloak openid oidc", - description="python-keycloak is a Python package providing access to the Keycloak API.", - long_description=long_description, - long_description_content_type="text/markdown", - packages=find_packages("src"), - package_dir={"": "src"}, - install_requires=reqs, - tests_require=dev_reqs, - extras_require={"docs": docs_reqs}, - python_requires=">=3.7", - project_urls={ - "Documentation": "https://python-keycloak.readthedocs.io/en/latest/", - "Issue tracker": "https://github.com/marcospereirampj/python-keycloak/issues", - }, - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Development Status :: 3 - Alpha", - "Operating System :: MacOS", - "Operating System :: Unix", - "Operating System :: Microsoft :: Windows", - "Topic :: Utilities", - ], -) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ed86877..44e9c3b 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2904,7 +2904,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( - urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path), + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path) ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) diff --git a/tox.ini b/tox.ini index 54b2c2b..c5c4d5b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,48 +1,44 @@ [tox] +isolated_build = true envlist = check, apply-check, docs, tests, build [testenv] install_command = pip install {opts} {packages} +deps = + poetry>=1.1.13 +commands_pre = + bash -c "python -m pip install -r <(poetry export --dev --extras=docs --no-interaction)" +whitelist_externals = + bash [testenv:check] -deps = - black - isort - flake8 commands = - black --check --diff src/keycloak tests docs setup.py - isort -c --df src/keycloak tests docs setup.py - flake8 src/keycloak tests docs setup.py + black --check --diff src/keycloak tests docs + isort -c --df src/keycloak tests docs + flake8 src/keycloak tests docs [testenv:apply-check] -deps = - black - isort - flake8 commands = - black -C src/keycloak tests docs setup.py - black src/keycloak tests docs setup.py - isort src/keycloak tests docs setup.py + black -C src/keycloak tests docs + black src/keycloak tests docs + isort src/keycloak tests docs [testenv:docs] -deps = - .[docs] commands = - python -m sphinx -T -E -W -b html -d _build/doctrees -D language=en ./docs/source _build/html + sphinx-build -T -E -W -b html -d _build/doctrees -D language=en ./docs/source _build/html [testenv:tests] setenv = file|tox.env -deps = - -rrequirements.txt - -rdev-requirements.txt commands = ./test_keycloak_init.sh "pytest -vv --cov=keycloak --cov-report term-missing {posargs}" [testenv:build] -deps = - -rdev-requirements.txt +commands_pre = +setenv = + POETRY_VIRTUALENVS_CREATE = false commands = - python setup.py sdist bdist_wheel + poetry build --format sdist + poetry build --format wheel [flake8] max-line-length = 99 From 81695472d3237d3f8129256fa432a33e48b53398 Mon Sep 17 00:00:00 2001 From: Chuma Umenze Date: Mon, 27 Jun 2022 19:43:52 +0100 Subject: [PATCH 053/222] chore: tox test install without hashes --- CONTRIBUTING.md | 2 +- pyproject.toml | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 645b3ea..2978d02 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ python -m pip install --upgrade pip poetry python -m poetry env use # install package dependencies including dev dependencies -python -m poetry install --dev +python -m poetry install # Activate virtualenv python -m poetry shell diff --git a/pyproject.toml b/pyproject.toml index d6012fe..ae6fc1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-keycloak" -version = "1.9.0" +version = "0.0.0" description = "python-keycloak is a Python package providing access to the Keycloak API." license = "MIT" readme = "README.md" diff --git a/tox.ini b/tox.ini index c5c4d5b..b88069a 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ install_command = pip install {opts} {packages} deps = poetry>=1.1.13 commands_pre = - bash -c "python -m pip install -r <(poetry export --dev --extras=docs --no-interaction)" + bash -c "python -m pip install -r <(poetry export --dev --extras=docs --without-hashes --no-interaction)" whitelist_externals = bash From b95b1d3505d5fe5d7f82ebb871122ca9afd80d6e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 09:19:12 +0200 Subject: [PATCH 054/222] refactor: slight restructure of the base fixtures --- tests/conftest.py | 73 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b9c266a..ada9820 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,22 +3,62 @@ import uuid import pytest -from keycloak import KeycloakAdmin +from keycloak import KeycloakAdmin, KeycloakOpenID + + +class KeycloakTestEnv(object): + def __init__( + self, + host: str = os.environ["KEYCLOAK_HOST"], + port: str = os.environ["KEYCLOAK_PORT"], + username: str = os.environ["KEYCLOAK_ADMIN"], + password: str = os.environ["KEYCLOAK_ADMIN_PASSWORD"], + ): + self.KEYCLOAK_HOST = host + self.KEYCLOAK_PORT = port + self.KEYCLOAK_ADMIN = username + self.KEYCLOAK_ADMIN_PASSWORD = password + + @property + def KEYCLOAK_HOST(self): + return self._KEYCLOAK_HOST + + @KEYCLOAK_HOST.setter + def KEYCLOAK_HOST(self, value: str): + self._KEYCLOAK_HOST = value + + @property + def KEYCLOAK_PORT(self): + return self._KEYCLOAK_PORT + + @KEYCLOAK_PORT.setter + def KEYCLOAK_PORT(self, value: str): + self._KEYCLOAK_PORT = value + + @property + def KEYCLOAK_ADMIN(self): + return self._KEYCLOAK_ADMIN + + @KEYCLOAK_ADMIN.setter + def KEYCLOAK_ADMIN(self, value: str): + self._KEYCLOAK_ADMIN = value + + @property + def KEYCLOAK_ADMIN_PASSWORD(self): + return self._KEYCLOAK_ADMIN_PASSWORD + + @KEYCLOAK_ADMIN_PASSWORD.setter + def KEYCLOAK_ADMIN_PASSWORD(self, value: str): + self._KEYCLOAK_ADMIN_PASSWORD = value @pytest.fixture def env(): - class KeycloakTestEnv(object): - KEYCLOAK_HOST = os.environ["KEYCLOAK_HOST"] - KEYCLOAK_PORT = os.environ["KEYCLOAK_PORT"] - KEYCLOAK_ADMIN = os.environ["KEYCLOAK_ADMIN"] - KEYCLOAK_ADMIN_PASSWORD = os.environ["KEYCLOAK_ADMIN_PASSWORD"] - return KeycloakTestEnv() @pytest.fixture -def admin(env): +def admin(env: KeycloakTestEnv): return KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -26,6 +66,23 @@ def admin(env): ) +@pytest.fixture +def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + # Set the realm + admin.realm_name = realm + # Create client + client = str(uuid.uuid4()) + client_id = admin.create_client(payload={"name": client, "clientId": client}) + # Return OID + yield KeycloakOpenID( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + realm_name=realm, + client_id=client, + ) + # Cleanup + admin.delete_client(client_id=client_id) + + @pytest.fixture def realm(admin: KeycloakAdmin) -> str: realm_name = str(uuid.uuid4()) From 590c7bb582237739582fc58352048ad3ca27be84 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 09:19:38 +0200 Subject: [PATCH 055/222] test: test of init and well_known of oid --- src/keycloak/keycloak_openid.py | 1 - tests/test_keycloak_openid.py | 79 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/test_keycloak_openid.py diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 7216b5d..3e045bc 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -170,7 +170,6 @@ class KeycloakOpenID: params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) def auth_url(self, redirect_uri): diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py new file mode 100644 index 0000000..0ee1bbb --- /dev/null +++ b/tests/test_keycloak_openid.py @@ -0,0 +1,79 @@ +from keycloak.keycloak_openid import KeycloakOpenID +from keycloak.connection import ConnectionManager +from keycloak.authorization import Authorization + + +def test_keycloak_openid_init(env): + oid = KeycloakOpenID( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + realm_name="master", + client_id="admin-cli", + ) + + assert oid.client_id == "admin-cli" + assert oid.client_secret_key is None + assert oid.realm_name == "master" + assert isinstance(oid.connection, ConnectionManager) + assert isinstance(oid.authorization, Authorization) + + +def test_well_known(oid: KeycloakOpenID): + res = oid.well_known() + assert res is not None + assert res != dict() + for key in [ + "acr_values_supported", + "authorization_encryption_alg_values_supported", + "authorization_encryption_enc_values_supported", + "authorization_endpoint", + "authorization_signing_alg_values_supported", + "backchannel_authentication_endpoint", + "backchannel_authentication_request_signing_alg_values_supported", + "backchannel_logout_session_supported", + "backchannel_logout_supported", + "backchannel_token_delivery_modes_supported", + "check_session_iframe", + "claim_types_supported", + "claims_parameter_supported", + "claims_supported", + "code_challenge_methods_supported", + "device_authorization_endpoint", + "end_session_endpoint", + "frontchannel_logout_session_supported", + "frontchannel_logout_supported", + "grant_types_supported", + "id_token_encryption_alg_values_supported", + "id_token_encryption_enc_values_supported", + "id_token_signing_alg_values_supported", + "introspection_endpoint", + "introspection_endpoint_auth_methods_supported", + "introspection_endpoint_auth_signing_alg_values_supported", + "issuer", + "jwks_uri", + "mtls_endpoint_aliases", + "pushed_authorization_request_endpoint", + "registration_endpoint", + "request_object_encryption_alg_values_supported", + "request_object_encryption_enc_values_supported", + "request_object_signing_alg_values_supported", + "request_parameter_supported", + "request_uri_parameter_supported", + "require_pushed_authorization_requests", + "require_request_uri_registration", + "response_modes_supported", + "response_types_supported", + "revocation_endpoint", + "revocation_endpoint_auth_methods_supported", + "revocation_endpoint_auth_signing_alg_values_supported", + "scopes_supported", + "subject_types_supported", + "tls_client_certificate_bound_access_tokens", + "token_endpoint", + "token_endpoint_auth_methods_supported", + "token_endpoint_auth_signing_alg_values_supported", + "userinfo_encryption_alg_values_supported", + "userinfo_encryption_enc_values_supported", + "userinfo_endpoint", + "userinfo_signing_alg_values_supported", + ]: + assert key in res From d79939535f7da6f8d1db1f37dfc6c3423b108ff4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 09:20:02 +0200 Subject: [PATCH 056/222] style: applied isort --- tests/test_keycloak_openid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 0ee1bbb..53a76dd 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -1,6 +1,6 @@ -from keycloak.keycloak_openid import KeycloakOpenID -from keycloak.connection import ConnectionManager from keycloak.authorization import Authorization +from keycloak.connection import ConnectionManager +from keycloak.keycloak_openid import KeycloakOpenID def test_keycloak_openid_init(env): From 5cd8fc391398762524e738d434b0bedee1af44cd Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 07:52:23 +0000 Subject: [PATCH 057/222] test: use tox-poetry plugin for tox --- tox.ini | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index b88069a..0458821 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,10 @@ [tox] -isolated_build = true +requires = + tox-poetry + poetry envlist = check, apply-check, docs, tests, build [testenv] -install_command = pip install {opts} {packages} -deps = - poetry>=1.1.13 -commands_pre = - bash -c "python -m pip install -r <(poetry export --dev --extras=docs --without-hashes --no-interaction)" whitelist_externals = bash @@ -24,16 +21,19 @@ commands = isort src/keycloak tests docs [testenv:docs] +extras = docs commands = sphinx-build -T -E -W -b html -d _build/doctrees -D language=en ./docs/source _build/html [testenv:tests] setenv = file|tox.env +passenv = CONTAINER_HOST commands = ./test_keycloak_init.sh "pytest -vv --cov=keycloak --cov-report term-missing {posargs}" [testenv:build] -commands_pre = +deps = + poetry setenv = POETRY_VIRTUALENVS_CREATE = false commands = From db888185c3b82a8e9581fb2ee3351e698636c63b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 19:57:18 +0000 Subject: [PATCH 058/222] test: store keycloak container logs --- .github/workflows/lint.yaml | 3 +++ .gitignore | 1 + test_keycloak_init.sh | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2cade19..0f5b146 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -71,6 +71,9 @@ jobs: - name: Run tests run: | tox -e tests + - name: Keycloak logs + run: | + cat keycloak_test_logs.txt build: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 0c17079..25a3ea4 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ nosetests.xml coverage.xml *.cover .hypothesis/ +keycloak_test_logs.txt # Translations *.mo diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index bd4c30a..82afabb 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -3,8 +3,6 @@ CMD_ARGS=$1 KEYCLOAK_DOCKER_IMAGE="quay.io/keycloak/keycloak:latest" -echo "${CMD_ARGS}" - function keycloak_stop() { docker stop unittest_keycloak &> /dev/null docker rm unittest_keycloak &> /dev/null @@ -30,6 +28,7 @@ keycloak_stop # In case it did not shut down correctly last time. keycloak_start eval ${CMD_ARGS} +docker logs unittest_keycloak > keycloak_test_logs.txt RETURN_VALUE=$? exit ${RETURN_VALUE} From 9ab340f4a42ee2ef8886722873c87992376e01c7 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 19:58:20 +0000 Subject: [PATCH 059/222] feat: added flake8-docstrings and upgraded dependencies --- poetry.lock | 256 +++++++++++++++++++++++++++++++++++++++---------- pyproject.toml | 2 + tox.ini | 2 + 3 files changed, 210 insertions(+), 50 deletions(-) diff --git a/poetry.lock b/poetry.lock index cbad183..c747f94 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,20 @@ category = "main" optional = true python-versions = "*" +[[package]] +name = "argcomplete" +version = "1.12.3" +description = "Bash tab completion for argparse" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} + +[package.extras] +test = ["coverage", "flake8", "pexpect", "wheel"] + [[package]] name = "astroid" version = "2.11.6" @@ -55,7 +69,7 @@ pytz = ">=2015.7" [[package]] name = "black" -version = "22.3.0" +version = "22.6.0" description = "The uncompromising code formatter." category = "dev" optional = false @@ -66,7 +80,7 @@ click = ">=8.0.0" mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} @@ -94,11 +108,11 @@ python-versions = ">=3.6.1" [[package]] name = "charset-normalizer" -version = "2.0.12" +version = "2.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = ">=3.5.0" +python-versions = ">=3.6.0" [package.extras] unicode_backport = ["unicodedata2"] @@ -123,6 +137,26 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "commitizen" +version = "2.28.0" +description = "Python commitizen client tool" +category = "dev" +optional = false +python-versions = ">=3.6.2,<4.0.0" + +[package.dependencies] +argcomplete = ">=1.12.1,<2.0.0" +colorama = ">=0.4.1,<0.5.0" +decli = ">=0.5.2,<0.6.0" +jinja2 = ">=2.10.3" +packaging = ">=19,<22" +pyyaml = ">=3.08" +questionary = ">=1.4.0,<2.0.0" +termcolor = ">=1.1,<2.0" +tomlkit = ">=0.5.3,<1.0.0" +typing-extensions = ">=4.0.1,<5.0.0" + [[package]] name = "commonmark" version = "0.9.1" @@ -148,6 +182,14 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "decli" +version = "0.5.2" +description = "Minimal, easy-to-use, declarative cli tool" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "distlib" version = "0.3.4" @@ -205,6 +247,18 @@ mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.7.0,<2.8.0" pyflakes = ">=2.3.0,<2.4.0" +[[package]] +name = "flake8-docstrings" +version = "1.6.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + [[package]] name = "identify" version = "2.5.1" @@ -226,7 +280,7 @@ python-versions = ">=3.5" [[package]] name = "imagesize" -version = "1.3.0" +version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" category = "main" optional = true @@ -276,7 +330,7 @@ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" -optional = true +optional = false python-versions = ">=3.7" [package.dependencies] @@ -310,7 +364,7 @@ name = "markupsafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" -optional = true +optional = false python-versions = ">=3.7" [[package]] @@ -421,6 +475,17 @@ pyyaml = ">=5.1" toml = "*" virtualenv = ">=20.0.8" +[[package]] +name = "prompt-toolkit" +version = "3.0.30" +description = "Library for building powerful interactive command lines in Python" +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + [[package]] name = "py" version = "1.11.0" @@ -445,6 +510,20 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pydocstyle" +version = "6.1.1" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +snowballstemmer = "*" + +[package.extras] +toml = ["toml"] + [[package]] name = "pyflakes" version = "2.3.1" @@ -543,6 +622,20 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "questionary" +version = "1.10.0" +description = "Python library to build pretty command line user prompts ⭐️" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +prompt_toolkit = ">=2.0,<4.0" + +[package.extras] +docs = ["Sphinx (>=3.3,<4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)"] + [[package]] name = "readthedocs-sphinx-ext" version = "2.1.8" @@ -571,7 +664,7 @@ sphinx = ">=1.3.1" [[package]] name = "requests" -version = "2.28.0" +version = "2.28.1" description = "Python HTTP for Humans." category = "main" optional = false @@ -579,13 +672,13 @@ python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2.0.0,<2.1.0" +charset-normalizer = ">=2,<3" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rsa" @@ -611,7 +704,7 @@ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." category = "main" -optional = true +optional = false python-versions = "*" [[package]] @@ -752,6 +845,14 @@ python-versions = ">=3.5" lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +[[package]] +name = "termcolor" +version = "1.1.0" +description = "ANSII Color formatting for output in terminal." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "toml" version = "0.10.2" @@ -768,9 +869,17 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "tomlkit" +version = "0.11.0" +description = "Style preserving TOML library" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + [[package]] name = "tox" -version = "3.25.0" +version = "3.25.1" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -801,7 +910,7 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.2.0" +version = "4.3.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false @@ -830,7 +939,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.15.0" +version = "20.15.1" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -847,6 +956,14 @@ six = ">=1.9.0,<2" docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "wrapt" version = "1.14.1" @@ -873,13 +990,17 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "58ad1dfa1c2cdbb232bc53ceb2c1a9d0767a3db7fd8e6d0baae3e753f1c570dc" +content-hash = "ed105f41fc20e390af8eeefafd3168bb4b370d3a5135bfdec55aab7fc5d0bb3e" [metadata.files] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] +argcomplete = [ + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, +] astroid = [ {file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"}, {file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"}, @@ -897,29 +1018,29 @@ babel = [ {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] black = [ - {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, - {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, - {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, - {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, - {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, - {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, - {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, - {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, - {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, - {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, - {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, - {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, - {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, - {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, - {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, - {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, - {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, - {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, - {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, + {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, + {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, + {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, + {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, + {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, + {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, + {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, + {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, + {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, + {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, + {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, + {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, + {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, + {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, + {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, + {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, + {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, ] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, @@ -930,8 +1051,8 @@ cfgv = [ {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, + {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, + {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, ] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, @@ -941,6 +1062,10 @@ colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] +commitizen = [ + {file = "commitizen-2.28.0-py3-none-any.whl", hash = "sha256:d222f68da12a3ebcaf85c270f19eec7caacbe904349f1823deca6b5e7c2fc0f5"}, + {file = "commitizen-2.28.0.tar.gz", hash = "sha256:8510b67e4c45131ef75114aeca5fe30b4f973b2b943457cf1667177af296192e"}, +] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, @@ -988,6 +1113,10 @@ coverage = [ {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, ] +decli = [ + {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, + {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, +] distlib = [ {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, @@ -1008,6 +1137,10 @@ flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] +flake8-docstrings = [ + {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, + {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, +] identify = [ {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, @@ -1017,8 +1150,8 @@ idna = [ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] imagesize = [ - {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, - {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, @@ -1161,6 +1294,10 @@ pre-commit = [ {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, ] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, + {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, +] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -1184,6 +1321,10 @@ pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] +pydocstyle = [ + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, +] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, @@ -1247,6 +1388,10 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +questionary = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] readthedocs-sphinx-ext = [ {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, @@ -1256,8 +1401,8 @@ recommonmark = [ {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, ] requests = [ - {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, - {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] rsa = [ {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, @@ -1307,6 +1452,9 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] +termcolor = [ + {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -1315,9 +1463,13 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +tomlkit = [ + {file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"}, + {file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"}, +] tox = [ - {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"}, - {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"}, + {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, + {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, ] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, @@ -1346,8 +1498,8 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, - {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, @@ -1358,8 +1510,12 @@ urllib3 = [ {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, ] virtualenv = [ - {file = "virtualenv-20.15.0-py2.py3-none-any.whl", hash = "sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336"}, - {file = "virtualenv-20.15.0.tar.gz", hash = "sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc"}, + {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, + {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] wrapt = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, diff --git a/pyproject.toml b/pyproject.toml index ae6fc1b..0e54eaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,8 @@ pre-commit = "^2.19.0" isort = "^5.10.1" black = "^22.3.0" flake8 = "^3.5.0" +flake8-docstrings = "^1.6.0" +commitizen = "^2.28.0" [tool.poetry.extras] docs = [ diff --git a/tox.ini b/tox.ini index 0458821..3fbdd66 100644 --- a/tox.ini +++ b/tox.ini @@ -42,3 +42,5 @@ commands = [flake8] max-line-length = 99 +docstring-convention = all +ignore = D203, D213, W503 From 6f839cbc03933a6cac3355993e83bbe9cfeddc23 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 19:59:53 +0000 Subject: [PATCH 060/222] docs: added docstrings to exceptions --- src/keycloak/exceptions.py | 42 +++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/keycloak/exceptions.py b/src/keycloak/exceptions.py index 925c937..8eb69bf 100644 --- a/src/keycloak/exceptions.py +++ b/src/keycloak/exceptions.py @@ -21,12 +21,22 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Keycloak custom exeptions module.""" + import requests class KeycloakError(Exception): - def __init__(self, error_message="", response_code=None, response_body=None): + """Base class for custom Keycloak errors. + + :param error_message: The error message + :type error_message: str + :param response_code: The response status code + :type response_code: int + """ + def __init__(self, error_message="", response_code=None, response_body=None): + """Init method.""" Exception.__init__(self, error_message) self.response_code = response_code @@ -34,6 +44,7 @@ class KeycloakError(Exception): self.error_message = error_message def __str__(self): + """Str method.""" if self.response_code is not None: return "{0}: {1}".format(self.response_code, self.error_message) else: @@ -41,62 +52,91 @@ class KeycloakError(Exception): class KeycloakAuthenticationError(KeycloakError): + """Keycloak authentication error exception.""" + pass class KeycloakConnectionError(KeycloakError): + """Keycloak connection error exception.""" + pass class KeycloakOperationError(KeycloakError): + """Keycloak operation error exception.""" + pass class KeycloakDeprecationError(KeycloakError): + """Keycloak deprecation error exception.""" + pass class KeycloakGetError(KeycloakOperationError): + """Keycloak request get error exception.""" + pass class KeycloakPostError(KeycloakOperationError): + """Keycloak request post error exception.""" + pass class KeycloakPutError(KeycloakOperationError): + """Keycloak request put error exception.""" + pass class KeycloakDeleteError(KeycloakOperationError): + """Keycloak request delete error exception.""" + pass class KeycloakSecretNotFound(KeycloakOperationError): + """Keycloak secret not found exception.""" + pass class KeycloakRPTNotFound(KeycloakOperationError): + """Keycloak RPT not found exception.""" + pass class KeycloakAuthorizationConfigError(KeycloakOperationError): + """Keycloak authorization config exception.""" + pass class KeycloakInvalidTokenError(KeycloakOperationError): + """Keycloak invalid token exception.""" + pass class KeycloakPermissionFormatError(KeycloakOperationError): + """Keycloak permission format exception.""" + pass class PermissionDefinitionError(Exception): + """Keycloak permission definition exception.""" + pass def raise_error_from_response(response, error, expected_codes=None, skip_exists=False): + """Raise an exception for the response.""" if expected_codes is None: expected_codes = [200, 201, 204] From bead0aff2bf5fdd7c966a0cc28fd1910c2b3e9ff Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 20:00:46 +0000 Subject: [PATCH 061/222] fix: raise correct exceptions --- src/keycloak/keycloak_admin.py | 2 +- src/keycloak/keycloak_openid.py | 27 ++++++++------------------- tests/test_keycloak_admin.py | 2 +- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 44e9c3b..b2c1de4 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2736,7 +2736,7 @@ class KeycloakAdmin: else: try: self.token = self.keycloak_openid.refresh_token(refresh_token) - except KeycloakGetError as e: + except KeycloakPostError as e: list_errors = [ b"Refresh token expired", b"Token is not active", diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 3e045bc..ede9a3c 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -229,7 +229,7 @@ class KeycloakOpenID: payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def refresh_token(self, refresh_token, grant_type=["refresh_token"]): """ @@ -252,7 +252,7 @@ class KeycloakOpenID: } payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: """ @@ -276,7 +276,7 @@ class KeycloakOpenID: } payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def userinfo(self, token): """ @@ -288,12 +288,9 @@ class KeycloakOpenID: :param token: :return: """ - self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name} - data_raw = self.connection.raw_get(URL_USERINFO.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) def logout(self, refresh_token): @@ -304,11 +301,9 @@ class KeycloakOpenID: """ params_path = {"realm-name": self.realm_name} payload = {"client_id": self.client_id, "refresh_token": refresh_token} - payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_LOGOUT.format(**params_path), data=payload) - - return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def certs(self): """ @@ -367,7 +362,6 @@ class KeycloakOpenID: :return: """ params_path = {"realm-name": self.realm_name} - payload = {"client_id": self.client_id, "token": token} if token_type_hint == "requesting_party_token": @@ -380,8 +374,7 @@ class KeycloakOpenID: payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_INTROSPECT.format(**params_path), data=payload) - - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakPostError) def decode_token(self, token, key, algorithms=["RS256"], **kwargs): """ @@ -399,7 +392,6 @@ class KeycloakOpenID: :param algorithms: :return: """ - return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) def load_authorization_config(self, path): @@ -409,10 +401,10 @@ class KeycloakOpenID: :param path: settings file (json) :return: """ - authorization_file = open(path, "r") - authorization_json = json.loads(authorization_file.read()) + with open(path, "r") as fp: + authorization_json = json.load(fp) + self.authorization.load_config(authorization_json) - authorization_file.close() def get_policies(self, token, method_token_info="introspect", **kwargs): """ @@ -421,7 +413,6 @@ class KeycloakOpenID: :param token: user token :return: policies list """ - if not self.authorization.policies: raise KeycloakAuthorizationConfigError( "Keycloak settings not found. Load Authorization Keycloak settings." @@ -455,7 +446,6 @@ class KeycloakOpenID: :param kwargs: parameters for decode :return: permissions list """ - if not self.authorization.policies: raise KeycloakAuthorizationConfigError( "Keycloak settings not found. Load Authorization Keycloak settings." @@ -493,7 +483,6 @@ class KeycloakOpenID: :param permissions: list of uma permissions list(resource:scope) requested by the user :return: permissions list """ - permission = build_permission_param(permissions) params_path = {"realm-name": self.realm_name} diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 6f33e03..e62bdda 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1731,7 +1731,7 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): verify=admin.verify, ) admin.token["refresh_token"] = "bad" - with pytest.raises(KeycloakGetError) as err: + with pytest.raises(KeycloakPostError) as err: admin.get_realm(realm_name="test-refresh") assert err.match( '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'' From 17bfad5ec0b8a6bad887c3964df3d427f8378b47 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 20:01:00 +0000 Subject: [PATCH 062/222] test: added a license test --- tests/test_license.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/test_license.py diff --git a/tests/test_license.py b/tests/test_license.py new file mode 100644 index 0000000..3c6b10e --- /dev/null +++ b/tests/test_license.py @@ -0,0 +1,14 @@ +"""Tests for license.""" +import os + + +def test_license_present(): + """Test that the MIT license is present in the header of each module file.""" + for path, _, files in os.walk("src/keycloak"): + for _file in files: + if _file.endswith(".py"): + with open(os.path.join(path, _file), "r") as fp: + content = fp.read() + assert content.startswith( + "# -*- coding: utf-8 -*-\n#\n# The MIT License (MIT)\n#\n#" + ) From 65a4af15503d483b7b315b7219d0ed36c50e74e7 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 3 Jul 2022 20:02:10 +0000 Subject: [PATCH 063/222] test: added auth_url and token tests, more structure to fixtures --- tests/conftest.py | 85 ++++++++++++++++++++++++++++++++++- tests/test_keycloak_openid.py | 58 ++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index ada9820..6023e51 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +"""Fixtures for tests.""" + import os import uuid @@ -7,6 +9,18 @@ from keycloak import KeycloakAdmin, KeycloakOpenID class KeycloakTestEnv(object): + """Wrapper for test Keycloak connection configuration. + + :param host: Hostname + :type host: str + :param port: Port + :type port: str + :param username: Admin username + :type username: str + :param password: Admin password + :type password: str + """ + def __init__( self, host: str = os.environ["KEYCLOAK_HOST"], @@ -14,6 +28,7 @@ class KeycloakTestEnv(object): username: str = os.environ["KEYCLOAK_ADMIN"], password: str = os.environ["KEYCLOAK_ADMIN_PASSWORD"], ): + """Init method.""" self.KEYCLOAK_HOST = host self.KEYCLOAK_PORT = port self.KEYCLOAK_ADMIN = username @@ -21,44 +36,54 @@ class KeycloakTestEnv(object): @property def KEYCLOAK_HOST(self): + """Hostname getter.""" return self._KEYCLOAK_HOST @KEYCLOAK_HOST.setter def KEYCLOAK_HOST(self, value: str): + """Hostname setter.""" self._KEYCLOAK_HOST = value @property def KEYCLOAK_PORT(self): + """Port getter.""" return self._KEYCLOAK_PORT @KEYCLOAK_PORT.setter def KEYCLOAK_PORT(self, value: str): + """Port setter.""" self._KEYCLOAK_PORT = value @property def KEYCLOAK_ADMIN(self): + """Admin username getter.""" return self._KEYCLOAK_ADMIN @KEYCLOAK_ADMIN.setter def KEYCLOAK_ADMIN(self, value: str): + """Admin username setter.""" self._KEYCLOAK_ADMIN = value @property def KEYCLOAK_ADMIN_PASSWORD(self): + """Admin password getter.""" return self._KEYCLOAK_ADMIN_PASSWORD @KEYCLOAK_ADMIN_PASSWORD.setter def KEYCLOAK_ADMIN_PASSWORD(self, value: str): + """Admin password setter.""" self._KEYCLOAK_ADMIN_PASSWORD = value @pytest.fixture def env(): + """Fixture for getting the test environment configuration object.""" return KeycloakTestEnv() @pytest.fixture def admin(env: KeycloakTestEnv): + """Fixture for initialized KeycloakAdmin class.""" return KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -68,11 +93,20 @@ def admin(env: KeycloakTestEnv): @pytest.fixture def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + """Fixture for initialized KeycloakOpenID class.""" # Set the realm admin.realm_name = realm # Create client client = str(uuid.uuid4()) - client_id = admin.create_client(payload={"name": client, "clientId": client}) + client_id = admin.create_client( + payload={ + "name": client, + "clientId": client, + "enabled": True, + "publicClient": True, + "protocol": "openid-connect", + } + ) # Return OID yield KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -83,16 +117,61 @@ def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): admin.delete_client(client_id=client_id) +@pytest.fixture +def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + # Set the realm + admin.realm_name = realm + # Create client + client = str(uuid.uuid4()) + client_id = admin.create_client( + payload={ + "name": client, + "clientId": client, + "enabled": True, + "publicClient": True, + "protocol": "openid-connect", + } + ) + # Create user + username = str(uuid.uuid4()) + password = str(uuid.uuid4()) + user_id = admin.create_user( + payload={ + "username": username, + "email": f"{username}@test.test", + "enabled": True, + "credentials": [{"type": "password", "value": password}], + } + ) + + yield ( + KeycloakOpenID( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + realm_name=realm, + client_id=client, + ), + username, + password, + ) + + # Cleanup + admin.delete_client(client_id=client_id) + admin.delete_user(user_id=user_id) + + @pytest.fixture def realm(admin: KeycloakAdmin) -> str: + """Fixture for a new random realm.""" realm_name = str(uuid.uuid4()) - admin.create_realm(payload={"realm": realm_name}) + admin.create_realm(payload={"realm": realm_name, "enabled": True}) yield realm_name admin.delete_realm(realm_name=realm_name) @pytest.fixture def user(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random user.""" admin.realm_name = realm username = str(uuid.uuid4()) user_id = admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) @@ -102,6 +181,7 @@ def user(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def group(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random group.""" admin.realm_name = realm group_name = str(uuid.uuid4()) group_id = admin.create_group(payload={"name": group_name}) @@ -111,6 +191,7 @@ def group(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random client.""" admin.realm_name = realm client = str(uuid.uuid4()) client_id = admin.create_client(payload={"name": client, "clientId": client}) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 53a76dd..f01b91c 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -1,9 +1,13 @@ +"""Test module for KeycloakOpenID.""" +from unittest import mock + from keycloak.authorization import Authorization from keycloak.connection import ConnectionManager from keycloak.keycloak_openid import KeycloakOpenID def test_keycloak_openid_init(env): + """Test KeycloakOpenId's init method.""" oid = KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name="master", @@ -18,6 +22,7 @@ def test_keycloak_openid_init(env): def test_well_known(oid: KeycloakOpenID): + """Test the well_known method.""" res = oid.well_known() assert res is not None assert res != dict() @@ -77,3 +82,56 @@ def test_well_known(oid: KeycloakOpenID): "userinfo_signing_alg_values_supported", ]: assert key in res + + +def test_auth_url(env, oid: KeycloakOpenID): + """Test the auth_url method.""" + res = oid.auth_url(redirect_uri="http://test.test/*") + assert ( + res + == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}/realms/{oid.realm_name}" + + f"/protocol/openid-connect/auth?client_id={oid.client_id}&response_type=code" + + "&redirect_uri=http://test.test/*" + ) + + +def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): + """Test the token method.""" + oid, username, password = oid_with_credentials + token = oid.token(username=username, password=password) + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + } + + # Test with dummy totp + token = oid.token(username=username, password=password, totp="123456") + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + } + + # Test with extra param + token = oid.token(username=username, password=password, extra_param="foo") + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + } From 81b3cc80db831d9ffb321bd4984fa2c082363fb6 Mon Sep 17 00:00:00 2001 From: Fredrik Lindner Date: Mon, 4 Jul 2022 14:28:22 +0200 Subject: [PATCH 064/222] docs: add timeout to docstring --- src/keycloak/keycloak_admin.py | 1 + src/keycloak/keycloak_openid.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 44e9c3b..4b85ead 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -57,6 +57,7 @@ class KeycloakAdmin: :param user_realm_name: The realm name of the user, if different from realm_name :param auto_refresh_token: list of methods that allows automatic token refresh. Ex: ['get', 'put', 'post', 'delete'] + :param timeout: connection timeout in seconds """ PAGE_SIZE = 100 diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 7216b5d..ad608d0 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -62,6 +62,7 @@ class KeycloakOpenID: :param verify: True if want check connection SSL :param custom_headers: dict of custom header to pass to each HTML request :param proxies: dict of proxies to sent the request by. + :param timeout: connection timeout in seconds """ def __init__( From b0dcd5f431a953183bbb2dfcc2f1de9f59881550 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 10 Jul 2022 11:31:50 +0200 Subject: [PATCH 065/222] docs: fix readthedocs build --- .readthedocs.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 7aa6ce5..4379fbf 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,7 +4,11 @@ build: os: "ubuntu-20.04" tools: python: "3.10" - -python: - install: - - requirements: docs-requirements.txt + jobs: + pre_create_environment: + - asdf plugin add poetry + - asdf install poetry latest + - asdf global poetry latest + - poetry config virtualenvs.create false + post_install: + - poetry install -E docs From b10c161ed8f2b786d0bce991748736dd8cf8ad3b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Mon, 11 Jul 2022 13:23:06 +0000 Subject: [PATCH 066/222] test: added more openid tests --- src/keycloak/keycloak_openid.py | 2 +- test_keycloak_init.sh | 4 +- tests/conftest.py | 55 +++++++++++++++- tests/test_keycloak_openid.py | 107 +++++++++++++++++++++++++++++++- 4 files changed, 161 insertions(+), 7 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index ede9a3c..fa04e4d 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -346,7 +346,7 @@ class KeycloakOpenID: if data_raw.status_code == 404: return raise_error_from_response(data_raw, KeycloakDeprecationError) - return raise_error_from_response(data_raw, KeycloakGetError) + return raise_error_from_response(data_raw, KeycloakGetError) # pragma: no cover def introspect(self, token, rpt=None, token_type_hint=None): """ diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index 82afabb..e9c6823 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -10,7 +10,7 @@ function keycloak_stop() { function keycloak_start() { echo "Starting keycloak docker container" - docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev + docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -e KC_FEATURES="token-exchange" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev SECONDS=0 until curl --silent --output /dev/null localhost:$KEYCLOAK_PORT; do sleep 5; @@ -28,7 +28,7 @@ keycloak_stop # In case it did not shut down correctly last time. keycloak_start eval ${CMD_ARGS} -docker logs unittest_keycloak > keycloak_test_logs.txt RETURN_VALUE=$? +docker logs unittest_keycloak > keycloak_test_logs.txt exit ${RETURN_VALUE} diff --git a/tests/conftest.py b/tests/conftest.py index 6023e51..47c9854 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -124,13 +124,65 @@ def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin) admin.realm_name = realm # Create client client = str(uuid.uuid4()) + secret = str(uuid.uuid4()) client_id = admin.create_client( payload={ "name": client, "clientId": client, "enabled": True, - "publicClient": True, + "publicClient": False, + "protocol": "openid-connect", + "secret": secret, + "clientAuthenticatorType": "client-secret", + } + ) + # Create user + username = str(uuid.uuid4()) + password = str(uuid.uuid4()) + user_id = admin.create_user( + payload={ + "username": username, + "email": f"{username}@test.test", + "enabled": True, + "credentials": [{"type": "password", "value": password}], + } + ) + + yield ( + KeycloakOpenID( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + realm_name=realm, + client_id=client, + client_secret_key=secret, + ), + username, + password, + ) + + # Cleanup + admin.delete_client(client_id=client_id) + admin.delete_user(user_id=user_id) + + +@pytest.fixture +def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + # Set the realm + admin.realm_name = realm + # Create client + client = str(uuid.uuid4()) + secret = str(uuid.uuid4()) + client_id = admin.create_client( + payload={ + "name": client, + "clientId": client, + "enabled": True, + "publicClient": False, "protocol": "openid-connect", + "secret": secret, + "clientAuthenticatorType": "client-secret", + "authorizationServicesEnabled": True, + "serviceAccountsEnabled": True, } ) # Create user @@ -150,6 +202,7 @@ def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin) server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name=realm, client_id=client, + client_secret_key=secret, ), username, password, diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index f01b91c..9ed2b88 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -1,8 +1,12 @@ """Test module for KeycloakOpenID.""" from unittest import mock +import pytest + from keycloak.authorization import Authorization from keycloak.connection import ConnectionManager +from keycloak.exceptions import KeycloakDeprecationError, KeycloakRPTNotFound +from keycloak.keycloak_admin import KeycloakAdmin from keycloak.keycloak_openid import KeycloakOpenID @@ -105,7 +109,7 @@ def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, - "scope": "profile email", + "scope": mock.ANY, "session_state": mock.ANY, "token_type": "Bearer", } @@ -118,7 +122,7 @@ def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, - "scope": "profile email", + "scope": mock.ANY, "session_state": mock.ANY, "token_type": "Bearer", } @@ -131,7 +135,104 @@ def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, - "scope": "profile email", + "scope": mock.ANY, "session_state": mock.ANY, "token_type": "Bearer", } + + +def test_exchange_token( + oid_with_credentials: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin +): + """Test the exchange token method.""" + # Verify existing user + oid, username, password = oid_with_credentials + + # Allow impersonation + admin.realm_name = oid.realm_name + admin.assign_client_role( + user_id=admin.get_user_id(username=username), + client_id=admin.get_client_id(client_name="realm-management"), + roles=[ + admin.get_client_role( + client_id=admin.get_client_id(client_name="realm-management"), + role_name="impersonation", + ) + ], + ) + + token = oid.token(username=username, password=password) + assert oid.userinfo(token=token["access_token"]) == { + "email": f"{username}@test.test", + "email_verified": False, + "preferred_username": username, + "sub": mock.ANY, + } + + # Exchange token with the new user + new_token = oid.exchange_token( + token=token["access_token"], + client_id=oid.client_id, + audience=oid.client_id, + subject=username, + ) + assert oid.userinfo(token=new_token["access_token"]) == { + "email": f"{username}@test.test", + "email_verified": False, + "preferred_username": username, + "sub": mock.ANY, + } + assert token != new_token + + +def test_certs(oid: KeycloakOpenID): + """Test certificates.""" + assert len(oid.certs()["keys"]) == 2 + + +def test_public_key(oid: KeycloakOpenID): + """Test public key.""" + assert oid.public_key() is not None + + +def test_entitlement( + oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin +): + """Test entitlement.""" + oid, username, password = oid_with_credentials_authz + token = oid.token(username=username, password=password) + resource_server_id = admin.get_client_authz_resources( + client_id=admin.get_client_id(oid.client_id) + )[0]["_id"] + + with pytest.raises(KeycloakDeprecationError): + oid.entitlement(token=token["access_token"], resource_server_id=resource_server_id) + + +def test_introspect(oid_with_credentials: tuple[KeycloakOpenID, str, str]): + """Test introspect.""" + oid, username, password = oid_with_credentials + token = oid.token(username=username, password=password) + + assert oid.introspect(token=token["access_token"])["active"] + assert oid.introspect( + token=token["access_token"], rpt="some", token_type_hint="requesting_party_token" + ) == {"active": False} + + with pytest.raises(KeycloakRPTNotFound): + oid.introspect(token=token["access_token"], token_type_hint="requesting_party_token") + + +def test_decode_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): + """Test decode token.""" + oid, username, password = oid_with_credentials + token = oid.token(username=username, password=password) + + assert ( + oid.decode_token( + token=token["access_token"], + key="-----BEGIN PUBLIC KEY-----\n" + oid.public_key() + "\n-----END PUBLIC KEY-----", + options={"verify_aud": False}, + )["preferred_username"] + == username + ) From 5e6c775735d9874a3b9b0df89bd30faeb8bc7e3e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 08:00:35 +0000 Subject: [PATCH 067/222] style: fixed docstrings everywhere --- poetry.lock | 44 +- src/keycloak/__init__.py | 2 + src/keycloak/authorization/__init__.py | 10 +- src/keycloak/authorization/permission.py | 14 +- src/keycloak/authorization/policy.py | 20 +- src/keycloak/authorization/role.py | 9 +- src/keycloak/connection.py | 15 +- src/keycloak/keycloak_admin.py | 609 +++++++++-------------- src/keycloak/keycloak_openid.py | 75 +-- src/keycloak/uma_permissions.py | 42 +- src/keycloak/urls_patterns.py | 2 + tests/__init__.py | 1 + tests/test_keycloak_admin.py | 29 ++ tests/test_uma_permissions.py | 28 ++ tests/test_urls_patterns.py | 3 +- 15 files changed, 450 insertions(+), 453 deletions(-) diff --git a/poetry.lock b/poetry.lock index c747f94..06cdcc1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,7 +22,7 @@ test = ["coverage", "flake8", "pexpect", "wheel"] [[package]] name = "astroid" -version = "2.11.6" +version = "2.11.7" description = "An abstract syntax tree for Python with inference support." category = "main" optional = true @@ -36,7 +36,7 @@ wrapt = ">=1.11,<2" [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false @@ -208,7 +208,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "ecdsa" -version = "0.17.0" +version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" category = "main" optional = false @@ -460,7 +460,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.19.0" +version = "2.20.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -871,7 +871,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.0" +version = "0.11.1" description = "Style preserving TOML library" category = "dev" optional = false @@ -926,11 +926,11 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.9" +version = "1.26.10" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] @@ -1001,14 +1001,8 @@ argcomplete = [ {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] -astroid = [ - {file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"}, - {file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] +astroid = [] +atomicwrites = [] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -1125,10 +1119,7 @@ docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] -ecdsa = [ - {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, - {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"}, -] +ecdsa = [] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, @@ -1290,10 +1281,7 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [ - {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, - {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, -] +pre-commit = [] prompt-toolkit = [ {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, @@ -1463,10 +1451,7 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [ - {file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"}, - {file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"}, -] +tomlkit = [] tox = [ {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, @@ -1505,10 +1490,7 @@ unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, ] -urllib3 = [ - {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, - {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, -] +urllib3 = [] virtualenv = [ {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, diff --git a/src/keycloak/__init__.py b/src/keycloak/__init__.py index 2c7f70f..694e53d 100644 --- a/src/keycloak/__init__.py +++ b/src/keycloak/__init__.py @@ -21,6 +21,8 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Python-Keycloak library.""" + from ._version import __version__ from .connection import ConnectionManager from .exceptions import ( diff --git a/src/keycloak/authorization/__init__.py b/src/keycloak/authorization/__init__.py index 789656d..fddd551 100644 --- a/src/keycloak/authorization/__init__.py +++ b/src/keycloak/authorization/__init__.py @@ -21,6 +21,8 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Authorization module.""" + import ast import json @@ -30,18 +32,19 @@ from .role import Role class Authorization: - """ - Keycloak Authorization (policies, roles, scopes and resources). + """Keycloak Authorization (policies, roles, scopes and resources). https://keycloak.gitbooks.io/documentation/authorization_services/index.html """ def __init__(self): + """Init method.""" self.policies = {} @property def policies(self): + """Get policies.""" return self._policies @policies.setter @@ -49,8 +52,7 @@ class Authorization: self._policies = value def load_config(self, data): - """ - Load policies, roles and permissions (scope/resources). + """Load policies, roles and permissions (scope/resources). :param data: keycloak authorization data (dict) :returns: None diff --git a/src/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py index a200afe..a444f83 100644 --- a/src/keycloak/authorization/permission.py +++ b/src/keycloak/authorization/permission.py @@ -21,9 +21,12 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Keycloak authorization Permission module.""" + class Permission: - """ + """Base permission class. + Consider this simple and very common permission: A permission associates the object being protected with the policies that must be evaluated to @@ -45,6 +48,7 @@ class Permission: """ def __init__(self, name, type, logic, decision_strategy): + """Init method.""" self._name = name self._type = type self._logic = logic @@ -53,13 +57,16 @@ class Permission: self._scopes = [] def __repr__(self): + """Repr method.""" return "" % (self.name, self.type) def __str__(self): + """Str method.""" return "Permission: %s (%s)" % (self.name, self.type) @property def name(self): + """Get name.""" return self._name @name.setter @@ -68,6 +75,7 @@ class Permission: @property def type(self): + """Get type.""" return self._type @type.setter @@ -76,6 +84,7 @@ class Permission: @property def logic(self): + """Get logic.""" return self._logic @logic.setter @@ -84,6 +93,7 @@ class Permission: @property def decision_strategy(self): + """Get decision strategy.""" return self._decision_strategy @decision_strategy.setter @@ -92,6 +102,7 @@ class Permission: @property def resources(self): + """Get resources.""" return self._resources @resources.setter @@ -100,6 +111,7 @@ class Permission: @property def scopes(self): + """Get scopes.""" return self._scopes @scopes.setter diff --git a/src/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py index 4014b7a..6b558d8 100644 --- a/src/keycloak/authorization/policy.py +++ b/src/keycloak/authorization/policy.py @@ -21,11 +21,14 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Keycloak authorization Policy module.""" + from ..exceptions import KeycloakAuthorizationConfigError class Policy: - """ + """Base policy class. + A policy defines the conditions that must be satisfied to grant access to an object. Unlike permissions, you do not specify the object being protected but rather the conditions that must be satisfied for access to a given object (for example, resource, scope, or both). @@ -39,6 +42,7 @@ class Policy: """ def __init__(self, name, type, logic, decision_strategy): + """Init method.""" self._name = name self._type = type self._logic = logic @@ -47,13 +51,16 @@ class Policy: self._permissions = [] def __repr__(self): + """Repr method.""" return "" % (self.name, self.type) def __str__(self): + """Str method.""" return "Policy: %s (%s)" % (self.name, self.type) @property def name(self): + """Get name.""" return self._name @name.setter @@ -62,6 +69,7 @@ class Policy: @property def type(self): + """Get type.""" return self._type @type.setter @@ -70,6 +78,7 @@ class Policy: @property def logic(self): + """Get logic.""" return self._logic @logic.setter @@ -78,6 +87,7 @@ class Policy: @property def decision_strategy(self): + """Get decision strategy.""" return self._decision_strategy @decision_strategy.setter @@ -86,15 +96,16 @@ class Policy: @property def roles(self): + """Get roles.""" return self._roles @property def permissions(self): + """Get permissions.""" return self._permissions def add_role(self, role): - """ - Add keycloak role in policy. + """Add keycloak role in policy. :param role: keycloak role. :return: @@ -106,8 +117,7 @@ class Policy: self._roles.append(role) def add_permission(self, permission): - """ - Add keycloak permission in policy. + """Add keycloak permission in policy. :param permission: keycloak permission. :return: diff --git a/src/keycloak/authorization/role.py b/src/keycloak/authorization/role.py index 3ff06dd..05da243 100644 --- a/src/keycloak/authorization/role.py +++ b/src/keycloak/authorization/role.py @@ -21,25 +21,30 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""The authorization Role module.""" + class Role: - """ + """Authorization Role base class. + Roles identify a type or category of user. Admin, user, manager, and employee are all typical roles that may exist in an organization. https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html - """ def __init__(self, name, required=False): + """Init method.""" self.name = name self.required = required @property def get_name(self): + """Get name.""" return self.name def __eq__(self, other): + """Eq method.""" if isinstance(other, str): return self.name == other return NotImplemented diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index 0757377..361d95d 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -21,6 +21,8 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Connection manager module.""" + try: from urllib.parse import urljoin except ImportError: @@ -33,8 +35,7 @@ from .exceptions import KeycloakConnectionError class ConnectionManager(object): - """ - Represents a simple server connection. + """Represents a simple server connection. :param base_url: (str) The server URL. :param headers: (dict) The header parameters of the requests to the server. @@ -44,6 +45,7 @@ class ConnectionManager(object): """ def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): + """Init method.""" self._base_url = base_url self._headers = headers self._timeout = timeout @@ -66,6 +68,7 @@ class ConnectionManager(object): self._s.proxies.update(proxies) def __del__(self): + """Del method.""" self._s.close() @property @@ -75,7 +78,6 @@ class ConnectionManager(object): @base_url.setter def base_url(self, value): - """ """ self._base_url = value @property @@ -85,7 +87,6 @@ class ConnectionManager(object): @timeout.setter def timeout(self, value): - """ """ self._timeout = value @property @@ -95,7 +96,6 @@ class ConnectionManager(object): @verify.setter def verify(self, value): - """ """ self._verify = value @property @@ -105,12 +105,10 @@ class ConnectionManager(object): @headers.setter def headers(self, value): - """ """ self._headers = value def param_headers(self, key): - """ - Return a specific header parameter. + """Return a specific header parameter. :param key: (str) Header parameters key. :returns: If the header parameters exist, return its value. @@ -151,7 +149,6 @@ class ConnectionManager(object): :returns: Response the request. :raises: HttpError Can't connect to server. """ - try: return self._s.get( urljoin(self.base_url, path), diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index b2c1de4..0825730 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -24,6 +24,8 @@ # Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the # internal Keycloak server ID, usually a uuid string +"""The keycloak admin module.""" + import json from builtins import isinstance from typing import Iterable @@ -41,8 +43,7 @@ from .keycloak_openid import KeycloakOpenID class KeycloakAdmin: - """ - Keycloak Admin client. + """Keycloak Admin client. :param server_url: Keycloak server url :param username: admin username @@ -90,6 +91,7 @@ class KeycloakAdmin: auto_refresh_token=None, timeout=60, ): + """Init method.""" self.server_url = server_url self.username = username self.password = password @@ -108,6 +110,7 @@ class KeycloakAdmin: @property def server_url(self): + """Get server url.""" return self._server_url @server_url.setter @@ -116,6 +119,7 @@ class KeycloakAdmin: @property def realm_name(self): + """Get realm name.""" return self._realm_name @realm_name.setter @@ -124,6 +128,7 @@ class KeycloakAdmin: @property def connection(self): + """Get connection.""" return self._connection @connection.setter @@ -132,6 +137,7 @@ class KeycloakAdmin: @property def client_id(self): + """Get client id.""" return self._client_id @client_id.setter @@ -140,6 +146,7 @@ class KeycloakAdmin: @property def client_secret_key(self): + """Get client secret key.""" return self._client_secret_key @client_secret_key.setter @@ -148,6 +155,7 @@ class KeycloakAdmin: @property def verify(self): + """Get verify.""" return self._verify @verify.setter @@ -156,6 +164,7 @@ class KeycloakAdmin: @property def username(self): + """Get username.""" return self._username @username.setter @@ -164,6 +173,7 @@ class KeycloakAdmin: @property def password(self): + """Get password.""" return self._password @password.setter @@ -172,6 +182,7 @@ class KeycloakAdmin: @property def totp(self): + """Get totp.""" return self._totp @totp.setter @@ -180,6 +191,7 @@ class KeycloakAdmin: @property def token(self): + """Get token.""" return self._token @token.setter @@ -188,10 +200,12 @@ class KeycloakAdmin: @property def auto_refresh_token(self): + """Get auto refresh token.""" return self._auto_refresh_token @property def user_realm_name(self): + """Get user realm name.""" return self._user_realm_name @user_realm_name.setter @@ -200,6 +214,7 @@ class KeycloakAdmin: @property def custom_headers(self): + """Get custom headers.""" return self._custom_headers @custom_headers.setter @@ -223,7 +238,9 @@ class KeycloakAdmin: self._auto_refresh_token = value def __fetch_all(self, url, query=None): - """Wrapper function to paginate GET requests + """Paginate over get requests. + + Wrapper function to paginate GET requests. :param url: The url on which the query is executed :param query: Existing query parameters (optional) @@ -258,8 +275,9 @@ class KeycloakAdmin: return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError) def import_realm(self, payload): - """ - Import a new realm from a RealmRepresentation. Realm name must be unique. + """Import a new realm from a RealmRepresentation. + + Realm name must be unique. RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation @@ -268,13 +286,11 @@ class KeycloakAdmin: :return: RealmRepresentation """ - data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def export_realm(self, export_clients=False, export_groups_and_role=False): - """ - Export the realm configurations in the json format + """Export the realm configurations in the json format. RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport @@ -295,8 +311,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_realms(self): - """ - Lists all realms in Keycloak deployment + """List all realms in Keycloak deployment. :return: realms list """ @@ -304,8 +319,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_realm(self, realm_name): - """ - Get a specific realm. + """Get a specific realm. RealmRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation @@ -318,8 +332,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def create_realm(self, payload, skip_exists=False): - """ - Create a realm + """Create a realm. RealmRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation @@ -328,15 +341,15 @@ class KeycloakAdmin: :param skip_exists: Skip if Realm already exist. :return: Keycloak server response (RealmRepresentation) """ - data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response( data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) def update_realm(self, realm_name, payload): - """ - Update a realm. This wil only update top level attributes and will ignore any user, + """Update a realm. + + This wil only update top level attributes and will ignore any user, role, or client information in the payload. RealmRepresentation: @@ -346,7 +359,6 @@ class KeycloakAdmin: :param payload: RealmRepresentation :return: Http response """ - params_path = {"realm-name": realm_name} data_raw = self.raw_put( urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload) @@ -354,19 +366,18 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm(self, realm_name): - """ - Delete a realm + """Delete a realm. :param realm_name: Realm name (not the realm id) :return: Http response """ - params_path = {"realm-name": realm_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_users(self, query=None): - """ + """Get all users. + Return a list of users, filtered according to query parameters UserRepresentation @@ -385,8 +396,7 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def create_idp(self, payload): - """ - Create an ID Provider, + """Create an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation @@ -400,8 +410,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_idp(self, idp_alias, payload): - """ - Update an ID Provider + """Update an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource @@ -416,8 +425,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_idp(self, idp_alias, payload): - """ - Create an ID Provider, + """Create an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation @@ -432,8 +440,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_mapper_in_idp(self, idp_alias, mapper_id, payload): - """ - Update an IdP mapper + """Update an IdP mapper. IdentityProviderMapperRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update @@ -457,7 +464,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_idp_mappers(self, idp_alias): - """ + """Get IDP mappers. + Returns a list of ID Providers mappers IdentityProviderMapperRepresentation @@ -471,7 +479,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_idps(self): - """ + """Get IDPs. + Returns a list of ID Providers, IdentityProviderRepresentation @@ -484,8 +493,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_idp(self, idp_alias): - """ - Deletes ID Provider, + """Delete an ID Provider. :param: idp_alias: idp alias name """ @@ -494,8 +502,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_user(self, payload, exist_ok=False): - """ - Create a new user. Username must be unique + """Create a new user. + + Username must be unique UserRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation @@ -522,8 +531,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def users_count(self, query=None): - """ - User counter + """Count users. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource @@ -537,8 +545,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_id(self, username): - """ - Get internal keycloak user id from username + """Get internal keycloak user id from username. + This is required for further actions against this user. UserRepresentation @@ -553,8 +561,7 @@ class KeycloakAdmin: return next((user["id"] for user in users if user["username"] == lower_user_name), None) def get_user(self, user_id): - """ - Get representation of the user + """Get representation of the user. :param user_id: User id @@ -568,7 +575,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_groups(self, user_id): - """ + """Get user groups. + Returns a list of groups of which the user is a member :param user_id: User id @@ -580,8 +588,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_user(self, user_id, payload): - """ - Update the user + """Update the user. :param user_id: User id :param payload: UserRepresentation @@ -595,8 +602,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_user(self, user_id): - """ - Delete the user + """Delete the user. :param user_id: User id @@ -607,8 +613,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def set_user_password(self, user_id, password, temporary=True): - """ - Set up a password for the user. If temporary is True, the user will have to reset + """Set up a password for the user. + + If temporary is True, the user will have to reset the temporary password next time they log in. https://www.keycloak.org/docs-api/18.0/rest-api/#_users_resource @@ -628,7 +635,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_credentials(self, user_id): - """ + """Get user credentials. + Returns a list of credential belonging to the user. CredentialRepresentation @@ -642,8 +650,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_credential(self, user_id, credential_id): - """ - Delete credential of the user. + """Delete credential of the user. CredentialRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation @@ -661,8 +668,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError) def user_logout(self, user_id): - """ - Logs out user. + """Log out the user. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout @@ -676,8 +682,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def user_consents(self, user_id): - """ - Get consents granted by the user + """Get consents granted by the user. UserConsentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation @@ -690,7 +695,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_social_logins(self, user_id): - """ + """Get user social logins. + Returns a list of federated identities/social logins of which the user has been associated with :param user_id: User id @@ -703,9 +709,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username): + """Add a federated identity / social login provider to the user. - """ - Add a federated identity / social login provider to the user :param user_id: User id :param provider_id: Social login provider id :param provider_userid: userid specified by the provider @@ -725,9 +730,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204]) def delete_user_social_login(self, user_id, provider_id): + """Delete a federated identity / social login provider from the user. - """ - Delete a federated identity / social login provider from the user :param user_id: User id :param provider_id: Social login provider id :return: @@ -741,9 +745,9 @@ class KeycloakAdmin: def send_update_account( self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None ): - """ - Send an update account email to the user. An email contains a - link the user can click to perform a set of required actions. + """Send an update account email to the user. + + An email contains a link the user can click to perform a set of required actions. :param user_id: User id :param payload: A list of actions for the user to complete @@ -763,9 +767,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def send_verify_email(self, user_id, client_id=None, redirect_uri=None): - """ - Send a update account email to the user An email contains a - link the user can click to perform a set of required actions. + """Send a update account email to the user. + + An email contains a link the user can click to perform a set of required actions. :param user_id: User id :param client_id: Client id (optional) @@ -783,8 +787,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def get_sessions(self, user_id): - """ - Get sessions associated with the user + """Get sessions associated with the user. :param user_id: id of user @@ -798,8 +801,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_server_info(self): - """ - Get themes, social providers, auth providers, and event listeners available on this server + """Get themes, social providers, auth providers, and event listeners available on this server. ServerInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation @@ -810,7 +812,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_groups(self, query=None): - """ + """Get groups. + Returns a list of groups belonging to the realm GroupRepresentation @@ -828,8 +831,9 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def get_group(self, group_id): - """ - Get group by id. Returns full group details + """Get group by id. + + Returns full group details GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation @@ -842,7 +846,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_subgroups(self, group, path): - """ + """Get subgroups. + Utility function to iterate through nested group structures GroupRepresentation @@ -853,7 +858,6 @@ class KeycloakAdmin: :return: Keycloak server response (GroupRepresentation) """ - for subgroup in group["subGroups"]: if subgroup["path"] == path: return subgroup @@ -866,8 +870,9 @@ class KeycloakAdmin: return None def get_group_members(self, group_id, query=None): - """ - Get members by group id. Returns group members + """Get members by group id. + + Returns group members GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_userrepresentation @@ -887,8 +892,8 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def get_group_by_path(self, path, search_in_subgroups=False): - """ - Get group id based on name or path. + """Get group id based on name or path. + A straight name or path match with a top-level group will return first. Subgroups are traversed, the first to match path (or name with path) is returned. @@ -899,7 +904,6 @@ class KeycloakAdmin: :param search_in_subgroups: True if want search in the subgroups :return: Keycloak server response (GroupRepresentation) """ - groups = self.get_groups() # TODO: Review this code is necessary @@ -916,8 +920,7 @@ class KeycloakAdmin: return None def create_group(self, payload, parent=None, skip_exists=False): - """ - Creates a group in the Realm + """Create a group in the Realm. :param payload: GroupRepresentation :param parent: parent group's id. Required to create a sub-group. @@ -928,7 +931,6 @@ class KeycloakAdmin: :return: Group id for newly created group or None for an existing group """ - if parent is None: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -950,8 +952,7 @@ class KeycloakAdmin: return def update_group(self, group_id, payload): - """ - Update group, ignores subgroups. + """Update group, ignores subgroups. :param group_id: id of group :param payload: GroupRepresentation with updated information. @@ -961,7 +962,6 @@ class KeycloakAdmin: :return: Http response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload) @@ -969,14 +969,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_set_permissions(self, group_id, enabled=True): - """ - Enable/Disable permissions for a group. Cannot delete group if disabled + """Enable/Disable permissions for a group. + + Cannot delete group if disabled :param group_id: id of group :param enabled: boolean :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), @@ -985,14 +985,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def group_user_add(self, user_id, group_id): - """ - Add user to group (user_id and group_id) + """Add user to group (user_id and group_id). :param user_id: id of user :param group_id: id of group to add to :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None @@ -1000,32 +998,29 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_user_remove(self, user_id, group_id): - """ - Remove user from group (user_id and group_id) + """Remove user from group (user_id and group_id). :param user_id: id of user :param group_id: id of group to remove from :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def delete_group(self, group_id): - """ - Deletes a group in the Realm + """Delete a group in the Realm. :param group_id: id of group to delete :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_clients(self): - """ + """Get clients. + Returns a list of clients belonging to the realm ClientRepresentation @@ -1033,14 +1028,12 @@ class KeycloakAdmin: :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client(self, client_id): - """ - Get representation of the client + """Get representation of the client. ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1048,21 +1041,19 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_id(self, client_name): - """ - Get internal keycloak client id from client-id. + """Get internal keycloak client id from client-id. + This is required for further actions against this client. :param client_name: name in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: client_id (uuid as string) """ - clients = self.get_clients() for client in clients: @@ -1072,14 +1063,12 @@ class KeycloakAdmin: return None def get_client_authz_settings(self, client_id): - """ - Get authorization json from client. + """Get authorization json from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path) @@ -1087,8 +1076,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_resource(self, client_id, payload, skip_exists=False): - """ - Create resources of client. + """Create resources of client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1097,7 +1085,6 @@ class KeycloakAdmin: :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1109,14 +1096,12 @@ class KeycloakAdmin: ) def get_client_authz_resources(self, client_id): - """ - Get resources from client. + """Get resources from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path) @@ -1124,8 +1109,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False): - """ - Create role-based policy of client. + """Create role-based policy of client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1147,7 +1131,6 @@ class KeycloakAdmin: } """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1159,8 +1142,7 @@ class KeycloakAdmin: ) def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): - """ - Create resource-based permission of client. + """Create resource-based permission of client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1183,7 +1165,6 @@ class KeycloakAdmin: ] """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1195,27 +1176,23 @@ class KeycloakAdmin: ) def get_client_authz_scopes(self, client_id): - """ - Get scopes from client. + """Get scopes from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_permissions(self, client_id): - """ - Get permissions from client. + """Get permissions from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path) @@ -1223,14 +1200,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policies(self, client_id): - """ - Get policies from client. + """Get policies from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path) @@ -1238,14 +1213,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_service_account_user(self, client_id): - """ - Get service account user from client. + """Get service account user from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: UserRepresentation """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path) @@ -1253,8 +1226,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client(self, payload, skip_exists=False): - """ - Create a client + """Create a client. ClientRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1263,7 +1235,6 @@ class KeycloakAdmin: :param payload: ClientRepresentation :return: Client ID """ - if skip_exists: client_id = self.get_client_id(client_name=payload["name"]) @@ -1281,8 +1252,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client(self, client_id, payload): - """ - Update a client + """Update a client. :param client_id: Client id :param payload: ClientRepresentation @@ -1296,8 +1266,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client(self, client_id): - """ - Get representation of the client + """Get representation of the client. ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1305,14 +1274,12 @@ class KeycloakAdmin: :param client_id: keycloak client id (not oauth client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_installation_provider(self, client_id, provider_id): - """ - Get content for given installation provider + """Get content for given installation provider. Related documentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource @@ -1323,7 +1290,6 @@ class KeycloakAdmin: :param client_id: Client id :param provider_id: provider id to specify response format """ - params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path) @@ -1331,22 +1297,20 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def get_realm_roles(self): - """ - Get all roles for the realm or client + """Get all roles for the realm or client. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_realm_role_members(self, role_name, query=None): - """ - Get role members of realm by role name. + """Get role members of realm by role name. + :param role_name: Name of the role. :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) @@ -1359,8 +1323,7 @@ class KeycloakAdmin: ) def get_client_roles(self, client_id): - """ - Get all roles for the client + """Get all roles for the client. :param client_id: id of client (not client-id) @@ -1369,14 +1332,13 @@ class KeycloakAdmin: :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role(self, client_id, role_name): - """ - Get client role id by name + """Get client role id by name. + This is required for further actions with this role. :param client_id: id of client (not client-id) @@ -1392,10 +1354,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role_id(self, client_id, role_name): - """ - Warning: Deprecated + """Get client role id by name. - Get client role id by name This is required for further actions with this role. :param client_id: id of client (not client-id) @@ -1410,8 +1370,7 @@ class KeycloakAdmin: return role.get("id") def create_client_role(self, client_role_id, payload, skip_exists=False): - """ - Create a client role + """Create a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1421,7 +1380,6 @@ class KeycloakAdmin: :param skip_exists: If true then do not raise an error if client role already exists :return: Client role name """ - if skip_exists: try: res = self.get_client_role(client_id=client_role_id, role_name=payload["name"]) @@ -1440,15 +1398,13 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): - """ - Add composite roles to client role + """Add composite roles to client role. :param client_role_id: id of client (not client-id) :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_post( @@ -1458,8 +1414,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def update_client_role(self, client_role_id, role_name, payload): - """ - Update a client role + """Update a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1475,8 +1430,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client_role(self, client_role_id, role_name): - """ - Delete a client role + """Delete a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1489,15 +1443,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def assign_client_role(self, user_id, client_id, roles): - """ - Assign a client role to a user + """Assign a client role to a user. :param user_id: id of user :param client_id: id of client (not client-id) :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_post( @@ -1507,8 +1459,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_client_role_members(self, client_id, role_name, **query): - """ - Get members by client role . + """Get members by client role. + :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters @@ -1521,8 +1473,8 @@ class KeycloakAdmin: ) def get_client_role_groups(self, client_id, role_name, **query): - """ - Get group members by client role . + """Get group members by client role. + :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters @@ -1535,14 +1487,12 @@ class KeycloakAdmin: ) def create_realm_role(self, payload, skip_exists=False): - """ - Create a new role for the realm or client + """Create a new role for the realm or client. :param payload: The role (use RoleRepresentation) :param skip_exists: If true then do not raise an error if realm role already exists :return: Realm role name """ - if skip_exists: try: role = self.get_realm_role(role_name=payload["name"]) @@ -1561,8 +1511,8 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_realm_role(self, role_name): - """ - Get realm role by role name + """Get realm role by role name. + :param role_name: role's name, not id! RoleRepresentation @@ -1576,13 +1526,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_realm_role(self, role_name, payload): - """ - Update a role for the realm by name + """Update a role for the realm by name. + :param role_name: The name of the role to be updated :param payload: The role (use RoleRepresentation) :return Keycloak server response """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), @@ -1591,12 +1540,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm_role(self, role_name): - """ - Delete a role for the realm by name + """Delete a role for the realm by name. + :param payload: The role name {'role-name':'name-of-the-role'} :return Keycloak server response """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path) @@ -1604,14 +1552,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_composite_realm_roles_to_role(self, role_name, roles): - """ - Add composite roles to the role + """Add composite roles to the role. :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_post( @@ -1621,14 +1567,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def remove_composite_realm_roles_to_role(self, role_name, roles): - """ - Remove composite roles from the role + """Remove composite roles from the role. :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be removed :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( @@ -1638,13 +1582,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_composite_realm_roles_of_role(self, role_name): - """ - Get composite roles of the role + """Get composite roles of the role. :param role_name: The name of the role :return: Keycloak server response (array RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path) @@ -1652,14 +1594,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_realm_roles(self, user_id, roles): - """ - Assign realm roles to a user + """Assign realm roles to a user. :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post( @@ -1669,14 +1609,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_realm_roles_of_user(self, user_id, roles): - """ - Deletes realm roles of a user + """Delete realm roles of a user. :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete( @@ -1686,20 +1624,18 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_realm_roles_of_user(self, user_id): - """ - Get all realm roles for a user. + """Get all realm roles for a user. :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_available_realm_roles_of_user(self, user_id): - """ - Get all available (i.e. unassigned) realm roles for a user. + """Get all available (i.e. unassigned) realm roles for a user. + :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ @@ -1710,8 +1646,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_composite_realm_roles_of_user(self, user_id): - """ - Get all composite (i.e. implicit) realm roles for a user. + """Get all composite (i.e. implicit) realm roles for a user. + :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ @@ -1722,14 +1658,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_realm_roles(self, group_id, roles): - """ - Assign realm roles to a group + """Assign realm roles to a group. :param group_id: id of groupp :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_post( @@ -1739,14 +1673,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_group_realm_roles(self, group_id, roles): - """ - Delete realm roles of a group + """Delete realm roles of a group. :param group_id: id of group :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete( @@ -1756,8 +1688,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_group_realm_roles(self, group_id): - """ - Get all realm roles for a group. + """Get all realm roles for a group. :param user_id: id of the group :return: Keycloak server response (array RoleRepresentation) @@ -1767,15 +1698,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_client_roles(self, group_id, client_id, roles): - """ - Assign client roles to a group + """Assign client roles to a group. :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_post( @@ -1785,28 +1714,24 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_group_client_roles(self, group_id, client_id): - """ - Get client roles of a group + """Get client roles of a group. :param group_id: id of group :param client_id: id of client (not client-id) :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_group_client_roles(self, group_id, client_id, roles): - """ - Delete client roles of a group + """Delete client roles of a group. :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response (array RoleRepresentation) """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_delete( @@ -1816,8 +1741,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_roles_of_user(self, user_id, client_id): - """ - Get all client roles for a user. + """Get all client roles for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1828,8 +1752,7 @@ class KeycloakAdmin: ) def get_available_client_roles_of_user(self, user_id, client_id): - """ - Get available client role-mappings for a user. + """Get available client role-mappings for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1840,8 +1763,7 @@ class KeycloakAdmin: ) def get_composite_client_roles_of_user(self, user_id, client_id): - """ - Get composite client role-mappings for a user. + """Get composite client role-mappings for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1857,8 +1779,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_client_roles_of_user(self, user_id, client_id, roles): - """ - Delete client roles from a user. + """Delete client roles from a user. :param user_id: id of user :param client_id: id of client containing role (not client-id) @@ -1874,8 +1795,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flows(self): - """ - Get authentication flows. Returns all flow details + """Get authentication flows. + + Returns all flow details AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1887,8 +1809,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authentication_flow_for_id(self, flow_id): - """ - Get one authentication flow by it's id. Returns all flow details + """Get one authentication flow by it's id. + + Returns all flow details AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1901,8 +1824,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow(self, payload, skip_exists=False): - """ - Create a new authentication flow + """Create a new authentication flow. AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1911,7 +1833,6 @@ class KeycloakAdmin: :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload) @@ -1921,15 +1842,14 @@ class KeycloakAdmin: ) def copy_authentication_flow(self, payload, flow_alias): - """ - Copy existing authentication flow under a new name. The new name is given as 'newName' - attribute of the passed payload. + """Copy existing authentication flow under a new name. + + The new name is given as 'newName' attribute of the passed payload. :param payload: JSON containing 'newName' attribute :param flow_alias: the flow alias :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) @@ -1937,8 +1857,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow(self, flow_id): - """ - Delete authentication flow + """Delete authentication flow. AuthenticationInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationinforepresentation @@ -1951,8 +1870,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flow_executions(self, flow_alias): - """ - Get authentication flow executions. Returns all execution steps + """Get authentication flow executions. + + Returns all execution steps :param flow_alias: the flow alias :return: Response(json) @@ -1962,8 +1882,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_authentication_flow_executions(self, payload, flow_alias): - """ - Update an authentication flow execution + """Update an authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -1972,7 +1891,6 @@ class KeycloakAdmin: :param flow_alias: The flow alias :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), @@ -1981,8 +1899,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204]) def get_authentication_flow_execution(self, execution_id): - """ - Get authentication flow execution. + """Get authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -1995,8 +1912,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow_execution(self, payload, flow_alias): - """ - Create an authentication flow execution + """Create an authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -2005,7 +1921,6 @@ class KeycloakAdmin: :param flow_alias: The flow alias :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), @@ -2014,8 +1929,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow_execution(self, execution_id): - """ - Delete authentication flow execution + """Delete authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -2028,8 +1942,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): - """ - Create a new sub authentication flow for a given authentication flow + """Create a new sub authentication flow for a given authentication flow. AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -2039,7 +1952,6 @@ class KeycloakAdmin: :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), @@ -2050,8 +1962,7 @@ class KeycloakAdmin: ) def get_authenticator_providers(self): - """ - Get authenticator providers list. + """Get authenticator providers list. :return: Response(json) """ @@ -2062,8 +1973,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authenticator_provider_config_description(self, provider_id): - """ - Get authenticator's provider configuration description. + """Get authenticator's provider configuration description. AuthenticatorConfigInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfiginforepresentation @@ -2078,8 +1988,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authenticator_config(self, config_id): - """ - Get authenticator configuration. Returns all configuration details. + """Get authenticator configuration. + + Returns all configuration details. :param config_id: Authenticator config id :return: Response(json) @@ -2089,8 +2000,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_authenticator_config(self, payload, config_id): - """ - Update an authenticator configuration. + """Update an authenticator configuration. AuthenticatorConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfigrepresentation @@ -2107,14 +2017,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_authenticator_config(self, config_id): - """ - Delete a authenticator configuration. + """Delete a authenticator configuration. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authentication_management_resource :param config_id: Authenticator config id :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_delete( urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path) @@ -2122,8 +2031,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def sync_users(self, storage_id, action): - """ - Function to trigger user sync from provider + """Trigger user sync from provider. :param storage_id: The id of the user storage provider :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync" @@ -2141,39 +2049,39 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_client_scopes(self): - """ + """Get client scopes. + Get representation of the client scopes for the realm where we are connected to https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :return: Keycloak server response Array of (ClientScopeRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scope(self, client_scope_id): - """ + """Get client scope. + Get representation of the client scopes for the realm where we are connected to https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_id: The id of the client scope :return: Keycloak server response (ClientScopeRepresentation) """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scope_by_name(self, client_scope_name): - """ + """Get client scope by name. + Get representation of the client scope identified by the client scope name. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_name: (str) Name of the client scope :returns: ClientScopeRepresentation or None """ - client_scopes = self.get_client_scopes() for client_scope in client_scopes: if client_scope["name"] == client_scope_name: @@ -2182,8 +2090,7 @@ class KeycloakAdmin: return None def create_client_scope(self, payload, skip_exists=False): - """ - Create a client scope + """Create a client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes @@ -2192,7 +2099,6 @@ class KeycloakAdmin: :param skip_exists: If true then do not raise an error if client scope already exists :return: Client scope id """ - if skip_exists: exists = self.get_client_scope_by_name(client_scope_name=payload["name"]) @@ -2210,8 +2116,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client_scope(self, client_scope_id, payload): - """ - Update a client scope + """Update a client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource @@ -2220,7 +2125,6 @@ class KeycloakAdmin: :param payload: ClientScopeRepresentation :return: Keycloak server response (ClientScopeRepresentation) """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) @@ -2228,8 +2132,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client_scope(self, client_scope_id): - """ - Delete existing client scope. + """Delete existing client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource @@ -2242,8 +2145,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_mappers_from_client_scope(self, client_scope_id): - """ - Get a list of all mappers connected to the client scope + """Get a list of all mappers connected to the client scope. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: Client scope id @@ -2256,15 +2158,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def add_mapper_to_client_scope(self, client_scope_id, payload): - """ - Add a mapper to a client scope + """Add a mapper to a client scope. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_scope_id: The id of the client scope :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_post( @@ -2275,15 +2176,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id): - """ - Delete a mapper from a client scope + """Delete a mapper from a client scope. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope :param protocol_mapper_id: Protocol mapper id :return: Keycloak server Response """ - params_path = { "realm-name": self.realm_name, "scope-id": client_scope_id, @@ -2296,8 +2196,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload): - """ - Update an existing protocol mapper in a client scope + """Update an existing protocol mapper in a client scope. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: The id of the client scope @@ -2306,7 +2206,6 @@ class KeycloakAdmin: :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = { "realm-name": self.realm_name, "scope-id": client_scope_id, @@ -2321,7 +2220,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_default_client_scopes(self): - """ + """Get default default client scopes. + Return list of default default client scopes :return: Keycloak server response @@ -2333,8 +2233,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_default_client_scope(self, scope_id): - """ - Delete default default client scope + """Delete default default client scope. :param scope_id: default default client scope id :return: Keycloak server response @@ -2346,8 +2245,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_default_client_scope(self, scope_id): - """ - Add default default client scope + """Add default default client scope. :param scope_id: default default client scope id :return: Keycloak server response @@ -2361,7 +2259,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_optional_client_scopes(self): - """ + """Get default optional client scopes. + Return list of default optional client scopes :return: Keycloak server response @@ -2373,8 +2272,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_optional_client_scope(self, scope_id): - """ - Delete default optional client scope + """Delete default optional client scope. :param scope_id: default optional client scope id :return: Keycloak server response @@ -2386,8 +2284,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_optional_client_scope(self, scope_id): - """ - Add default optional client scope + """Add default optional client scope. :param scope_id: default optional client scope id :return: Keycloak server response @@ -2401,8 +2298,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_mappers_from_client(self, client_id): - """ - List of all client mappers. + """List of all client mappers. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocolmapperrepresentation @@ -2418,15 +2314,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200]) def add_mapper_to_client(self, client_id, payload): - """ - Add a mapper to a client + """Add a mapper to a client. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_id: The id of the client :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -2437,14 +2332,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_client_mapper(self, client_id, mapper_id, payload): - """ - Update client mapper + """Update client mapper. + :param client_id: The id of the client :param client_mapper_id: The id of the mapper to be deleted :param payload: ProtocolMapperRepresentation :return: Keycloak server response """ - params_path = { "realm-name": self.realm_name, "id": client_id, @@ -2459,14 +2353,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def remove_client_mapper(self, client_id, client_mapper_id): - """ - Removes a mapper from the client + """Remove a mapper from the client. + https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_protocol_mappers_resource :param client_id: The id of the client :param client_mapper_id: The id of the mapper to be deleted :return: Keycloak server response """ - params_path = { "realm-name": self.realm_name, "id": client_id, @@ -2479,15 +2372,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def generate_client_secrets(self, client_id): - """ + """Generate a new secret for the client. - Generate a new secret for the client https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_regeneratesecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None @@ -2495,21 +2386,20 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_client_secrets(self, client_id): - """ + """Get representation of the client secrets. - Get representation of the client secrets https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_components(self, query=None): - """ + """Get components. + Return a list of components, filtered according to query parameters ComponentRepresentation @@ -2526,8 +2416,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_component(self, payload): - """ - Create a new component. + """Create a new component. ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation @@ -2544,8 +2433,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_component(self, component_id): - """ - Get representation of the component + """Get representation of the component. :param component_id: Component id @@ -2559,8 +2447,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_component(self, component_id, payload): - """ - Update the component + """Update the component. :param component_id: Component id :param payload: ComponentRepresentation @@ -2575,8 +2462,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_component(self, component_id): - """ - Delete the component + """Delete the component. :param component_id: Component id @@ -2587,7 +2473,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_keys(self): - """ + """Get keys. + Return a list of keys, filtered according to query parameters KeysMetadataRepresentation @@ -2600,7 +2487,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_events(self, query=None): - """ + """Get events. + Return a list of events, filtered according to query parameters EventRepresentation array @@ -2616,8 +2504,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def set_events(self, payload): - """ - Set realm events configuration + """Set realm events configuration. RealmEventsConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmeventsconfigrepresentation @@ -2631,8 +2518,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def raw_get(self, *args, **kwargs): - """ - Calls connection.raw_get. + """Call connection.raw_get. If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token and try *get* once more. @@ -2644,8 +2530,7 @@ class KeycloakAdmin: return r def raw_post(self, *args, **kwargs): - """ - Calls connection.raw_post. + """Call connection.raw_post. If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token and try *post* once more. @@ -2657,8 +2542,7 @@ class KeycloakAdmin: return r def raw_put(self, *args, **kwargs): - """ - Calls connection.raw_put. + """Call connection.raw_put. If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token and try *put* once more. @@ -2670,8 +2554,7 @@ class KeycloakAdmin: return r def raw_delete(self, *args, **kwargs): - """ - Calls connection.raw_delete. + """Call connection.raw_delete. If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token and try *delete* once more. @@ -2683,6 +2566,7 @@ class KeycloakAdmin: return r def get_token(self): + """Get admin token.""" if self.user_realm_name: token_realm_name = self.user_realm_name elif self.realm_name: @@ -2730,6 +2614,7 @@ class KeycloakAdmin: ) def refresh_token(self): + """Refresh the token.""" refresh_token = self.token.get("refresh_token", None) if refresh_token is None: self.get_token() @@ -2752,8 +2637,7 @@ class KeycloakAdmin: ) def get_client_all_sessions(self, client_id): - """ - Get sessions associated with the client + """Get sessions associated with the client. :param client_id: id of client @@ -2767,8 +2651,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_sessions_stats(self): - """ - Get current session count for all clients with active sessions + """Get current session count for all clients with active sessions. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsessionstats @@ -2779,8 +2662,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_management_permissions(self, client_id): - """ - Get management permissions for a client. + """Get management permissions for a client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2793,8 +2675,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_client_management_permissions(self, payload, client_id): - """ - Update management permissions for a client. + """Update management permissions for a client. ManagementPermissionReference https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_managementpermissionreference @@ -2819,8 +2700,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200]) def get_client_authz_policy_scopes(self, client_id, policy_id): - """ - Get scopes for a given policy. + """Get scopes for a given policy. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2834,8 +2714,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policy_resources(self, client_id, policy_id): - """ - Get resources for a given policy. + """Get resources for a given policy. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2849,8 +2728,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_scope_permission(self, client_id, scope_id): - """ - Get permissions for a given scope. + """Get permissions for a given scope. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2864,8 +2742,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_client_authz_scope_permission(self, payload, client_id, scope_id): - """ - Update permissions for a given scope. + """Update permissions for a given scope. :param payload: No Document :param client_id: id in ClientRepresentation @@ -2895,8 +2772,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201]) def get_client_authz_client_policies(self, client_id): - """ - Get policies for a given client. + """Get policies for a given client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2909,8 +2785,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def create_client_authz_client_policy(self, payload, client_id): - """ - Create a new policy for a given client. + """Create a new policy for a given client. :param payload: No Document :param client_id: id in ClientRepresentation diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index fa04e4d..83575b2 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -21,6 +21,12 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Keycloak OpenID module. + +The module contains mainly the implementation of KeycloakOpenID class, the main +class to handle authentication and token manipulation. +""" + import json from jose import jwt @@ -52,8 +58,7 @@ from .urls_patterns import ( class KeycloakOpenID: - """ - Keycloak OpenID client. + """Keycloak OpenID client. :param server_url: Keycloak server url :param client_id: client id @@ -75,6 +80,7 @@ class KeycloakOpenID: proxies=None, timeout=60, ): + """Init method.""" self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name @@ -87,6 +93,7 @@ class KeycloakOpenID: @property def client_id(self): + """Get client id.""" return self._client_id @client_id.setter @@ -95,6 +102,7 @@ class KeycloakOpenID: @property def client_secret_key(self): + """Get the client secret key.""" return self._client_secret_key @client_secret_key.setter @@ -103,6 +111,7 @@ class KeycloakOpenID: @property def realm_name(self): + """Get the realm name.""" return self._realm_name @realm_name.setter @@ -111,6 +120,7 @@ class KeycloakOpenID: @property def connection(self): + """Get connection.""" return self._connection @connection.setter @@ -119,6 +129,7 @@ class KeycloakOpenID: @property def authorization(self): + """Get authorization.""" return self._authorization @authorization.setter @@ -126,8 +137,7 @@ class KeycloakOpenID: self._authorization = value def _add_secret_key(self, payload): - """ - Add secret key if exist. + """Add secret key if exists. :param payload: :return: @@ -138,7 +148,7 @@ class KeycloakOpenID: return payload def _build_name_role(self, role): - """ + """Build name of a role. :param role: :return: @@ -146,7 +156,7 @@ class KeycloakOpenID: return self.client_id + "/" + role def _token_info(self, token, method_token_info, **kwargs): - """ + """Getter for the token data. :param token: :param method_token_info: @@ -161,19 +171,20 @@ class KeycloakOpenID: return token_info def well_known(self): - """The most important endpoint to understand is the well-known configuration + """Get the well_known object. + + The most important endpoint to understand is the well-known configuration endpoint. It lists endpoints and other configuration options relevant to the OpenID Connect implementation in Keycloak. :return It lists endpoints and other configuration options relevant. """ - params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def auth_url(self, redirect_uri): - """ + """Get the authentication URL endpoint. http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint @@ -196,7 +207,8 @@ class KeycloakOpenID: totp=None, **extra ): - """ + """Retrieve user token. + The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used. The token endpoint is also used to obtain new access tokens @@ -232,7 +244,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def refresh_token(self, refresh_token, grant_type=["refresh_token"]): - """ + """Refresh the user token. + The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used. The token endpoint is also used to obtain new access tokens @@ -255,7 +268,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: - """ + """Exchange user token. + Use a token to obtain an entirely different token. See https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange @@ -279,7 +293,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def userinfo(self, token): - """ + """Get the user info object. + The userinfo endpoint returns standard claims about the authenticated user, and is protected by a bearer token. @@ -294,8 +309,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) def logout(self, refresh_token): - """ - The logout endpoint logs out the authenticated user. + """Log out the authenticated user. + :param refresh_token: :return: """ @@ -306,7 +321,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def certs(self): - """ + """Get certificates. + The certificate endpoint returns the public keys enabled by the realm, encoded as a JSON Web Key (JWK). Depending on the realm settings there can be one or more keys enabled for verifying tokens. @@ -320,7 +336,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) def public_key(self): - """ + """Retrieve the public key. + The public key is exposed by the realm page directly. :return: @@ -330,7 +347,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError)["public_key"] def entitlement(self, token, resource_server_id): - """ + """Get entitlements from the token. + Client applications can use a specific endpoint to obtain a special security token called a requesting party token (RPT). This token consists of all the entitlements (or permissions) for a user as a result of the evaluation of the permissions and @@ -349,7 +367,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) # pragma: no cover def introspect(self, token, rpt=None, token_type_hint=None): - """ + """Introspect the user token. + The introspection endpoint is used to retrieve the active state of a token. It is can only be invoked by confidential clients. @@ -377,7 +396,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def decode_token(self, token, key, algorithms=["RS256"], **kwargs): - """ + """Decode user token. + A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. This specification also defines a JWK Set JSON data structure that represents a set of @@ -395,8 +415,7 @@ class KeycloakOpenID: return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) def load_authorization_config(self, path): - """ - Load Keycloak settings (authorization) + """Load Keycloak settings (authorization). :param path: settings file (json) :return: @@ -407,8 +426,7 @@ class KeycloakOpenID: self.authorization.load_config(authorization_json) def get_policies(self, token, method_token_info="introspect", **kwargs): - """ - Get policies by user token + """Get policies by user token. :param token: user token :return: policies list @@ -438,8 +456,7 @@ class KeycloakOpenID: return list(set(policies)) def get_permissions(self, token, method_token_info="introspect", **kwargs): - """ - Get permission by user token + """Get permission by user token. :param token: user token :param method_token_info: Decode token method @@ -471,8 +488,7 @@ class KeycloakOpenID: return list(set(permissions)) def uma_permissions(self, token, permissions=""): - """ - Get UMA permissions by user token with requested permissions + """Get UMA permissions by user token with requested permissions. The token endpoint is used to retrieve UMA permissions from Keycloak. It can only be invoked by confidential clients. @@ -499,8 +515,7 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def has_uma_access(self, token, permissions): - """ - Determine whether user has uma permissions with specified user token + """Determine whether user has uma permissions with specified user token. :param token: user token :param permissions: list of uma permissions (resource:scope) diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 5653c76..1bf2136 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -21,11 +21,14 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""User-managed access permissions module.""" + from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError class UMAPermission: """A class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. Usage example: @@ -36,9 +39,16 @@ class UMAPermission: >>> print(permission) 'Users#delete' + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str """ def __init__(self, permission=None, resource="", scope=""): + """Init method.""" self.resource = resource self.scope = scope @@ -53,21 +63,26 @@ class UMAPermission: self.scope = str(permission.scope) def __str__(self): + """Str method.""" scope = self.scope if scope: scope = "#" + scope return "{}{}".format(self.resource, scope) def __eq__(self, __o: object) -> bool: + """Eq method.""" return str(self) == str(__o) def __repr__(self) -> str: + """Repr method.""" return self.__str__() def __hash__(self) -> int: + """Hash method.""" return hash(str(self)) def __call__(self, permission=None, resource="", scope="") -> object: + """Call method.""" result_resource = self.resource result_scope = self.scope @@ -91,36 +106,58 @@ class UMAPermission: class Resource(UMAPermission): """An UMAPermission Resource class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + + :param resource: Resource + :type resource: str """ def __init__(self, resource): + """Init method.""" super().__init__(resource=resource) class Scope(UMAPermission): """An UMAPermission Scope class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + + :param scope: Scope + :type scope: str """ def __init__(self, scope): + """Init method.""" super().__init__(scope=scope) class AuthStatus: """A class that represents the authorization/login status of a user associated with a token. + This has to evaluate to True if and only if the user is properly authorized - for the requested resource.""" + for the requested resource. + + :param is_logged_in: Is logged in indicator + :type is_logged_in: bool + :param is_authorized: Is authorized indicator + :type is_authorized: bool + :param missing_permissions: Missing permissions + :type missing_permissions: set + """ def __init__(self, is_logged_in, is_authorized, missing_permissions): + """Init method.""" self.is_logged_in = is_logged_in self.is_authorized = is_authorized self.missing_permissions = missing_permissions def __bool__(self): + """Bool method.""" return self.is_authorized def __repr__(self): + """Repr method.""" return ( f"AuthStatus(" f"is_authorized={self.is_authorized}, " @@ -130,8 +167,7 @@ class AuthStatus: def build_permission_param(permissions): - """ - Transform permissions to a set, so they are usable for requests + """Transform permissions to a set, so they are usable for requests. :param permissions: either str (resource#scope), iterable[str] (resource#scope), diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 3ec134c..b836692 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -21,6 +21,8 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Keycloak URL patterns.""" + # OPENID URLS URL_REALM = "realms/{realm-name}" URL_WELL_KNOWN = "realms/{realm-name}/.well-known/openid-configuration" diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..f1b390f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests module.""" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index e62bdda..069bb61 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1,3 +1,5 @@ +"""Test the keycloak admin object.""" + import pytest import keycloak @@ -13,10 +15,12 @@ from keycloak.exceptions import ( def test_keycloak_version(): + """Test version.""" assert keycloak.__version__, keycloak.__version__ def test_keycloak_admin_bad_init(env): + """Test keycloak admin bad init.""" with pytest.raises(TypeError) as err: KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -37,6 +41,7 @@ def test_keycloak_admin_bad_init(env): def test_keycloak_admin_init(env): + """Test keycloak admin init.""" admin = KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -111,6 +116,7 @@ def test_keycloak_admin_init(env): def test_realms(admin: KeycloakAdmin): + """Test realms.""" # Get realms realms = admin.get_realms() assert len(realms) == 1, realms @@ -175,6 +181,7 @@ def test_realms(admin: KeycloakAdmin): def test_import_export_realms(admin: KeycloakAdmin, realm: str): + """Test import and export of realms.""" admin.realm_name = realm realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True) @@ -192,6 +199,7 @@ def test_import_export_realms(admin: KeycloakAdmin, realm: str): def test_users(admin: KeycloakAdmin, realm: str): + """Test users.""" admin.realm_name = realm # Check no users present @@ -283,6 +291,7 @@ def test_users(admin: KeycloakAdmin, realm: str): def test_users_pagination(admin: KeycloakAdmin, realm: str): + """Test user pagination.""" admin.realm_name = realm for ind in range(admin.PAGE_SIZE + 50): @@ -300,6 +309,7 @@ def test_users_pagination(admin: KeycloakAdmin, realm: str): def test_idps(admin: KeycloakAdmin, realm: str): + """Test IDPs.""" admin.realm_name = realm # Create IDP @@ -371,6 +381,7 @@ def test_idps(admin: KeycloakAdmin, realm: str): def test_user_credentials(admin: KeycloakAdmin, user: str): + """Test user credentials.""" res = admin.set_user_password(user_id=user, password="booya", temporary=True) assert res == dict(), res @@ -398,6 +409,7 @@ def test_user_credentials(admin: KeycloakAdmin, user: str): def test_social_logins(admin: KeycloakAdmin, user: str): + """Test social logins.""" res = admin.add_user_social_login( user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test" ) @@ -437,6 +449,7 @@ def test_social_logins(admin: KeycloakAdmin, user: str): def test_server_info(admin: KeycloakAdmin): + """Test server info.""" info = admin.get_server_info() assert set(info.keys()) == { "systemInfo", @@ -456,6 +469,7 @@ def test_server_info(admin: KeycloakAdmin): def test_groups(admin: KeycloakAdmin, user: str): + """Test groups.""" # Test get groups groups = admin.get_groups() assert len(groups) == 0 @@ -599,6 +613,7 @@ def test_groups(admin: KeycloakAdmin, user: str): def test_clients(admin: KeycloakAdmin, realm: str): + """Test clients.""" admin.realm_name = realm # Test get clients @@ -860,6 +875,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): def test_realm_roles(admin: KeycloakAdmin, realm: str): + """Test realm roles.""" admin.realm_name = realm # Test get realm roles @@ -1015,6 +1031,7 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): def test_client_roles(admin: KeycloakAdmin, client: str): + """Test client roles.""" # Test get client roles res = admin.get_client_roles(client_id=client) assert len(res) == 0 @@ -1177,6 +1194,7 @@ def test_client_roles(admin: KeycloakAdmin, client: str): def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): + """Test enable token exchange.""" # Test enabling token exchange between two confidential clients admin.realm_name = realm @@ -1265,6 +1283,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): def test_email(admin: KeycloakAdmin, user: str): + """Test email.""" # Emails will fail as we don't have SMTP test setup with pytest.raises(KeycloakPutError) as err: admin.send_update_account(user_id=user, payload=dict()) @@ -1277,6 +1296,7 @@ def test_email(admin: KeycloakAdmin, user: str): def test_get_sessions(admin: KeycloakAdmin): + """Test get sessions.""" sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username)) assert len(sessions) >= 1 with pytest.raises(KeycloakGetError) as err: @@ -1285,6 +1305,7 @@ def test_get_sessions(admin: KeycloakAdmin): def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): + """Test get client installation provider.""" with pytest.raises(KeycloakGetError) as err: admin.get_client_installation_provider(client_id=client, provider_id="bad") assert err.match('404: b\'{"error":"Unknown Provider"}\'') @@ -1303,6 +1324,7 @@ def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): def test_auth_flows(admin: KeycloakAdmin, realm: str): + """Test auth flows.""" admin.realm_name = realm res = admin.get_authentication_flows() @@ -1449,6 +1471,7 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str): def test_authentication_configs(admin: KeycloakAdmin, realm: str): + """Test authentication configs.""" admin.realm_name = realm # Test list of auth providers @@ -1480,6 +1503,7 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str): def test_sync_users(admin: KeycloakAdmin, realm: str): + """Test sync users.""" admin.realm_name = realm # Only testing the error message @@ -1489,6 +1513,7 @@ def test_sync_users(admin: KeycloakAdmin, realm: str): def test_client_scopes(admin: KeycloakAdmin, realm: str): + """Test client scopes.""" admin.realm_name = realm # Test get client scopes @@ -1626,6 +1651,7 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): def test_components(admin: KeycloakAdmin, realm: str): + """Test components.""" admin.realm_name = realm # Test get components @@ -1676,6 +1702,7 @@ def test_components(admin: KeycloakAdmin, realm: str): def test_keys(admin: KeycloakAdmin, realm: str): + """Test keys.""" admin.realm_name = realm assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} assert {k["algorithm"] for k in admin.get_keys()["keys"]} == { @@ -1687,6 +1714,7 @@ def test_keys(admin: KeycloakAdmin, realm: str): def test_events(admin: KeycloakAdmin, realm: str): + """Test events.""" admin.realm_name = realm events = admin.get_events() @@ -1706,6 +1734,7 @@ def test_events(admin: KeycloakAdmin, realm: str): def test_auto_refresh(admin: KeycloakAdmin, realm: str): + """Test auto refresh token.""" # Test get refresh admin.auto_refresh_token = list() admin.connection = ConnectionManager( diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py index 09d7147..581faf4 100644 --- a/tests/test_uma_permissions.py +++ b/tests/test_uma_permissions.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . + +"""Test uma permissions.""" + import re import pytest @@ -23,30 +26,35 @@ from keycloak.uma_permissions import Resource, Scope, build_permission_param def test_resource_with_scope_obj(): + """Test resource with scope.""" r = Resource("Resource1") s = Scope("Scope1") assert r(s) == "Resource1#Scope1" def test_scope_with_resource_obj(): + """Test scope with resource.""" r = Resource("Resource1") s = Scope("Scope1") assert s(r) == "Resource1#Scope1" def test_resource_scope_str(): + """Test resource scope as string.""" r = Resource("Resource1") s = "Scope1" assert r(scope=s) == "Resource1#Scope1" def test_scope_resource_str(): + """Test scope resource as string.""" r = "Resource1" s = Scope("Scope1") assert s(resource=r) == "Resource1#Scope1" def test_resource_scope_list(): + """Test resource scope as list.""" r = Resource("Resource1") s = ["Scope1"] with pytest.raises(PermissionDefinitionError) as err: @@ -55,94 +63,114 @@ def test_resource_scope_list(): def test_build_permission_none(): + """Test build permission param with None.""" assert build_permission_param(None) == set() def test_build_permission_empty_str(): + """Test build permission param with an empty string.""" assert build_permission_param("") == set() def test_build_permission_empty_list(): + """Test build permission param with an empty list.""" assert build_permission_param([]) == set() def test_build_permission_empty_tuple(): + """Test build permission param with an empty tuple.""" assert build_permission_param(()) == set() def test_build_permission_empty_set(): + """Test build permission param with an empty set.""" assert build_permission_param(set()) == set() def test_build_permission_empty_dict(): + """Test build permission param with an empty dict.""" assert build_permission_param({}) == set() def test_build_permission_str(): + """Test build permission param as string.""" assert build_permission_param("resource1") == {"resource1"} def test_build_permission_list_str(): + """Test build permission param with list of strings.""" assert build_permission_param(["res1#scope1", "res1#scope2"]) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_str(): + """Test build permission param with tuple of strings.""" assert build_permission_param(("res1#scope1", "res1#scope2")) == {"res1#scope1", "res1#scope2"} def test_build_permission_set_str(): + """Test build permission param with set of strings.""" assert build_permission_param({"res1#scope1", "res1#scope2"}) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_dict_str_str(): + """Test build permission param with dictionary.""" assert build_permission_param({"res1": "scope1"}) == {"res1#scope1"} def test_build_permission_tuple_dict_str_list_str(): + """Test build permission param with dictionary of list.""" assert build_permission_param({"res1": ["scope1", "scope2"]}) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_dict_str_list_str2(): + """Test build permission param with mutliple-keyed dictionary.""" assert build_permission_param( {"res1": ["scope1", "scope2"], "res2": ["scope2", "scope3"]} ) == {"res1#scope1", "res1#scope2", "res2#scope2", "res2#scope3"} def test_build_permission_uma(): + """Test build permission param with UMA.""" assert build_permission_param(Resource("res1")(Scope("scope1"))) == {"res1#scope1"} def test_build_permission_uma_list(): + """Test build permission param with list of UMAs.""" assert build_permission_param( [Resource("res1")(Scope("scope1")), Resource("res1")(Scope("scope2"))] ) == {"res1#scope1", "res1#scope2"} def test_build_permission_misbuilt_dict_str_list_list_str(): + """Test bad build of permission param from dictionary.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": [["scope1", "scope2"]]}) assert err.match(re.escape("misbuilt permission {'res1': [['scope1', 'scope2']]}")) def test_build_permission_misbuilt_list_list_str(): + """Test bad build of permission param from list.""" with pytest.raises(KeycloakPermissionFormatError) as err: print(build_permission_param([["scope1", "scope2"]])) assert err.match(re.escape("misbuilt permission [['scope1', 'scope2']]")) def test_build_permission_misbuilt_list_set_str(): + """Test bad build of permission param from set.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1", "scope2"}]) assert err.match("misbuilt permission.*") def test_build_permission_misbuilt_set_set_str(): + """Test bad build of permission param from list of set.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1"}]) assert err.match(re.escape("misbuilt permission [{'scope1'}]")) def test_build_permission_misbuilt_dict_non_iterable(): + """Test bad build of permission param from non-iterable.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": 5}) assert err.match(re.escape("misbuilt permission {'res1': 5}")) diff --git a/tests/test_urls_patterns.py b/tests/test_urls_patterns.py index 6fa5a87..5fae847 100644 --- a/tests/test_urls_patterns.py +++ b/tests/test_urls_patterns.py @@ -1,9 +1,10 @@ +"""Test URL patterns.""" + from keycloak import urls_patterns def test_correctness_of_patterns(): """Test that there are no duplicate url patterns.""" - # Test that the patterns are present urls = [x for x in dir(urls_patterns) if not x.startswith("__")] assert len(urls) >= 0 From dfc7c4a2d52ab11b6a11ef1cff4bdd9585b5f473 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 08:00:54 +0000 Subject: [PATCH 068/222] style: added docstring to conf --- docs/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index b60a1c9..9a5d438 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,6 +20,9 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) + +"""Sphinx documentation configuration.""" + import sphinx_rtd_theme # -- General configuration ------------------------------------------------ From 3a697caaef50254725d51fb052e999c1e9690a4b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 15:28:12 +0000 Subject: [PATCH 069/222] test: added load authorization config test --- src/keycloak/authorization/permission.py | 12 +++---- src/keycloak/authorization/policy.py | 20 +++++++---- tests/conftest.py | 8 +++++ tests/data/authz_settings.json | 45 ++++++++++++++++++++++++ tests/test_keycloak_openid.py | 38 +++++++++++++++++++- 5 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 tests/data/authz_settings.json diff --git a/src/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py index a444f83..667f8c3 100644 --- a/src/keycloak/authorization/permission.py +++ b/src/keycloak/authorization/permission.py @@ -49,12 +49,12 @@ class Permission: def __init__(self, name, type, logic, decision_strategy): """Init method.""" - self._name = name - self._type = type - self._logic = logic - self._decision_strategy = decision_strategy - self._resources = [] - self._scopes = [] + self.name = name + self.type = type + self.logic = logic + self.decision_strategy = decision_strategy + self.resources = [] + self.scopes = [] def __repr__(self): """Repr method.""" diff --git a/src/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py index 6b558d8..7e03db0 100644 --- a/src/keycloak/authorization/policy.py +++ b/src/keycloak/authorization/policy.py @@ -43,12 +43,12 @@ class Policy: def __init__(self, name, type, logic, decision_strategy): """Init method.""" - self._name = name - self._type = type - self._logic = logic - self._decision_strategy = decision_strategy - self._roles = [] - self._permissions = [] + self.name = name + self.type = type + self.logic = logic + self.decision_strategy = decision_strategy + self.roles = [] + self.permissions = [] def __repr__(self): """Repr method.""" @@ -99,11 +99,19 @@ class Policy: """Get roles.""" return self._roles + @roles.setter + def roles(self, value): + self._roles = value + @property def permissions(self): """Get permissions.""" return self._permissions + @permissions.setter + def permissions(self, value): + self._permissions = value + def add_role(self, role): """Add keycloak role in policy. diff --git a/tests/conftest.py b/tests/conftest.py index 47c9854..632c51b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -185,6 +185,14 @@ def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: Keycloak "serviceAccountsEnabled": True, } ) + admin.create_client_authz_role_based_policy( + client_id=client_id, + payload={ + "name": "test-authz-rb-policy", + "roles": [{"id": admin.get_realm_role(role_name="offline_access")["id"]}], + }, + ) + admin.create_client_authz_resource # Create user username = str(uuid.uuid4()) password = str(uuid.uuid4()) diff --git a/tests/data/authz_settings.json b/tests/data/authz_settings.json new file mode 100644 index 0000000..e051085 --- /dev/null +++ b/tests/data/authz_settings.json @@ -0,0 +1,45 @@ +{ + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "policies": [ + { + "name": "Default Policy", + "type": "js", + "logic": "POSITIVE", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n" + } + }, + { + "name": "test-authz-rb-policy", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"offline_access\",\"required\":false}]" + } + }, + { + "name": "Default Permission", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "applyPolicies": "[\"test-authz-rb-policy\"]" + } + }, + { + "name": "Test scope", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "scopes": "[]", + "applyPolicies": "[\"test-authz-rb-policy\"]" + } + } + ], + "scopes": [], + "decisionStrategy": "UNANIMOUS" +} \ No newline at end of file diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 9ed2b88..f2c0f7e 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -4,8 +4,15 @@ from unittest import mock import pytest from keycloak.authorization import Authorization +from keycloak.authorization.permission import Permission +from keycloak.authorization.policy import Policy +from keycloak.authorization.role import Role from keycloak.connection import ConnectionManager -from keycloak.exceptions import KeycloakDeprecationError, KeycloakRPTNotFound +from keycloak.exceptions import ( + KeycloakAuthenticationError, + KeycloakDeprecationError, + KeycloakRPTNotFound, +) from keycloak.keycloak_admin import KeycloakAdmin from keycloak.keycloak_openid import KeycloakOpenID @@ -185,6 +192,18 @@ def test_exchange_token( assert token != new_token +def test_logout(oid_with_credentials): + """Test logout.""" + oid, username, password = oid_with_credentials + + token = oid.token(username=username, password=password) + assert oid.userinfo(token=token["access_token"]) != dict() + assert oid.logout(refresh_token=token["refresh_token"]) == dict() + + with pytest.raises(KeycloakAuthenticationError): + oid.userinfo(token=token["access_token"]) + + def test_certs(oid: KeycloakOpenID): """Test certificates.""" assert len(oid.certs()["keys"]) == 2 @@ -236,3 +255,20 @@ def test_decode_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): )["preferred_username"] == username ) + + +def test_load_authorization_config( + oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin +): + """Test load authorization config.""" + oid, username, password = oid_with_credentials_authz + + oid.load_authorization_config(path="tests/data/authz_settings.json") + assert "test-authz-rb-policy" in oid.authorization.policies + assert isinstance(oid.authorization.policies["test-authz-rb-policy"], Policy) + assert len(oid.authorization.policies["test-authz-rb-policy"].roles) == 1 + assert isinstance(oid.authorization.policies["test-authz-rb-policy"].roles[0], Role) + assert len(oid.authorization.policies["test-authz-rb-policy"].permissions) == 2 + assert isinstance( + oid.authorization.policies["test-authz-rb-policy"].permissions[0], Permission + ) From e4c0ff2c7d865237bbd97720b7ce0383198af319 Mon Sep 17 00:00:00 2001 From: Zerek <16066557+Zerek-Cheng@users.noreply.github.com> Date: Wed, 13 Jul 2022 00:45:09 +0800 Subject: [PATCH 070/222] fix: Support the auth_url method called with scope & state params now --- src/keycloak/keycloak_openid.py | 4 +++- src/keycloak/urls_patterns.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index ad608d0..b44915f 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -174,7 +174,7 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) - def auth_url(self, redirect_uri): + def auth_url(self, redirect_uri, scope="email", state=""): """ http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint @@ -185,6 +185,8 @@ class KeycloakOpenID: "authorization-endpoint": self.well_known()["authorization_endpoint"], "client-id": self.client_id, "redirect-uri": redirect_uri, + "scope": scope, + "state": state, } return URL_AUTH.format(**params_path) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 3ec134c..18b1951 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -32,6 +32,7 @@ URL_INTROSPECT = "realms/{realm-name}/protocol/openid-connect/token/introspect" URL_ENTITLEMENT = "realms/{realm-name}/authz/entitlement/{resource-server-id}" URL_AUTH = ( "{authorization-endpoint}?client_id={client-id}&response_type=code&redirect_uri={redirect-uri}" + "&scope={scope}&state={state} " ) # ADMIN URLS From 18ce10c73b9ded0d589098f803504477fad358f6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 20:34:24 +0000 Subject: [PATCH 071/222] test: added authz tests --- src/keycloak/keycloak_openid.py | 1 - tests/test_keycloak_openid.py | 83 +++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 83575b2..0a45dc3 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -511,7 +511,6 @@ class KeycloakOpenID: self.connection.add_param_headers("Authorization", "Bearer " + token) data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) - return raise_error_from_response(data_raw, KeycloakPostError) def has_uma_access(self, token, permissions): diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index f2c0f7e..0e94cd3 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -10,7 +10,9 @@ from keycloak.authorization.role import Role from keycloak.connection import ConnectionManager from keycloak.exceptions import ( KeycloakAuthenticationError, + KeycloakAuthorizationConfigError, KeycloakDeprecationError, + KeycloakInvalidTokenError, KeycloakRPTNotFound, ) from keycloak.keycloak_admin import KeycloakAdmin @@ -257,9 +259,7 @@ def test_decode_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): ) -def test_load_authorization_config( - oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin -): +def test_load_authorization_config(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): """Test load authorization config.""" oid, username, password = oid_with_credentials_authz @@ -272,3 +272,80 @@ def test_load_authorization_config( assert isinstance( oid.authorization.policies["test-authz-rb-policy"].permissions[0], Permission ) + + +def test_get_policies(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): + """Test get policies.""" + oid, username, password = oid_with_credentials_authz + token = oid.token(username=username, password=password) + + with pytest.raises(KeycloakAuthorizationConfigError): + oid.get_policies(token=token["access_token"]) + + oid.load_authorization_config(path="tests/data/authz_settings.json") + assert oid.get_policies(token=token["access_token"]) is None + + key = "-----BEGIN PUBLIC KEY-----\n" + oid.public_key() + "\n-----END PUBLIC KEY-----" + orig_client_id = oid.client_id + oid.client_id = "account" + assert oid.get_policies(token=token["access_token"], method_token_info="decode", key=key) == [] + policy = Policy(name="test", type="role", logic="POSITIVE", decision_strategy="UNANIMOUS") + policy.add_role(role="account/view-profile") + oid.authorization.policies["test"] = policy + assert [ + str(x) + for x in oid.get_policies(token=token["access_token"], method_token_info="decode", key=key) + ] == ["Policy: test (role)"] + assert [ + repr(x) + for x in oid.get_policies(token=token["access_token"], method_token_info="decode", key=key) + ] == [""] + oid.client_id = orig_client_id + + oid.logout(refresh_token=token["refresh_token"]) + with pytest.raises(KeycloakInvalidTokenError): + oid.get_policies(token=token["access_token"]) + + +def test_get_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): + """Test get policies.""" + oid, username, password = oid_with_credentials_authz + token = oid.token(username=username, password=password) + + with pytest.raises(KeycloakAuthorizationConfigError): + oid.get_permissions(token=token["access_token"]) + + oid.load_authorization_config(path="tests/data/authz_settings.json") + assert oid.get_permissions(token=token["access_token"]) is None + + key = "-----BEGIN PUBLIC KEY-----\n" + oid.public_key() + "\n-----END PUBLIC KEY-----" + orig_client_id = oid.client_id + oid.client_id = "account" + assert ( + oid.get_permissions(token=token["access_token"], method_token_info="decode", key=key) == [] + ) + policy = Policy(name="test", type="role", logic="POSITIVE", decision_strategy="UNANIMOUS") + policy.add_role(role="account/view-profile") + policy.add_permission( + permission=Permission( + name="test-perm", type="resource", logic="POSITIVE", decision_strategy="UNANIMOUS" + ) + ) + oid.authorization.policies["test"] = policy + assert [ + str(x) + for x in oid.get_permissions( + token=token["access_token"], method_token_info="decode", key=key + ) + ] == ["Permission: test-perm (resource)"] + assert [ + repr(x) + for x in oid.get_permissions( + token=token["access_token"], method_token_info="decode", key=key + ) + ] == [""] + oid.client_id = orig_client_id + + oid.logout(refresh_token=token["refresh_token"]) + with pytest.raises(KeycloakInvalidTokenError): + oid.get_permissions(token=token["access_token"]) From 25f1f687059ee780f9cb99eaf18ce821fb7943ef Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 20:47:18 +0000 Subject: [PATCH 072/222] style: fix docstring for docs pages --- src/keycloak/keycloak_admin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 0825730..52c5677 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1313,7 +1313,7 @@ class KeycloakAdmin: :param role_name: Name of the role. :param query: Additional Query parameters - (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) :return: Keycloak Server Response (UserRepresentation) """ query = query or dict() @@ -1464,7 +1464,7 @@ class KeycloakAdmin: :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters - (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) :return: Keycloak server response (UserRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} @@ -1478,7 +1478,7 @@ class KeycloakAdmin: :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters - (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} @@ -1530,7 +1530,7 @@ class KeycloakAdmin: :param role_name: The name of the role to be updated :param payload: The role (use RoleRepresentation) - :return Keycloak server response + :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( @@ -1543,7 +1543,7 @@ class KeycloakAdmin: """Delete a role for the realm by name. :param payload: The role name {'role-name':'name-of-the-role'} - :return Keycloak server response + :return: Keycloak server response """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( From a07a5eb60f2c3cbb73be966c5141d8c91a7fd85e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 12 Jul 2022 21:08:28 +0000 Subject: [PATCH 073/222] style: removed manifest, applied pre-commit hooks --- LICENSE | 2 +- MANIFEST.in | 4 --- docs/Makefile | 2 +- poetry.lock | 57 ++++------------------------------ pyproject.toml | 1 + tests/data/authz_settings.json | 2 +- tox.ini | 2 +- 7 files changed, 11 insertions(+), 59 deletions(-) delete mode 100644 MANIFEST.in diff --git a/LICENSE b/LICENSE index f193f7d..781617c 100644 --- a/LICENSE +++ b/LICENSE @@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index acf84af..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include LICENSE -include requirements.txt -include dev-requirements.txt -include docs-requirements.txt diff --git a/docs/Makefile b/docs/Makefile index 28027de..c86fc18 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/poetry.lock b/poetry.lock index 06cdcc1..24b870b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -170,7 +170,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "6.4.1" +version = "6.4.2" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -974,15 +974,15 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "zipp" -version = "3.8.0" +version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [extras] docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] @@ -1064,49 +1064,7 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] -coverage = [ - {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, - {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, - {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, - {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, - {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, - {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, - {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, - {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, - {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, - {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, - {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, - {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, - {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, -] +coverage = [] decli = [ {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, @@ -1565,7 +1523,4 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [ - {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, - {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, -] +zipp = [] diff --git a/pyproject.toml b/pyproject.toml index 0e54eaf..3c32037 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ packages = [ { include = "keycloak", from = "src/" }, { include = "keycloak/**/*.py", from = "src/" }, ] +include = ["LICENSE", "CHANGELOG.md", "CODEOWNERS", "CONTRIBUTING.md"] [tool.poetry.urls] Documentation = "https://python-keycloak.readthedocs.io/en/latest/" diff --git a/tests/data/authz_settings.json b/tests/data/authz_settings.json index e051085..8f11198 100644 --- a/tests/data/authz_settings.json +++ b/tests/data/authz_settings.json @@ -42,4 +42,4 @@ ], "scopes": [], "decisionStrategy": "UNANIMOUS" -} \ No newline at end of file +} diff --git a/tox.ini b/tox.ini index 3fbdd66..219a4d5 100644 --- a/tox.ini +++ b/tox.ini @@ -32,7 +32,7 @@ commands = ./test_keycloak_init.sh "pytest -vv --cov=keycloak --cov-report term-missing {posargs}" [testenv:build] -deps = +deps = poetry setenv = POETRY_VIRTUALENVS_CREATE = false From 962133ec01d9135acba959b59276683736676464 Mon Sep 17 00:00:00 2001 From: Zerek <16066557+Zerek-Cheng@users.noreply.github.com> Date: Wed, 13 Jul 2022 08:48:44 +0800 Subject: [PATCH 074/222] docs: update auth_url method's docstring and readme file --- README.md | 13 +++++++++++++ src/keycloak/keycloak_openid.py | 14 ++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d3572f5..d300fa9 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,19 @@ keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", # Get WellKnow config_well_known = keycloak_openid.well_known() +# Get Code With Oauth Authorization Request +auth_url = keycloak_openid.auth_url( + redirect_uri="your_call_back_url", + scope="email", + state="your_state_info") + +# Get Access Token With Code +access_token = keycloak_openid.token( + grant_type='authorization_code', + code='the_code_you_get_from_auth_url_callback', + redirect_uri="your_call_back_url") + + # Get Token token = keycloak_openid.token("user", "password") token = keycloak_openid.token("user", "password", totp="012345") diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index b44915f..85447b2 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -176,10 +176,16 @@ class KeycloakOpenID: def auth_url(self, redirect_uri, scope="email", state=""): """ - - http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint - - :return: + Get authorization URL endpoint. + + :param redirect_uri: Redirect url to receive oauth code + :type redirect_uri: str + :param scope: Scope of authorization request, split with the blank space + :type: scope: str + :param state: State will be returned to the redirect_uri + :type: str + :returns: Authorization URL Full Build + :rtype: str """ params_path = { "authorization-endpoint": self.well_known()["authorization_endpoint"], From 7031123c1fa3462290c20fa11c4c80b6732a5bf7 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 07:31:04 +0000 Subject: [PATCH 075/222] test: finished off openid tests --- src/keycloak/keycloak_openid.py | 4 ++-- tests/conftest.py | 1 - tests/test_keycloak_openid.py | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 0a45dc3..e2fcca1 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -524,7 +524,7 @@ class KeycloakOpenID: try: granted = self.uma_permissions(token, permissions) except (KeycloakPostError, KeycloakAuthenticationError) as e: - if e.response_code == 403: + if e.response_code == 403: # pragma: no cover return AuthStatus( is_logged_in=True, is_authorized=False, missing_permissions=needed ) @@ -540,7 +540,7 @@ class KeycloakOpenID: if not scopes: needed.discard(resource) continue - for scope in scopes: + for scope in scopes: # pragma: no cover needed.discard("{}#{}".format(resource, scope)) return AuthStatus( diff --git a/tests/conftest.py b/tests/conftest.py index 632c51b..5f340ae 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -192,7 +192,6 @@ def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: Keycloak "roles": [{"id": admin.get_realm_role(role_name="offline_access")["id"]}], }, ) - admin.create_client_authz_resource # Create user username = str(uuid.uuid4()) password = str(uuid.uuid4()) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 0e94cd3..55c9d44 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -13,6 +13,7 @@ from keycloak.exceptions import ( KeycloakAuthorizationConfigError, KeycloakDeprecationError, KeycloakInvalidTokenError, + KeycloakPostError, KeycloakRPTNotFound, ) from keycloak.keycloak_admin import KeycloakAdmin @@ -349,3 +350,43 @@ def test_get_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, oid.logout(refresh_token=token["refresh_token"]) with pytest.raises(KeycloakInvalidTokenError): oid.get_permissions(token=token["access_token"]) + + +def test_uma_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): + """Test UMA permissions.""" + oid, username, password = oid_with_credentials_authz + token = oid.token(username=username, password=password) + + assert len(oid.uma_permissions(token=token["access_token"])) == 1 + assert oid.uma_permissions(token=token["access_token"])[0]["rsname"] == "Default Resource" + + +def test_has_uma_access( + oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin +): + """Test has UMA access.""" + oid, username, password = oid_with_credentials_authz + token = oid.token(username=username, password=password) + + assert ( + str(oid.has_uma_access(token=token["access_token"], permissions="")) + == "AuthStatus(is_authorized=True, is_logged_in=True, missing_permissions=set())" + ) + assert ( + str(oid.has_uma_access(token=token["access_token"], permissions="Default Resource")) + == "AuthStatus(is_authorized=True, is_logged_in=True, missing_permissions=set())" + ) + + with pytest.raises(KeycloakPostError): + oid.has_uma_access(token=token["access_token"], permissions="Does not exist") + + oid.logout(refresh_token=token["refresh_token"]) + assert ( + str(oid.has_uma_access(token=token["access_token"], permissions="")) + == "AuthStatus(is_authorized=False, is_logged_in=False, missing_permissions=set())" + ) + assert ( + str(oid.has_uma_access(token=admin.token["access_token"], permissions="Default Resource")) + == "AuthStatus(is_authorized=False, is_logged_in=False, missing_permissions=" + + "{'Default Resource'})" + ) From 81cc71c000d9c9c39e354ef1a683221b7150d8c3 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 07:43:01 +0000 Subject: [PATCH 076/222] test: fix the tests, make them compatible with older python versions --- tests/test_keycloak_openid.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 55c9d44..0324077 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -1,4 +1,5 @@ """Test module for KeycloakOpenID.""" +from typing import Tuple from unittest import mock import pytest @@ -105,11 +106,11 @@ def test_auth_url(env, oid: KeycloakOpenID): res == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}/realms/{oid.realm_name}" + f"/protocol/openid-connect/auth?client_id={oid.client_id}&response_type=code" - + "&redirect_uri=http://test.test/*" + + "&redirect_uri=http://test.test/*&scope=email&state= " ) -def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): +def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): """Test the token method.""" oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -152,7 +153,7 @@ def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): def test_exchange_token( - oid_with_credentials: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin + oid_with_credentials: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): """Test the exchange token method.""" # Verify existing user @@ -218,7 +219,7 @@ def test_public_key(oid: KeycloakOpenID): def test_entitlement( - oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin + oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): """Test entitlement.""" oid, username, password = oid_with_credentials_authz @@ -231,7 +232,7 @@ def test_entitlement( oid.entitlement(token=token["access_token"], resource_server_id=resource_server_id) -def test_introspect(oid_with_credentials: tuple[KeycloakOpenID, str, str]): +def test_introspect(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): """Test introspect.""" oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -245,7 +246,7 @@ def test_introspect(oid_with_credentials: tuple[KeycloakOpenID, str, str]): oid.introspect(token=token["access_token"], token_type_hint="requesting_party_token") -def test_decode_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): +def test_decode_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): """Test decode token.""" oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -260,7 +261,7 @@ def test_decode_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): ) -def test_load_authorization_config(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): +def test_load_authorization_config(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): """Test load authorization config.""" oid, username, password = oid_with_credentials_authz @@ -275,7 +276,7 @@ def test_load_authorization_config(oid_with_credentials_authz: tuple[KeycloakOpe ) -def test_get_policies(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): +def test_get_policies(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): """Test get policies.""" oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -308,7 +309,7 @@ def test_get_policies(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str oid.get_policies(token=token["access_token"]) -def test_get_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): +def test_get_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): """Test get policies.""" oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -352,7 +353,7 @@ def test_get_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, oid.get_permissions(token=token["access_token"]) -def test_uma_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, str]): +def test_uma_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): """Test UMA permissions.""" oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -362,7 +363,7 @@ def test_uma_permissions(oid_with_credentials_authz: tuple[KeycloakOpenID, str, def test_has_uma_access( - oid_with_credentials_authz: tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin + oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): """Test has UMA access.""" oid, username, password = oid_with_credentials_authz From 3052f80fd617b90e8190b427f265ac879bee2572 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 17:08:37 +0000 Subject: [PATCH 077/222] refactor: no need to try if the type check is performed --- src/keycloak/uma_permissions.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 1bf2136..94779f1 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -206,13 +206,9 @@ def build_permission_param(permissions): except AttributeError: pass - try: # treat as any other iterable of permissions - result = set() - for permission in permissions: - if not isinstance(permission, (str, UMAPermission)): - raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) - result.add(str(permission)) - return result - except TypeError: - pass - raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) + result = set() + for permission in permissions: + if not isinstance(permission, (str, UMAPermission)): + raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) + result.add(str(permission)) + return result From 940f022d921c019fdb5aeb76f547d92dc43bd692 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 17:08:52 +0000 Subject: [PATCH 078/222] test: 100% coverage on UMA permissions --- tests/test_uma_permissions.py | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py index 581faf4..14f6302 100644 --- a/tests/test_uma_permissions.py +++ b/tests/test_uma_permissions.py @@ -22,7 +22,32 @@ import re import pytest from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError -from keycloak.uma_permissions import Resource, Scope, build_permission_param +from keycloak.uma_permissions import ( + Resource, + Scope, + build_permission_param, + UMAPermission, + AuthStatus, +) + + +def test_uma_permission_obj(): + """Test generic UMA permission.""" + with pytest.raises(PermissionDefinitionError): + UMAPermission(permission="bad") + + p1 = UMAPermission(permission=Resource("Resource")) + assert p1.resource == "Resource" + assert p1.scope == "" + assert repr(p1) == "Resource" + assert str(p1) == "Resource" + + p2 = UMAPermission(permission=Scope("Scope")) + assert p2.resource == "" + assert p2.scope == "Scope" + assert repr(p2) == "#Scope" + assert str(p2) == "#Scope" + assert {p1, p1} != {p2, p2} def test_resource_with_scope_obj(): @@ -174,3 +199,14 @@ def test_build_permission_misbuilt_dict_non_iterable(): with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": 5}) assert err.match(re.escape("misbuilt permission {'res1': 5}")) + + +def test_auth_status_bool(): + """Test bool method of AuthStatus.""" + assert not bool(AuthStatus(is_logged_in=True, is_authorized=False, missing_permissions="")) + assert bool(AuthStatus(is_logged_in=True, is_authorized=True, missing_permissions="")) + + +def test_build_permission_without_scopes(): + """Test build permission param with scopes.""" + assert build_permission_param(permissions={"Resource": None}) == {"Resource"} From 49ddcdc3a696e9e2f5416d1aa40c7ea5845c6c7b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 22:46:48 +0000 Subject: [PATCH 079/222] fix: turn get_name into a method, use setters in connection manager --- src/keycloak/authorization/role.py | 1 - src/keycloak/connection.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/keycloak/authorization/role.py b/src/keycloak/authorization/role.py index 05da243..4ee6ec9 100644 --- a/src/keycloak/authorization/role.py +++ b/src/keycloak/authorization/role.py @@ -38,7 +38,6 @@ class Role: self.name = name self.required = required - @property def get_name(self): """Get name.""" return self.name diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index 361d95d..44978e5 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -25,7 +25,7 @@ try: from urllib.parse import urljoin -except ImportError: +except ImportError: # pragma: no cover from urlparse import urljoin import requests @@ -46,10 +46,10 @@ class ConnectionManager(object): def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): """Init method.""" - self._base_url = base_url - self._headers = headers - self._timeout = timeout - self._verify = verify + self.base_url = base_url + self.headers = headers + self.timeout = timeout + self.verify = verify self._s = requests.Session() self._s.auth = lambda x: x # don't let requests add auth headers From cab018fa5ab289c3d2e329aac00df22dfa8ae1a1 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 13 Jul 2022 22:47:02 +0000 Subject: [PATCH 080/222] test: added all unit tests --- tests/test_authorization.py | 42 +++++++++++++++++++++++++++++++++++ tests/test_connection.py | 41 ++++++++++++++++++++++++++++++++++ tests/test_exceptions.py | 20 +++++++++++++++++ tests/test_uma_permissions.py | 4 ++-- 4 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 tests/test_authorization.py create mode 100644 tests/test_connection.py create mode 100644 tests/test_exceptions.py diff --git a/tests/test_authorization.py b/tests/test_authorization.py new file mode 100644 index 0000000..a9ffc54 --- /dev/null +++ b/tests/test_authorization.py @@ -0,0 +1,42 @@ +"""Test authorization module.""" +import pytest + +from keycloak.authorization import Permission, Policy, Role +from keycloak.exceptions import KeycloakAuthorizationConfigError + + +def test_authorization_objects(): + """Test authorization objects.""" + # Test permission + p = Permission(name="test", type="test", logic="test", decision_strategy="test") + assert p.name == "test" + assert p.type == "test" + assert p.logic == "test" + assert p.decision_strategy == "test" + p.resources = ["test"] + assert p.resources == ["test"] + p.scopes = ["test"] + assert p.scopes == ["test"] + + # Test policy + p = Policy(name="test", type="test", logic="test", decision_strategy="test") + assert p.name == "test" + assert p.type == "test" + assert p.logic == "test" + assert p.decision_strategy == "test" + p.roles = ["test"] + assert p.roles == ["test"] + p.permissions = ["test"] + assert p.permissions == ["test"] + p.add_permission(permission="test2") + assert p.permissions == ["test", "test2"] + with pytest.raises(KeycloakAuthorizationConfigError): + p.add_role(role="test2") + + # Test role + r = Role(name="test") + assert r.name == "test" + assert not r.required + assert r.get_name() == "test" + assert r == r + assert r == "test" diff --git a/tests/test_connection.py b/tests/test_connection.py new file mode 100644 index 0000000..85730cd --- /dev/null +++ b/tests/test_connection.py @@ -0,0 +1,41 @@ +"""Connection test module.""" + +import pytest + +from keycloak.connection import ConnectionManager +from keycloak.exceptions import KeycloakConnectionError + + +def test_connection_proxy(): + """Test proxies of connection manager.""" + cm = ConnectionManager( + base_url="http://test.test", proxies={"http://test.test": "localhost:8080"} + ) + assert cm._s.proxies == {"http://test.test": "localhost:8080"} + + +def test_headers(): + """Test headers manipulation.""" + cm = ConnectionManager(base_url="http://test.test", headers={"H": "A"}) + assert cm.param_headers(key="H") == "A" + assert cm.param_headers(key="A") is None + cm.clean_headers() + assert cm.headers == dict() + cm.add_param_headers(key="H", value="B") + assert cm.exist_param_headers(key="H") + assert not cm.exist_param_headers(key="B") + cm.del_param_headers(key="H") + assert not cm.exist_param_headers(key="H") + + +def test_bad_connection(): + """Test bad connection.""" + cm = ConnectionManager(base_url="http://not.real.domain") + with pytest.raises(KeycloakConnectionError): + cm.raw_get(path="bad") + with pytest.raises(KeycloakConnectionError): + cm.raw_delete(path="bad") + with pytest.raises(KeycloakConnectionError): + cm.raw_post(path="bad", data={}) + with pytest.raises(KeycloakConnectionError): + cm.raw_put(path="bad", data={}) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000..72e7161 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,20 @@ +"""Test the exceptions module.""" + +from unittest.mock import Mock + +import pytest + +from keycloak.exceptions import KeycloakOperationError, raise_error_from_response + + +def test_raise_error_from_response_from_dict(): + """Test raise error from response using a dictionary.""" + response = Mock() + response.json.return_value = {"key": "value"} + response.status_code = 408 + response.content = "Error" + + with pytest.raises(KeycloakOperationError): + raise_error_from_response( + response=response, error=dict(), expected_codes=[200], skip_exists=False + ) diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py index 14f6302..8179ff9 100644 --- a/tests/test_uma_permissions.py +++ b/tests/test_uma_permissions.py @@ -23,11 +23,11 @@ import pytest from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError from keycloak.uma_permissions import ( + AuthStatus, Resource, Scope, - build_permission_param, UMAPermission, - AuthStatus, + build_permission_param, ) From 1c6524e4db5f3e810c7344245400712ee7287f72 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 17 Jul 2022 11:19:45 +0200 Subject: [PATCH 081/222] fix: check client existence based on clientId Remove the necessity for supplying client name for create a new client request, also don't check existing clients based on client name as those can be duplicate BREAKING CHANGE: Renamed parameter client_name to client_id in get_client_id method Closes #351 --- src/keycloak/keycloak_admin.py | 8 ++++---- tests/test_keycloak_admin.py | 10 +++++----- tests/test_keycloak_openid.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 8d65b62..00501ef 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1046,19 +1046,19 @@ class KeycloakAdmin: data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def get_client_id(self, client_name): + def get_client_id(self, client_id): """Get internal keycloak client id from client-id. This is required for further actions against this client. - :param client_name: name in ClientRepresentation + :param client_id: clientId in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: client_id (uuid as string) """ clients = self.get_clients() for client in clients: - if client_name == client.get("name") or client_name == client.get("clientId"): + if client_id == client.get("clientId"): return client["id"] return None @@ -1237,7 +1237,7 @@ class KeycloakAdmin: :return: Client ID """ if skip_exists: - client_id = self.get_client_id(client_name=payload["name"]) + client_id = self.get_client_id(client_id=payload["clientId"]) if client_id is not None: return client_id diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 069bb61..106e15a 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -655,8 +655,8 @@ def test_clients(admin: KeycloakAdmin, realm: str): assert len(admin.get_clients()) == 7 # Test get client id - assert admin.get_client_id(client_name="test-client") == client_id - assert admin.get_client_id(client_name="does-not-exist") is None + assert admin.get_client_id(client_id="test-client") == client_id + assert admin.get_client_id(client_id="does-not-exist") is None # Test update client res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"}) @@ -856,7 +856,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"Could not find client"}\'') secrets = admin.get_client_secrets( - client_id=admin.get_client_id(client_name="test-confidential") + client_id=admin.get_client_id(client_id="test-confidential") ) assert secrets == {"type": "secret", "value": "test-secret"} @@ -865,11 +865,11 @@ def test_clients(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"Could not find client"}\'') res = admin.generate_client_secrets( - client_id=admin.get_client_id(client_name="test-confidential") + client_id=admin.get_client_id(client_id="test-confidential") ) assert res assert ( - admin.get_client_secrets(client_id=admin.get_client_id(client_name="test-confidential")) + admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential")) == res ) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 0324077..8eccb0f 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -163,10 +163,10 @@ def test_exchange_token( admin.realm_name = oid.realm_name admin.assign_client_role( user_id=admin.get_user_id(username=username), - client_id=admin.get_client_id(client_name="realm-management"), + client_id=admin.get_client_id(client_id="realm-management"), roles=[ admin.get_client_role( - client_id=admin.get_client_id(client_name="realm-management"), + client_id=admin.get_client_id(client_id="realm-management"), role_name="impersonation", ) ], From 5bc5d4f32152f947e369bcd267b1194815734a51 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Sun, 17 Jul 2022 21:10:41 +0200 Subject: [PATCH 082/222] feat: add functions covering some missing REST API calls --- poetry.lock | 8 ++++++ src/keycloak/keycloak_admin.py | 51 ++++++++++++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 8 ++++++ 3 files changed, 67 insertions(+) diff --git a/poetry.lock b/poetry.lock index 24b870b..ae1c1f0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -688,6 +688,14 @@ category = "main" optional = false python-versions = ">=3.6,<4" +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=3.6,<4" + [package.dependencies] pyasn1 = ">=0.1.3" diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 00501ef..4d2c368 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -29,6 +29,8 @@ import json from builtins import isinstance from typing import Iterable +import copy +from requests_toolbelt import MultipartEncoder from . import urls_patterns from .connection import ConnectionManager @@ -2810,3 +2812,52 @@ class KeycloakAdmin: data=json.dumps(payload), ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + + def get_composite_client_roles_of_group(self, client_id, group_id): + params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} + data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_role_client_level_children(self, client_id, role_id): + params_path = {"realm-name": self.realm_name, "role-id": role_id, "client-id": client_id} + data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_user_credentials(self, user_id): + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def upload_certificate(self, client_id, certcont): + params_path = {"realm-name": self.realm_name, "id": client_id, "attr": "jwt.credential"} + m = MultipartEncoder( + fields={ + "keystoreFormat": "Certificate PEM", + "file": certcont + } + ) + new_headers = copy.deepcopy(self.connection.headers) + new_headers["Content-Type"] = m.content_type + self.connection.headers = new_headers + data_raw = self.raw_post(urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path), + data=m, headers=new_headers) + return raise_error_from_response(data_raw, KeycloakPostError) + + def get_required_action_by_alias(self, action_alias): + actions = self.get_required_actions() + for a in actions: + if a['alias'] == action_alias: + break + return a + + def get_required_actions(self): + params_path = {"realm-name": self.realm_name} + data_raw = self.raw_get(urls_patterns.URL_ADMIN_REQUIRED_ACTIONS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def update_required_action(self, action_alias, payload): + if not isinstance(payload, str): + payload = json.dumps(payload) + params_path = {"realm-name": self.realm_name, "action-alias": action_alias} + data_raw = self.raw_put(urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload) + return raise_error_from_response(data_raw, KeycloakPutError) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 2990362..325d693 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -183,3 +183,11 @@ URL_ADMIN_USER_FEDERATED_IDENTITY = ( URL_ADMIN_EVENTS = "admin/realms/{realm-name}/events" URL_ADMIN_EVENTS_CONFIG = URL_ADMIN_EVENTS + "/config" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" + +URL_ADMIN_REALM = "admin/realms/{realm-name}" +URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE = URL_ADMIN_GROUPS_CLIENT_ROLES+"/composite" +URL_ADMIN_CLIENT_ROLE_CHILDREN = "admin/realms/{realm-name}/roles-by-id/{role-id}/composites/clients/{client-id}" +URL_ADMIN_USER_CREDENTIALS = URL_ADMIN_USERS+"/{id}/credentials" +URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + '/upload-certificate' +URL_ADMIN_REQUIRED_ACTIONS = URL_ADMIN_REALM + "/authentication/required-actions" +URL_ADMIN_REQUIRED_ACTIONS_ALIAS = URL_ADMIN_REQUIRED_ACTIONS + "/{action-alias}" From 9bff615fec9e1de56abf610ab44dc3973456d187 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 09:23:14 +0200 Subject: [PATCH 083/222] feat: add docstrings --- poetry.lock | 101 +++++++++++++++++++++++++++------ pyproject.toml | 1 + src/keycloak/keycloak_admin.py | 55 ++++++++++++++++++ 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index ae1c1f0..d287c38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -681,17 +681,20 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "rsa" -version = "4.8" -description = "Pure-Python RSA implementation" +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" category = "main" optional = false -python-versions = ">=3.6,<4" +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" [[package]] -name = "requests-toolbelt" -version = "0.9.1" -description = "A utility belt for advanced users of python-requests" +name = "rsa" +version = "4.8" +description = "Pure-Python RSA implementation" category = "main" optional = false python-versions = ">=3.6,<4" @@ -998,7 +1001,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "ed105f41fc20e390af8eeefafd3168bb4b370d3a5135bfdec55aab7fc5d0bb3e" +content-hash = "fb17c4ff35afb569d2bae025578ede6face011dcaa98b1fece090082746d7511" [metadata.files] alabaster = [ @@ -1009,8 +1012,13 @@ argcomplete = [ {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] -astroid = [] -atomicwrites = [] +astroid = [ + {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, + {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -1072,7 +1080,49 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] -coverage = [] +coverage = [ + {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, + {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, + {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, + {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, + {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, + {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, + {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, + {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, + {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, + {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, + {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, + {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, + {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, + {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, + {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, + {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, + {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, +] decli = [ {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, @@ -1085,7 +1135,10 @@ docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] -ecdsa = [] +ecdsa = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, @@ -1247,7 +1300,10 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [] +pre-commit = [ + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, +] prompt-toolkit = [ {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, @@ -1358,6 +1414,10 @@ requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] rsa = [ {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, @@ -1417,7 +1477,10 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [] +tomlkit = [ + {file = "tomlkit-0.11.1-py3-none-any.whl", hash = "sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5"}, + {file = "tomlkit-0.11.1.tar.gz", hash = "sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e"}, +] tox = [ {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, @@ -1456,7 +1519,10 @@ unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, ] -urllib3 = [] +urllib3 = [ + {file = "urllib3-1.26.10-py2.py3-none-any.whl", hash = "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec"}, + {file = "urllib3-1.26.10.tar.gz", hash = "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"}, +] virtualenv = [ {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, @@ -1531,4 +1597,7 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [] +zipp = [ + {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, + {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, +] diff --git a/pyproject.toml b/pyproject.toml index 3c32037..c9f3b90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ sphinx-rtd-theme = {version = "^1.0.0", optional = true} readthedocs-sphinx-ext = {version = "^2.1.8", optional = true} m2r2 = {version = "^0.3.2", optional = true} sphinx-autoapi = {version = "^1.8.4", optional = true} +requests-toolbelt = "^0.9.1" [tool.poetry.dev-dependencies] tox = "^3.25.0" diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 4d2c368..1123885 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2814,21 +2814,55 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def get_composite_client_roles_of_group(self, client_id, group_id): + """Get the composite client roles of the given group for the given client + + :param client_id: id of the client + :type client_id: str + :param group_id: id of the group + :type group_id: str + :return: the composite client roles of the group (list of RoleRepresentation) + :rtype: list + """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_role_client_level_children(self, client_id, role_id): + """Get the child roles of which the given composite client role is composed of + + :param client_id: id of the client + :type client_id: str + :param role_id: id of the role + :type role_id: str + :return: the child roles (list of RoleRepresentation) + :rtype: list + """ params_path = {"realm-name": self.realm_name, "role-id": role_id, "client-id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_user_credentials(self, user_id): + """Get the credentials of the given user + + :param user_id: id of the user + :type user_id: str + :return: the user credentials (list of CredentialsRepresentation) + :rtype: list + """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def upload_certificate(self, client_id, certcont): + """Upload a new certificate for the client + + :param client_id: id of the client + :type client_id: str + :param certcont: the content of the certificate + :type certcont: str + :return: dictionary {"certificate": ""}, where is the content of the uploaded certificate + :rtype: dict + """ params_path = {"realm-name": self.realm_name, "id": client_id, "attr": "jwt.credential"} m = MultipartEncoder( fields={ @@ -2844,6 +2878,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_required_action_by_alias(self, action_alias): + """Get a required action by its alias + + :param action_alias: the alias of the requried action + :type action_alias: str + :return: the required action (RequiredActionProviderRepresentation) + :rtype: dict + """ actions = self.get_required_actions() for a in actions: if a['alias'] == action_alias: @@ -2851,11 +2892,25 @@ class KeycloakAdmin: return a def get_required_actions(self): + """Get the required actions for the realms + + :return: the required actions (list of RequiredActionProviderRepresentation) + :rtype: list + """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REQUIRED_ACTIONS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def update_required_action(self, action_alias, payload): + """Update a required action + + :param action_alias: the action alias + :type action_alias: str + :param payload: the new required action (RequiredActionProviderRepresentation) + :type payload: dict + :return: empty dictionary + :rtype: dict + """ if not isinstance(payload, str): payload = json.dumps(payload) params_path = {"realm-name": self.realm_name, "action-alias": action_alias} From fb942c11d8a9418ce52c6482db34a9d8d1fc00d7 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 14:29:25 +0200 Subject: [PATCH 084/222] feat: add unit tests --- poetry.lock | 134 ++++++++++++++++++++++++++++++++- pyproject.toml | 1 + src/keycloak/keycloak_admin.py | 27 ++++--- src/keycloak/urls_patterns.py | 10 ++- tests/conftest.py | 87 +++++++++++++++++++++ tests/test_keycloak_admin.py | 71 +++++++++++++++++ 6 files changed, 313 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index d287c38..863296e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -98,6 +98,17 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "cfgv" version = "3.3.1" @@ -182,6 +193,25 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "cryptography" +version = "37.0.4" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + [[package]] name = "decli" version = "0.5.2" @@ -510,6 +540,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pydocstyle" version = "6.1.1" @@ -1001,7 +1039,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "fb17c4ff35afb569d2bae025578ede6face011dcaa98b1fece090082746d7511" +content-hash = "add6d6f1a2e9ec5cf7aae027fd49d5fcafd212963daa3257d363909aedb380be" [metadata.files] alabaster = [ @@ -1056,6 +1094,72 @@ certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -1123,6 +1227,30 @@ coverage = [ {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, ] +cryptography = [ + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, + {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, + {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, + {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, +] decli = [ {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, @@ -1331,6 +1459,10 @@ pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, diff --git a/pyproject.toml b/pyproject.toml index c9f3b90..3534b4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ readthedocs-sphinx-ext = {version = "^2.1.8", optional = true} m2r2 = {version = "^0.3.2", optional = true} sphinx-autoapi = {version = "^1.8.4", optional = true} requests-toolbelt = "^0.9.1" +cryptography = "^37.0.4" [tool.poetry.dev-dependencies] tox = "^3.25.0" diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 1123885..58bbaa5 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -26,10 +26,11 @@ """The keycloak admin module.""" +import copy import json from builtins import isinstance from typing import Iterable -import copy + from requests_toolbelt import MultipartEncoder from . import urls_patterns @@ -2824,7 +2825,9 @@ class KeycloakAdmin: :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path) + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_role_client_level_children(self, client_id, role_id): @@ -2864,17 +2867,15 @@ class KeycloakAdmin: :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id, "attr": "jwt.credential"} - m = MultipartEncoder( - fields={ - "keystoreFormat": "Certificate PEM", - "file": certcont - } - ) + m = MultipartEncoder(fields={"keystoreFormat": "Certificate PEM", "file": certcont}) new_headers = copy.deepcopy(self.connection.headers) new_headers["Content-Type"] = m.content_type self.connection.headers = new_headers - data_raw = self.raw_post(urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path), - data=m, headers=new_headers) + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path), + data=m, + headers=new_headers, + ) return raise_error_from_response(data_raw, KeycloakPostError) def get_required_action_by_alias(self, action_alias): @@ -2887,7 +2888,7 @@ class KeycloakAdmin: """ actions = self.get_required_actions() for a in actions: - if a['alias'] == action_alias: + if a["alias"] == action_alias: break return a @@ -2914,5 +2915,7 @@ class KeycloakAdmin: if not isinstance(payload, str): payload = json.dumps(payload) params_path = {"realm-name": self.realm_name, "action-alias": action_alias} - data_raw = self.raw_put(urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload) + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload + ) return raise_error_from_response(data_raw, KeycloakPutError) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 325d693..69a2f1b 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -185,9 +185,11 @@ URL_ADMIN_EVENTS_CONFIG = URL_ADMIN_EVENTS + "/config" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" URL_ADMIN_REALM = "admin/realms/{realm-name}" -URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE = URL_ADMIN_GROUPS_CLIENT_ROLES+"/composite" -URL_ADMIN_CLIENT_ROLE_CHILDREN = "admin/realms/{realm-name}/roles-by-id/{role-id}/composites/clients/{client-id}" -URL_ADMIN_USER_CREDENTIALS = URL_ADMIN_USERS+"/{id}/credentials" -URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + '/upload-certificate' +URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE = URL_ADMIN_GROUPS_CLIENT_ROLES + "/composite" +URL_ADMIN_CLIENT_ROLE_CHILDREN = ( + "admin/realms/{realm-name}/roles-by-id/{role-id}/composites/clients/{client-id}" +) +URL_ADMIN_USER_CREDENTIALS = URL_ADMIN_USERS + "/{id}/credentials" +URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + "/upload-certificate" URL_ADMIN_REQUIRED_ACTIONS = URL_ADMIN_REALM + "/authentication/required-actions" URL_ADMIN_REQUIRED_ACTIONS_ALIAS = URL_ADMIN_REQUIRED_ACTIONS + "/{action-alias}" diff --git a/tests/conftest.py b/tests/conftest.py index 5f340ae..7380999 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -257,3 +257,90 @@ def client(admin: KeycloakAdmin, realm: str) -> str: client_id = admin.create_client(payload={"name": client, "clientId": client}) yield client_id admin.delete_client(client_id=client_id) + + +@pytest.fixture +def client_role(admin: KeycloakAdmin, realm: str, client: str) -> str: + """Fixture for a new random client role.""" + admin.realm_name = realm + role = str(uuid.uuid4()) + admin.create_client_role(client, {"name": role, "composite": False}) + yield role + admin.delete_client_role(client, role) + + +@pytest.fixture +def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_role: str) -> str: + """Fixture for a new random composite client role.""" + admin.realm_name = realm + role = str(uuid.uuid4()) + admin.create_client_role(client, {"name": role, "composite": True}) + role_repr = admin.get_client_role(client, client_role) + admin.add_composite_client_roles_to_role(client, role, roles=[role_repr]) + yield role + admin.delete_client_role(client, role) + + +@pytest.fixture +def selfsigned_cert(): + """Generates self signed certificate for a hostname, and optional IP addresses.""" + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import serialization + from cryptography.hazmat.primitives.asymmetric import rsa + from datetime import datetime, timedelta + import ipaddress + hostname = "testcert" + ip_addresses = None + key = None + # Generate our key + if key is None: + key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend(), + ) + + name = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, hostname) + ]) + + # best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored. + alt_names = [x509.DNSName(hostname)] + + # allow addressing by IP, for when you don't have real DNS (common in most testing scenarios + if ip_addresses: + for addr in ip_addresses: + # openssl wants DNSnames for ips... + alt_names.append(x509.DNSName(addr)) + # ... whereas golang's crypto/tls is stricter, and needs IPAddresses + # note: older versions of cryptography do not understand ip_address objects + alt_names.append(x509.IPAddress(ipaddress.ip_address(addr))) + + san = x509.SubjectAlternativeName(alt_names) + + # path_len=0 means this cert can only sign itself, not other certs. + basic_contraints = x509.BasicConstraints(ca=True, path_length=0) + now = datetime.utcnow() + cert = ( + x509.CertificateBuilder() + .subject_name(name) + .issuer_name(name) + .public_key(key.public_key()) + .serial_number(1000) + .not_valid_before(now) + .not_valid_after(now + timedelta(days=10*365)) + .add_extension(basic_contraints, False) + .add_extension(san, False) + .sign(key, hashes.SHA256(), default_backend()) + ) + cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM) + key_pem = key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + + return cert_pem, key_pem diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 106e15a..20341af 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -2,6 +2,7 @@ import pytest +import copy import keycloak from keycloak import KeycloakAdmin from keycloak.connection import ConnectionManager @@ -1815,3 +1816,73 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): admin.auto_refresh_token = ["get", "post", "put", "delete"] assert admin.delete_realm(realm_name="test-refresh") == dict() + + +def test_get_required_actions(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + ractions = admin.get_required_actions() + assert isinstance(ractions, list) + for ra in ractions: + for key in [ + "alias", + "name", + "providerId", + "enabled", + "defaultAction", + "priority", + "config", + ]: + assert key in ra + + +def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + ractions = admin.get_required_actions() + ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") + assert ra in ractions + assert ra['alias'] == "UPDATE_PASSWORD" + + +def test_update_required_action(admin: KeycloakAdmin, realm: str): + admin.realm_name = realm + ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") + old = copy.deepcopy(ra) + ra['enabled'] = False + admin.update_required_action("UPDATE_PASSWORD", ra) + newra = admin.get_required_action_by_alias("UPDATE_PASSWORD") + assert old != newra + assert newra['enabled'] is False + + +def test_get_composite_client_roles_of_group(admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str): + admin.realm_name = realm + role = admin.get_client_role(client, composite_client_role) + admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role]) + result = admin.get_composite_client_roles_of_group(client, group) + assert role['id'] in [x['id'] for x in result] + + +def test_get_role_client_level_children(admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str): + admin.realm_name = realm + child = admin.get_client_role(client, client_role) + parent = admin.get_client_role(client, composite_client_role) + res = admin.get_role_client_level_children(client, parent['id']) + assert child['id'] in [x['id'] for x in res] + + +def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): + admin.realm_name = realm + admin.set_user_password(user, "pwd", temporary=False) + res = admin.get_user_credentials(user) + assert isinstance(res, list) + assert len(res) == 1 + assert res[0]['type'] == "password" + + +def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): + admin.realm_name = realm + cert, _ = selfsigned_cert + cert = cert.decode('utf-8').strip() + admin.upload_certificate(client, cert) + cl = admin.get_client(client) + assert cl['attributes']['jwt.credential.certificate'] == "".join(cert.splitlines()[1:-1]) From 7e50d4313c4d2ead8a2843346a24e8a8a5561703 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 14:49:02 +0200 Subject: [PATCH 085/222] fix: applied tox linting check --- tests/conftest.py | 21 +++++++++------------ tests/test_keycloak_admin.py | 29 +++++++++++++++++------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7380999..44865e2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -284,28 +284,25 @@ def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_ @pytest.fixture def selfsigned_cert(): """Generates self signed certificate for a hostname, and optional IP addresses.""" + import ipaddress + from datetime import datetime, timedelta + from cryptography import x509 - from cryptography.x509.oid import NameOID - from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization + from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa - from datetime import datetime, timedelta - import ipaddress + from cryptography.x509.oid import NameOID + hostname = "testcert" ip_addresses = None key = None # Generate our key if key is None: key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048, - backend=default_backend(), + public_exponent=65537, key_size=2048, backend=default_backend() ) - name = x509.Name([ - x509.NameAttribute(NameOID.COMMON_NAME, hostname) - ]) + name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, hostname)]) # best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored. alt_names = [x509.DNSName(hostname)] @@ -331,7 +328,7 @@ def selfsigned_cert(): .public_key(key.public_key()) .serial_number(1000) .not_valid_before(now) - .not_valid_after(now + timedelta(days=10*365)) + .not_valid_after(now + timedelta(days=10 * 365)) .add_extension(basic_contraints, False) .add_extension(san, False) .sign(key, hashes.SHA256(), default_backend()) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 20341af..76d0cf4 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1,8 +1,9 @@ """Test the keycloak admin object.""" +import copy + import pytest -import copy import keycloak from keycloak import KeycloakAdmin from keycloak.connection import ConnectionManager @@ -1840,34 +1841,38 @@ def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): ractions = admin.get_required_actions() ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") assert ra in ractions - assert ra['alias'] == "UPDATE_PASSWORD" + assert ra["alias"] == "UPDATE_PASSWORD" def test_update_required_action(admin: KeycloakAdmin, realm: str): admin.realm_name = realm ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") old = copy.deepcopy(ra) - ra['enabled'] = False + ra["enabled"] = False admin.update_required_action("UPDATE_PASSWORD", ra) newra = admin.get_required_action_by_alias("UPDATE_PASSWORD") assert old != newra - assert newra['enabled'] is False + assert newra["enabled"] is False -def test_get_composite_client_roles_of_group(admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str): +def test_get_composite_client_roles_of_group( + admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str +): admin.realm_name = realm role = admin.get_client_role(client, composite_client_role) admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role]) result = admin.get_composite_client_roles_of_group(client, group) - assert role['id'] in [x['id'] for x in result] + assert role["id"] in [x["id"] for x in result] -def test_get_role_client_level_children(admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str): +def test_get_role_client_level_children( + admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str +): admin.realm_name = realm child = admin.get_client_role(client, client_role) parent = admin.get_client_role(client, composite_client_role) - res = admin.get_role_client_level_children(client, parent['id']) - assert child['id'] in [x['id'] for x in res] + res = admin.get_role_client_level_children(client, parent["id"]) + assert child["id"] in [x["id"] for x in res] def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): @@ -1876,13 +1881,13 @@ def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): res = admin.get_user_credentials(user) assert isinstance(res, list) assert len(res) == 1 - assert res[0]['type'] == "password" + assert res[0]["type"] == "password" def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): admin.realm_name = realm cert, _ = selfsigned_cert - cert = cert.decode('utf-8').strip() + cert = cert.decode("utf-8").strip() admin.upload_certificate(client, cert) cl = admin.get_client(client) - assert cl['attributes']['jwt.credential.certificate'] == "".join(cert.splitlines()[1:-1]) + assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1]) From 96085b7b1ddfed2dde43d726da6923ae25ab7871 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 15:14:49 +0200 Subject: [PATCH 086/222] fix: applied flake linting checks --- poetry.lock | 8 +++--- pyproject.toml | 2 +- src/keycloak/keycloak_admin.py | 49 +++++++++++++++++----------------- tests/conftest.py | 4 +-- tests/test_keycloak_admin.py | 7 +++++ 5 files changed, 38 insertions(+), 32 deletions(-) diff --git a/poetry.lock b/poetry.lock index 863296e..e65379e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -102,7 +102,7 @@ python-versions = ">=3.6" name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" +category = "dev" optional = false python-versions = "*" @@ -197,7 +197,7 @@ toml = ["tomli"] name = "cryptography" version = "37.0.4" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -544,7 +544,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -1039,7 +1039,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "add6d6f1a2e9ec5cf7aae027fd49d5fcafd212963daa3257d363909aedb380be" +content-hash = "b6851001fb6b3e331a39e6bab1fa2ed99fdc555e9683137c20c9c593c0e1c040" [metadata.files] alabaster = [ diff --git a/pyproject.toml b/pyproject.toml index 3534b4c..8adfa58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,6 @@ readthedocs-sphinx-ext = {version = "^2.1.8", optional = true} m2r2 = {version = "^0.3.2", optional = true} sphinx-autoapi = {version = "^1.8.4", optional = true} requests-toolbelt = "^0.9.1" -cryptography = "^37.0.4" [tool.poetry.dev-dependencies] tox = "^3.25.0" @@ -56,6 +55,7 @@ black = "^22.3.0" flake8 = "^3.5.0" flake8-docstrings = "^1.6.0" commitizen = "^2.28.0" +cryptography = "^37.0.4" [tool.poetry.extras] docs = [ diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 58bbaa5..12350a8 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2815,13 +2815,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def get_composite_client_roles_of_group(self, client_id, group_id): - """Get the composite client roles of the given group for the given client + """Get the composite client roles of the given group for the given client. - :param client_id: id of the client + :param client_id: id of the client. :type client_id: str - :param group_id: id of the group + :param group_id: id of the group. :type group_id: str - :return: the composite client roles of the group (list of RoleRepresentation) + :return: the composite client roles of the group (list of RoleRepresentation). :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} @@ -2831,13 +2831,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_role_client_level_children(self, client_id, role_id): - """Get the child roles of which the given composite client role is composed of + """Get the child roles of which the given composite client role is composed of. - :param client_id: id of the client + :param client_id: id of the client. :type client_id: str - :param role_id: id of the role + :param role_id: id of the role. :type role_id: str - :return: the child roles (list of RoleRepresentation) + :return: the child roles (list of RoleRepresentation). :rtype: list """ params_path = {"realm-name": self.realm_name, "role-id": role_id, "client-id": client_id} @@ -2845,11 +2845,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_credentials(self, user_id): - """Get the credentials of the given user + """Get the credentials of the given user. - :param user_id: id of the user + :param user_id: id of the user. :type user_id: str - :return: the user credentials (list of CredentialsRepresentation) + :return: the user credentials (list of CredentialsRepresentation). :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} @@ -2857,13 +2857,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def upload_certificate(self, client_id, certcont): - """Upload a new certificate for the client + """Upload a new certificate for the client. - :param client_id: id of the client + :param client_id: id of the client. :type client_id: str - :param certcont: the content of the certificate + :param certcont: the content of the certificate. :type certcont: str - :return: dictionary {"certificate": ""}, where is the content of the uploaded certificate + :return: dictionary {"certificate": ""}, + where is the content of the uploaded certificate. :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id, "attr": "jwt.credential"} @@ -2879,11 +2880,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_required_action_by_alias(self, action_alias): - """Get a required action by its alias + """Get a required action by its alias. - :param action_alias: the alias of the requried action + :param action_alias: the alias of the requried action. :type action_alias: str - :return: the required action (RequiredActionProviderRepresentation) + :return: the required action (RequiredActionProviderRepresentation). :rtype: dict """ actions = self.get_required_actions() @@ -2893,9 +2894,9 @@ class KeycloakAdmin: return a def get_required_actions(self): - """Get the required actions for the realms + """Get the required actions for the realms. - :return: the required actions (list of RequiredActionProviderRepresentation) + :return: the required actions (list of RequiredActionProviderRepresentation). :rtype: list """ params_path = {"realm-name": self.realm_name} @@ -2903,13 +2904,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_required_action(self, action_alias, payload): - """Update a required action + """Update a required action. - :param action_alias: the action alias + :param action_alias: the action alias. :type action_alias: str - :param payload: the new required action (RequiredActionProviderRepresentation) + :param payload: the new required action (RequiredActionProviderRepresentation). :type payload: dict - :return: empty dictionary + :return: empty dictionary. :rtype: dict """ if not isinstance(payload, str): diff --git a/tests/conftest.py b/tests/conftest.py index 44865e2..85f6a8f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -283,7 +283,7 @@ def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_ @pytest.fixture def selfsigned_cert(): - """Generates self signed certificate for a hostname, and optional IP addresses.""" + """Generate self signed certificate for a hostname, and optional IP addresses.""" import ipaddress from datetime import datetime, timedelta @@ -303,8 +303,6 @@ def selfsigned_cert(): ) name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, hostname)]) - - # best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored. alt_names = [x509.DNSName(hostname)] # allow addressing by IP, for when you don't have real DNS (common in most testing scenarios diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 76d0cf4..c4ba3d8 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1820,6 +1820,7 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): def test_get_required_actions(admin: KeycloakAdmin, realm: str): + """Test requried actions.""" admin.realm_name = realm ractions = admin.get_required_actions() assert isinstance(ractions, list) @@ -1837,6 +1838,7 @@ def test_get_required_actions(admin: KeycloakAdmin, realm: str): def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): + """Test get required action by alias.""" admin.realm_name = realm ractions = admin.get_required_actions() ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") @@ -1845,6 +1847,7 @@ def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): def test_update_required_action(admin: KeycloakAdmin, realm: str): + """Test update required action.""" admin.realm_name = realm ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") old = copy.deepcopy(ra) @@ -1858,6 +1861,7 @@ def test_update_required_action(admin: KeycloakAdmin, realm: str): def test_get_composite_client_roles_of_group( admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str ): + """Test get composite client roles of group.""" admin.realm_name = realm role = admin.get_client_role(client, composite_client_role) admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role]) @@ -1868,6 +1872,7 @@ def test_get_composite_client_roles_of_group( def test_get_role_client_level_children( admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str ): + """Test get children of composite client role.""" admin.realm_name = realm child = admin.get_client_role(client, client_role) parent = admin.get_client_role(client, composite_client_role) @@ -1876,6 +1881,7 @@ def test_get_role_client_level_children( def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): + """Test get user credentials.""" admin.realm_name = realm admin.set_user_password(user, "pwd", temporary=False) res = admin.get_user_credentials(user) @@ -1885,6 +1891,7 @@ def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): + """Test upload certificate.""" admin.realm_name = realm cert, _ = selfsigned_cert cert = cert.decode("utf-8").strip() From 7eb56d53888f5af20710726592467d4dd25e67d3 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 15:42:51 +0200 Subject: [PATCH 087/222] fix: applied tox -e docs --- src/keycloak/urls_patterns.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 69a2f1b..10bd99c 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -184,12 +184,10 @@ URL_ADMIN_EVENTS = "admin/realms/{realm-name}/events" URL_ADMIN_EVENTS_CONFIG = URL_ADMIN_EVENTS + "/config" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" -URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE = URL_ADMIN_GROUPS_CLIENT_ROLES + "/composite" URL_ADMIN_CLIENT_ROLE_CHILDREN = ( "admin/realms/{realm-name}/roles-by-id/{role-id}/composites/clients/{client-id}" ) -URL_ADMIN_USER_CREDENTIALS = URL_ADMIN_USERS + "/{id}/credentials" URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + "/upload-certificate" URL_ADMIN_REQUIRED_ACTIONS = URL_ADMIN_REALM + "/authentication/required-actions" URL_ADMIN_REQUIRED_ACTIONS_ALIAS = URL_ADMIN_REQUIRED_ACTIONS + "/{action-alias}" From 2d217eca1c30aa48fb02ea3b15900f5bc50e5268 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 16:30:26 +0200 Subject: [PATCH 088/222] fix: remove duplicate function --- src/keycloak/keycloak_admin.py | 12 ------------ tests/test_keycloak_admin.py | 10 ---------- 2 files changed, 22 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 12350a8..66c1fe6 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2844,18 +2844,6 @@ class KeycloakAdmin: data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def get_user_credentials(self, user_id): - """Get the credentials of the given user. - - :param user_id: id of the user. - :type user_id: str - :return: the user credentials (list of CredentialsRepresentation). - :rtype: list - """ - params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) - return raise_error_from_response(data_raw, KeycloakGetError) - def upload_certificate(self, client_id, certcont): """Upload a new certificate for the client. diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index c4ba3d8..6d62f22 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1880,16 +1880,6 @@ def test_get_role_client_level_children( assert child["id"] in [x["id"] for x in res] -def test_get_user_credentials(admin: KeycloakAdmin, realm: str, user: str): - """Test get user credentials.""" - admin.realm_name = realm - admin.set_user_password(user, "pwd", temporary=False) - res = admin.get_user_credentials(user) - assert isinstance(res, list) - assert len(res) == 1 - assert res[0]["type"] == "password" - - def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): """Test upload certificate.""" admin.realm_name = realm From 4b95d509e8b14e6afca249b519ecc4ddac06bd34 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 16:37:00 +0200 Subject: [PATCH 089/222] fix: moved imports at the top of the file --- tests/conftest.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 85f6a8f..1815d39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,14 @@ import os import uuid - +import ipaddress +from datetime import datetime, timedelta + +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID import pytest from keycloak import KeycloakAdmin, KeycloakOpenID @@ -284,15 +291,6 @@ def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_ @pytest.fixture def selfsigned_cert(): """Generate self signed certificate for a hostname, and optional IP addresses.""" - import ipaddress - from datetime import datetime, timedelta - - from cryptography import x509 - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import hashes, serialization - from cryptography.hazmat.primitives.asymmetric import rsa - from cryptography.x509.oid import NameOID - hostname = "testcert" ip_addresses = None key = None From 067673f81b2748a55ac02e1739815ba3a80fa76e Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 17:11:04 +0200 Subject: [PATCH 090/222] fix: now get_required_action_by_alias now returns None if action does not exist --- src/keycloak/keycloak_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 66c1fe6..7d16ad5 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -2878,8 +2878,8 @@ class KeycloakAdmin: actions = self.get_required_actions() for a in actions: if a["alias"] == action_alias: - break - return a + return a + return None def get_required_actions(self): """Get the required actions for the realms. From 83d6cf36f637aeb613656b1897dc73f38a76d044 Mon Sep 17 00:00:00 2001 From: Luca Paganin Date: Mon, 18 Jul 2022 21:41:35 +0200 Subject: [PATCH 091/222] fix: linting --- tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1815d39..39bd584 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,16 +1,16 @@ """Fixtures for tests.""" +import ipaddress import os import uuid -import ipaddress from datetime import datetime, timedelta +import pytest from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID -import pytest from keycloak import KeycloakAdmin, KeycloakOpenID From e6c4b2810860efca1f32e2a20afc3a4ed350703d Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 19 Jul 2022 17:43:48 +0200 Subject: [PATCH 092/222] fix: removed whitespace from urls --- src/keycloak/urls_patterns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 10bd99c..3f4151e 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -34,7 +34,7 @@ URL_INTROSPECT = "realms/{realm-name}/protocol/openid-connect/token/introspect" URL_ENTITLEMENT = "realms/{realm-name}/authz/entitlement/{resource-server-id}" URL_AUTH = ( "{authorization-endpoint}?client_id={client-id}&response_type=code&redirect_uri={redirect-uri}" - "&scope={scope}&state={state} " + "&scope={scope}&state={state}" ) # ADMIN URLS From 905e0caa038ed77b3bb8ed9a442ded435c3fb7c4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 19 Jul 2022 17:44:09 +0200 Subject: [PATCH 093/222] test: improved the tests on urls, back to 100% --- tests/test_keycloak_admin.py | 1 + tests/test_keycloak_openid.py | 2 +- tests/test_urls_patterns.py | 12 ++++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 6d62f22..2d34a18 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1844,6 +1844,7 @@ def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") assert ra in ractions assert ra["alias"] == "UPDATE_PASSWORD" + assert admin.get_required_action_by_alias("does-not-exist") is None def test_update_required_action(admin: KeycloakAdmin, realm: str): diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 8eccb0f..6bee648 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -106,7 +106,7 @@ def test_auth_url(env, oid: KeycloakOpenID): res == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}/realms/{oid.realm_name}" + f"/protocol/openid-connect/auth?client_id={oid.client_id}&response_type=code" - + "&redirect_uri=http://test.test/*&scope=email&state= " + + "&redirect_uri=http://test.test/*&scope=email&state=" ) diff --git a/tests/test_urls_patterns.py b/tests/test_urls_patterns.py index 5fae847..5c412b2 100644 --- a/tests/test_urls_patterns.py +++ b/tests/test_urls_patterns.py @@ -1,5 +1,5 @@ """Test URL patterns.""" - +import inspect from keycloak import urls_patterns @@ -15,7 +15,12 @@ def test_correctness_of_patterns(): # Test that the patterns have unique names seen_urls = list() - for url in urls: + urls_from_src = [ + x.split("=")[0].strip() + for x in inspect.getsource(urls_patterns).splitlines() + if x.startswith("URL_") + ] + for url in urls_from_src: assert url not in seen_urls, f"The url pattern {url} is present twice." seen_urls.append(url) @@ -24,4 +29,7 @@ def test_correctness_of_patterns(): for url in urls: url_value = urls_patterns.__dict__[url] assert url_value not in seen_url_values, f"The url {url} has a duplicate value {url_value}" + assert ( + url_value == url_value.strip() + ), f"The url {url} with value '{url_value}' has whitespace values" seen_url_values.append(url_value) From 0a5e39d8a5d69fe4cfaeda8d353a77985eb1b729 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 19 Jul 2022 17:45:08 +0200 Subject: [PATCH 094/222] chore: updated dependencies --- poetry.lock | 263 +++++----------------------------------------------- 1 file changed, 25 insertions(+), 238 deletions(-) diff --git a/poetry.lock b/poetry.lock index e65379e..c636fa2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -222,7 +222,7 @@ python-versions = ">=3.6" [[package]] name = "distlib" -version = "0.3.4" +version = "0.3.5" description = "Distribution utilities" category = "dev" optional = false @@ -1050,13 +1050,8 @@ argcomplete = [ {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] -astroid = [ - {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, - {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] +astroid = [] +atomicwrites = [] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -1065,109 +1060,17 @@ babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] -black = [ - {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, - {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, - {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, - {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, - {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, - {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, - {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, - {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, - {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, - {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, - {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, - {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, - {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, - {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, - {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, - {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, - {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, - {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, - {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, - {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, - {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, - {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, - {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, -] +black = [] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] +cffi = [] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] -charset-normalizer = [ - {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, - {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, -] +charset-normalizer = [] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -1176,97 +1079,23 @@ colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] -commitizen = [ - {file = "commitizen-2.28.0-py3-none-any.whl", hash = "sha256:d222f68da12a3ebcaf85c270f19eec7caacbe904349f1823deca6b5e7c2fc0f5"}, - {file = "commitizen-2.28.0.tar.gz", hash = "sha256:8510b67e4c45131ef75114aeca5fe30b4f973b2b943457cf1667177af296192e"}, -] +commitizen = [] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] -coverage = [ - {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, - {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, - {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, - {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, - {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, - {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, - {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, - {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, - {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, - {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, - {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, - {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, - {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, -] -cryptography = [ - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, - {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, - {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, - {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, -] +coverage = [] +cryptography = [] decli = [ {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, ] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] +distlib = [] docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] -ecdsa = [ - {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, - {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, -] +ecdsa = [] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, @@ -1275,10 +1104,7 @@ flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] -flake8-docstrings = [ - {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, - {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, -] +flake8-docstrings = [] identify = [ {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, @@ -1287,10 +1113,7 @@ idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] -imagesize = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] +imagesize = [] importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, @@ -1428,14 +1251,8 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, -] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, - {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, -] +pre-commit = [] +prompt-toolkit = [] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -1459,14 +1276,8 @@ pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, -] +pycparser = [] +pydocstyle = [] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, @@ -1542,14 +1353,8 @@ recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, ] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] -requests-toolbelt = [ - {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, - {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, -] +requests = [] +requests-toolbelt = [] rsa = [ {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, @@ -1609,14 +1414,8 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [ - {file = "tomlkit-0.11.1-py3-none-any.whl", hash = "sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5"}, - {file = "tomlkit-0.11.1.tar.gz", hash = "sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e"}, -] -tox = [ - {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, - {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, -] +tomlkit = [] +tox = [] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, @@ -1643,22 +1442,13 @@ typed-ast = [ {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] -typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] +typing-extensions = [] unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, ] -urllib3 = [ - {file = "urllib3-1.26.10-py2.py3-none-any.whl", hash = "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec"}, - {file = "urllib3-1.26.10.tar.gz", hash = "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"}, -] -virtualenv = [ - {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, - {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, -] +urllib3 = [] +virtualenv = [] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, @@ -1729,7 +1519,4 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [ - {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, - {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, -] +zipp = [] From 37ef5e9f96c54351a5ef247c2a4ce87ff1f861c6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 19 Jul 2022 17:51:17 +0200 Subject: [PATCH 095/222] refactor: applied linting --- tests/test_urls_patterns.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_urls_patterns.py b/tests/test_urls_patterns.py index 5c412b2..91aaaad 100644 --- a/tests/test_urls_patterns.py +++ b/tests/test_urls_patterns.py @@ -1,5 +1,6 @@ """Test URL patterns.""" import inspect + from keycloak import urls_patterns From 15fbe3683c5c119c02e664c2359a3d65bbe09ba4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 19 Jul 2022 21:53:10 +0200 Subject: [PATCH 096/222] docs: added changelog creation with cicd --- .github/workflows/lint.yaml | 4 +- .github/workflows/publish.yaml | 10 ++ CHANGELOG.md | 288 +++++++++++++++++++++++++++++++-- docs/source/changelog.rst | 2 + docs/source/index.rst | 1 + tox.ini | 8 +- 6 files changed, 298 insertions(+), 15 deletions(-) create mode 100644 docs/source/changelog.rst diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0f5b146..61534ac 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -77,7 +77,9 @@ jobs: build: runs-on: ubuntu-latest - needs: test + needs: + - test + - check-docs steps: - uses: actions/checkout@v3 - name: Set up Python 3.10 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 693f67b..5c5f97c 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -10,6 +10,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + fetch-depth: '0' - name: Set up Python 3.10 uses: actions/setup-python@v3 with: @@ -31,3 +33,11 @@ jobs: TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} run: | twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* + - name: Run changelog + run: | + tox -e changelog + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "docs: changelog update" + branch: master + file_pattern: CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 4916c27..251c6d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,49 +1,311 @@ # Changelog -All notable changes to this project will be documented in this file. +## v2.1.1 (2022-07-19) -## [0.5.0] - 2017-08-21 +### Refactor + +- applied linting + +### Fix + +- removed whitespace from urls + +## v2.1.0 (2022-07-18) + +### Feat + +- add functions covering some missing REST API calls +- add unit tests +- add docstrings +- add functions covering some missing REST API calls + +### Fix + +- linting +- now get_required_action_by_alias now returns None if action does not exist +- moved imports at the top of the file +- remove duplicate function +- applied tox -e docs +- applied flake linting checks +- applied tox linting check + +## v2.0.0 (2022-07-17) + +### Fix + +- check client existence based on clientId +- check client existence based on clientId + +### BREAKING CHANGE + +- Renamed parameter client_name to client_id in get_client_id method + +## v1.9.1 (2022-07-13) + +### Fix + +- turn get_name into a method, use setters in connection manager + +### Refactor + +- no need to try if the type check is performed + +## v1.9.0 (2022-07-13) + +### Refactor + +- merge master branch into local + +## v1.8.1 (2022-07-13) + +### Fix + +- Support the auth_url method called with scope & state params now +- Support the auth_url method called with scope & state params now +- raise correct exceptions + +### Feat + +- added flake8-docstrings and upgraded dependencies +- use poetry for package management + +### Refactor + +- slight restructure of the base fixtures + +## v1.8.0 (2022-06-22) + +### Feat + +- Ability to set custom timeout for KeycloakOpenId and KeycloakAdmin +- Ability to set custom timeout for KCOpenId and KCAdmin + +## v1.7.0 (2022-06-16) + +### Feat + +- Allow fetching existing policies before calling create_client_authz_client_policy() + +## v1.6.0 (2022-06-13) + +### Feat + +- support token exchange config via admin API + +## v1.5.0 (2022-06-03) + +### Feat + +- Add update_idp +- Add update_idp + +## v1.4.0 (2022-06-02) + +### Feat + +- Add update_mapper_in_idp +- Add update_mapper_in_idp + +## v1.3.0 (2022-05-31) + +## v1.2.0 (2022-05-31) + +### Feat + +- Add get_idp_mappers, fix #329 +- Support Token Exchange. Fixes #305 + +## v1.1.1 (2022-05-27) + +### Fix + +- fixed bugs in events methods +- fixed components bugs +- use param for update client mapper + +## v1.1.0 (2022-05-26) + +### Feat + +- added new methods for client scopes + +## v1.0.1 (2022-05-25) + +### Fix + +- allow query parameters for users count +- allow query parameters for users count + +## v1.0.0 (2022-05-25) + +### Fix + +- correct spelling of public API method + +### BREAKING CHANGE + +- Renames `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` + +## v0.29.1 (2022-05-24) + +### Fix + +- allow client_credentials token if username and password not spec… +- allow client_credentials token if username and password not specified + +## v0.29.0 (2022-05-23) + +### Fix + +- added fixes based on feedback + +## v0.28.3 (2022-05-23) + +### Fix + +- import classes in the base module +- import classes in the base module + +### Feat + +- added UMA-permission request functionality + +## v0.28.2 (2022-05-19) + +### Fix + +- escape when get role fails + +## v0.28.1 (2022-05-19) + +### Fix + +- Add missing keycloak.authorization package +- Add missing keycloak.authorization package + +## v0.28.0 (2022-05-19) + +## v (2022-05-19) + +### Feat + +- added authenticator providers getters +- fixed admin client to pass the tests +- initial setup of CICD and linting + +### Refactor + +- isort conf.py +- Merge branch 'master' into feature/cicd + +### Fix + +- full tox fix ready +- raise correct errors + +## v0.27.1 (2022-05-18) + +### Fix + +- **release**: version bumps for hotfix release + +## v0.27.0 (2022-02-16) + +### Fix + +- handle refresh_token error "Session not active" + +## v0.26.1 (2021-08-30) + +### Feat + +- add KeycloakAdmin.set_events +- add KeycloakAdmin.set_events + +## v0.25.0 (2021-05-05) + +## v0.24.0 (2020-12-18) + +## 0.23.0 (2020-11-19) + +## v0.22.0 (2020-08-16) + +## v0.21.0 (2020-06-30) + +### Feat + +- add components + +## v0.20.0 (2020-04-11) + +## v0.19.0 (2020-02-18) + +## v0.18.0 (2019-12-10) + +## v0.17.6 (2019-10-10) + +## v0.5.0 (2017-08-21) + +### Feat - Basic functions for Keycloak API (well_know, token, userinfo, logout, certs, entitlement, instropect) -## [0.6.0] - 2017-08-23 +## v0.6.0 (2017-08-23) + +### Feat - Added load authorization settings -## [0.7.0] - 2017-08-23 +## v0.7.0 (2017-08-23) + +### Feat - Added polices -## [0.8.0] - 2017-08-23 +## v0.8.0 (2017-08-23) + +### Feat - Added permissions -## [0.9.0] - 2017-09-05 +## v0.9.0 (2017-09-05) + +### Feat - Added functions for Admin Keycloak API -## [0.10.0] - 2017-10-23 +## v0.10.0 (2017-10-23) + +### Feat - Updated libraries versions - Updated Docs -## [0.11.0] - 2017-12-12 +## v0.11.0 (2017-12-12) + +### Feat - Changed Instropect RPT -## [0.12.0] - 2018-01-25 +## v0.12.0 (2018-01-25) + +### Feat - Add groups functions - Add Admin Tasks for user and client role management - Function to trigger user sync from provider -## [0.12.1] - 2018-08-04 +## v0.12.1 (2018-08-04) + +### Feat - Add get_idps - Rework group functions - ## [master] +## master + +### Feat - * Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` - * Add `KeycloakOpenID.token_exchange` to support Token Exchange +- Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` +- Add `KeycloakOpenID.token_exchange` to support Token Exchange diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 0000000..102c113 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,2 @@ +.. mdinclude:: ../../CHANGELOG.md + \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 6dff08d..ecf88ea 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,4 +11,5 @@ :caption: Contents: readme + changelog reference/keycloak/index diff --git a/tox.ini b/tox.ini index 219a4d5..16768bb 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ requires = tox-poetry poetry -envlist = check, apply-check, docs, tests, build +envlist = check, apply-check, docs, tests, build, changelog [testenv] whitelist_externals = @@ -40,6 +40,12 @@ commands = poetry build --format sdist poetry build --format wheel +[testenv:changelog] +setenv = file|tox.env +passenv = CONTAINER_HOST +commands = + cz changelog --incremental + [flake8] max-line-length = 99 docstring-convention = all From 2bf150f7c11ca8fe77cf5c2df08437c83a3c3f7e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sat, 30 Jul 2022 18:05:07 +0200 Subject: [PATCH 097/222] style: start of more checks --- docs/source/conf.py | 1 + poetry.lock | 38 +++-- pyproject.toml | 5 + src/keycloak/authorization/__init__.py | 8 +- src/keycloak/authorization/permission.py | 69 ++++++-- src/keycloak/authorization/policy.py | 78 +++++++-- src/keycloak/authorization/role.py | 27 +++- src/keycloak/connection.py | 115 ++++++++++---- src/keycloak/exceptions.py | 2 +- src/keycloak/keycloak_admin.py | 194 ++++++++++++++++++++--- tests/test_keycloak_admin.py | 2 +- tox.ini | 5 + 12 files changed, 453 insertions(+), 91 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9a5d438..c6bcb60 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -86,6 +86,7 @@ language = "en" # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +suppress_warnings = ["ref.python"] add_function_parentheses = False add_module_names = True diff --git a/poetry.lock b/poetry.lock index c636fa2..9fb3952 100644 --- a/poetry.lock +++ b/poetry.lock @@ -140,6 +140,18 @@ python-versions = ">=3.7" colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +[[package]] +name = "codespell" +version = "2.1.0" +description = "Codespell" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] +hard-encoding-detection = ["chardet"] + [[package]] name = "colorama" version = "0.4.5" @@ -212,6 +224,14 @@ sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + [[package]] name = "decli" version = "0.5.2" @@ -291,7 +311,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.1" +version = "2.5.2" description = "File identification library for Python" category = "dev" optional = false @@ -731,7 +751,7 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rsa" -version = "4.8" +version = "4.9" description = "Pure-Python RSA implementation" category = "main" optional = false @@ -1039,7 +1059,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "b6851001fb6b3e331a39e6bab1fa2ed99fdc555e9683137c20c9c593c0e1c040" +content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0" [metadata.files] alabaster = [ @@ -1075,6 +1095,7 @@ click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] +codespell = [] colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, @@ -1086,6 +1107,7 @@ commonmark = [ ] coverage = [] cryptography = [] +darglint = [] decli = [ {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, @@ -1105,10 +1127,7 @@ flake8 = [ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] flake8-docstrings = [] -identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, -] +identify = [] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, @@ -1355,10 +1374,7 @@ recommonmark = [ ] requests = [] requests-toolbelt = [] -rsa = [ - {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, - {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, -] +rsa = [] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index 8adfa58..7874ca4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,8 @@ flake8 = "^3.5.0" flake8-docstrings = "^1.6.0" commitizen = "^2.28.0" cryptography = "^37.0.4" +codespell = "^2.1.0" +darglint = "^1.8.1" [tool.poetry.extras] docs = [ @@ -80,3 +82,6 @@ line-length = 99 [tool.isort] line_length = 99 profile = "black" + +[tool.darglint] +enable = "DAR104" \ No newline at end of file diff --git a/src/keycloak/authorization/__init__.py b/src/keycloak/authorization/__init__.py index fddd551..feebe0b 100644 --- a/src/keycloak/authorization/__init__.py +++ b/src/keycloak/authorization/__init__.py @@ -44,7 +44,11 @@ class Authorization: @property def policies(self): - """Get policies.""" + """Get policies. + + :returns: Policies + :rtype: dict + """ return self._policies @policies.setter @@ -55,7 +59,7 @@ class Authorization: """Load policies, roles and permissions (scope/resources). :param data: keycloak authorization data (dict) - :returns: None + :type data: dict """ for pol in data["policies"]: if pol["type"] == "role": diff --git a/src/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py index 667f8c3..d1b606f 100644 --- a/src/keycloak/authorization/permission.py +++ b/src/keycloak/authorization/permission.py @@ -45,10 +45,29 @@ class Permission: https://keycloak.gitbooks.io/documentation/authorization_services/topics/permission/overview.html + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ def __init__(self, name, type, logic, decision_strategy): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ self.name = name self.type = type self.logic = logic @@ -57,16 +76,28 @@ class Permission: self.scopes = [] def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: Class representation + :rtype: str + """ return "" % (self.name, self.type) def __str__(self): - """Str method.""" + """Str method. + + :returns: Class string representation + :rtype: str + """ return "Permission: %s (%s)" % (self.name, self.type) @property def name(self): - """Get name.""" + """Get name. + + :returns: name + :rtype: str + """ return self._name @name.setter @@ -75,7 +106,11 @@ class Permission: @property def type(self): - """Get type.""" + """Get type. + + :returns: type + :rtype: str + """ return self._type @type.setter @@ -84,7 +119,11 @@ class Permission: @property def logic(self): - """Get logic.""" + """Get logic. + + :returns: Logic + :rtype: str + """ return self._logic @logic.setter @@ -93,7 +132,11 @@ class Permission: @property def decision_strategy(self): - """Get decision strategy.""" + """Get decision strategy. + + :returns: Decision strategy + :rtype: str + """ return self._decision_strategy @decision_strategy.setter @@ -102,7 +145,11 @@ class Permission: @property def resources(self): - """Get resources.""" + """Get resources. + + :returns: Resources + :rtype: list + """ return self._resources @resources.setter @@ -111,7 +158,11 @@ class Permission: @property def scopes(self): - """Get scopes.""" + """Get scopes. + + :returns: Scopes + :rtype: list + """ return self._scopes @scopes.setter diff --git a/src/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py index 7e03db0..fdf482d 100644 --- a/src/keycloak/authorization/policy.py +++ b/src/keycloak/authorization/policy.py @@ -39,10 +39,29 @@ class Policy: https://keycloak.gitbooks.io/documentation/authorization_services/topics/policy/overview.html + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ def __init__(self, name, type, logic, decision_strategy): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ self.name = name self.type = type self.logic = logic @@ -51,16 +70,28 @@ class Policy: self.permissions = [] def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: Class representation + :rtype: str + """ return "" % (self.name, self.type) def __str__(self): - """Str method.""" + """Str method. + + :returns: Class string representation + :rtype: str + """ return "Policy: %s (%s)" % (self.name, self.type) @property def name(self): - """Get name.""" + """Get name. + + :returns: Name + :rtype: str + """ return self._name @name.setter @@ -69,7 +100,11 @@ class Policy: @property def type(self): - """Get type.""" + """Get type. + + :returns: Type + :rtype: str + """ return self._type @type.setter @@ -78,7 +113,11 @@ class Policy: @property def logic(self): - """Get logic.""" + """Get logic. + + :returns: Logic + :rtype: str + """ return self._logic @logic.setter @@ -87,7 +126,11 @@ class Policy: @property def decision_strategy(self): - """Get decision strategy.""" + """Get decision strategy. + + :returns: Decision strategy + :rtype: str + """ return self._decision_strategy @decision_strategy.setter @@ -96,7 +139,11 @@ class Policy: @property def roles(self): - """Get roles.""" + """Get roles. + + :returns: Roles + :rtype: list + """ return self._roles @roles.setter @@ -105,7 +152,11 @@ class Policy: @property def permissions(self): - """Get permissions.""" + """Get permissions. + + :returns: Permissions + :rtype: list + """ return self._permissions @permissions.setter @@ -115,8 +166,9 @@ class Policy: def add_role(self, role): """Add keycloak role in policy. - :param role: keycloak role. - :return: + :param role: Keycloak role + :type role: keycloak.authorization.Role + :raises KeycloakAuthorizationConfigError: In case of misconfigured policy type """ if self.type != "role": raise KeycloakAuthorizationConfigError( @@ -127,7 +179,7 @@ class Policy: def add_permission(self, permission): """Add keycloak permission in policy. - :param permission: keycloak permission. - :return: + :param permission: Keycloak permission + :type permission: keycloak.authorization.Permission """ self._permissions.append(permission) diff --git a/src/keycloak/authorization/role.py b/src/keycloak/authorization/role.py index 4ee6ec9..3d4c000 100644 --- a/src/keycloak/authorization/role.py +++ b/src/keycloak/authorization/role.py @@ -31,19 +31,40 @@ class Role: manager, and employee are all typical roles that may exist in an organization. https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html + + :param name: Name + :type name: str + :param required: Required role indicator + :type required: bool """ def __init__(self, name, required=False): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param required: Required role indicator + :type required: bool + """ self.name = name self.required = required def get_name(self): - """Get name.""" + """Get name. + + :returns: Name + :rtype: str + """ return self.name def __eq__(self, other): - """Eq method.""" + """Eq method. + + :param other: The other object + :type other: str + :returns: Equality bool + :rtype: bool | NotImplemented + """ if isinstance(other, str): return self.name == other return NotImplemented diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index 44978e5..fcdffb4 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -37,15 +37,32 @@ from .exceptions import KeycloakConnectionError class ConnectionManager(object): """Represents a simple server connection. - :param base_url: (str) The server URL. - :param headers: (dict) The header parameters of the requests to the server. - :param timeout: (int) Timeout to use for requests to the server. - :param verify: (bool) Verify server SSL. - :param proxies: (dict) The proxies servers requests is sent by. + :param base_url: The server URL. + :type base_url: str + :param headers: The header parameters of the requests to the server. + :type headers: dict + :param timeout: Timeout to use for requests to the server. + :type timeout: int + :param verify: Verify server SSL. + :type verify: bool + :param proxies: The proxies servers requests is sent by. + :type proxies: dict """ def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): - """Init method.""" + """Init method. + + :param base_url: The server URL. + :type base_url: str + :param headers: The header parameters of the requests to the server. + :type headers: dict + :param timeout: Timeout to use for requests to the server. + :type timeout: int + :param verify: Verify server SSL. + :type verify: bool + :param proxies: The proxies servers requests is sent by. + :type proxies: dict + """ self.base_url = base_url self.headers = headers self.timeout = timeout @@ -73,7 +90,11 @@ class ConnectionManager(object): @property def base_url(self): - """Return base url in use for requests to the server.""" + """Return base url in use for requests to the server. + + :returns: Base URL + :rtype: str + """ return self._base_url @base_url.setter @@ -82,7 +103,11 @@ class ConnectionManager(object): @property def timeout(self): - """Return timeout in use for request to the server.""" + """Return timeout in use for request to the server. + + :returns: Timeout + :rtype: int + """ return self._timeout @timeout.setter @@ -91,7 +116,11 @@ class ConnectionManager(object): @property def verify(self): - """Return verify in use for request to the server.""" + """Return verify in use for request to the server. + + :returns: Verify indicator + :rtype: bool + """ return self._verify @verify.setter @@ -100,7 +129,11 @@ class ConnectionManager(object): @property def headers(self): - """Return header request to the server.""" + """Return header request to the server. + + :returns: Request headers + :rtype: dict + """ return self._headers @headers.setter @@ -110,8 +143,10 @@ class ConnectionManager(object): def param_headers(self, key): """Return a specific header parameter. - :param key: (str) Header parameters key. + :param key: Header parameters key. + :type key: str :returns: If the header parameters exist, return its value. + :rtype: str """ return self.headers.get(key) @@ -122,32 +157,41 @@ class ConnectionManager(object): def exist_param_headers(self, key): """Check if the parameter exists in the header. - :param key: (str) Header parameters key. + :param key: Header parameters key. + :type key: str :returns: If the header parameters exist, return True. + :rtype: bool """ return self.param_headers(key) is not None def add_param_headers(self, key, value): """Add a single parameter inside the header. - :param key: (str) Header parameters key. - :param value: (str) Value to be added. + :param key: Header parameters key. + :type key: str + :param value: Value to be added. + :type value: str """ self.headers[key] = value def del_param_headers(self, key): """Remove a specific parameter. - :param key: (str) Key of the header parameters. + :param key: Key of the header parameters. + :type key: str """ self.headers.pop(key, None) def raw_get(self, path, **kwargs): """Submit get request to the path. - :param path: (str) Path for request. + :param path: Path for request. + :type path: str + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.get( @@ -163,10 +207,15 @@ class ConnectionManager(object): def raw_post(self, path, data, **kwargs): """Submit post request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.post( @@ -183,10 +232,15 @@ class ConnectionManager(object): def raw_put(self, path, data, **kwargs): """Submit put request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.put( @@ -200,19 +254,24 @@ class ConnectionManager(object): except Exception as e: raise KeycloakConnectionError("Can't connect to server (%s)" % e) - def raw_delete(self, path, data={}, **kwargs): + def raw_delete(self, path, data=None, **kwargs): """Submit delete request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict | None + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.delete( urljoin(self.base_url, path), params=kwargs, - data=data, + data=data or dict(), headers=self.headers, timeout=self.timeout, verify=self.verify, diff --git a/src/keycloak/exceptions.py b/src/keycloak/exceptions.py index 8eb69bf..9d947e3 100644 --- a/src/keycloak/exceptions.py +++ b/src/keycloak/exceptions.py @@ -21,7 +21,7 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Keycloak custom exeptions module.""" +"""Keycloak custom exceptions module.""" import requests diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 7d16ad5..4d3209b 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -49,19 +49,31 @@ class KeycloakAdmin: """Keycloak Admin client. :param server_url: Keycloak server url + :type server_url: str :param username: admin username + :type username: str :param password: admin password + :type password: str :param totp: Time based OTP + :type totp: str :param realm_name: realm name + :type realm_name: str :param client_id: client id + :type client_id: str :param verify: True if want check connection SSL + :type verify: bool :param client_secret_key: client secret key (optional, required only for access type confidential) + :type client_secret_key: str :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict :param user_realm_name: The realm name of the user, if different from realm_name + :type user_realm_name: str :param auto_refresh_token: list of methods that allows automatic token refresh. Ex: ['get', 'put', 'post', 'delete'] + :type auto_refresh_token: list :param timeout: connection timeout in seconds + :type timeout: int """ PAGE_SIZE = 100 @@ -95,7 +107,35 @@ class KeycloakAdmin: auto_refresh_token=None, timeout=60, ): - """Init method.""" + """Init method. + + :param server_url: Keycloak server url + :type server_url: str + :param username: admin username + :type username: str + :param password: admin password + :type password: str + :param totp: Time based OTP + :type totp: str + :param realm_name: realm name + :type realm_name: str + :param client_id: client id + :type client_id: str + :param verify: True if want check connection SSL + :type verify: bool + :param client_secret_key: client secret key + (optional, required only for access type confidential) + :type client_secret_key: str + :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict + :param user_realm_name: The realm name of the user, if different from realm_name + :type user_realm_name: str + :param auto_refresh_token: list of methods that allows automatic token refresh. + Ex: ['get', 'put', 'post', 'delete'] + :type auto_refresh_token: list + :param timeout: connection timeout in seconds + :type timeout: int + """ self.server_url = server_url self.username = username self.password = password @@ -114,7 +154,11 @@ class KeycloakAdmin: @property def server_url(self): - """Get server url.""" + """Get server url. + + :returns: Keycloak server url + :rtype: str + """ return self._server_url @server_url.setter @@ -123,7 +167,11 @@ class KeycloakAdmin: @property def realm_name(self): - """Get realm name.""" + """Get realm name. + + :returns: Realm name + :rtype: str + """ return self._realm_name @realm_name.setter @@ -132,7 +180,11 @@ class KeycloakAdmin: @property def connection(self): - """Get connection.""" + """Get connection. + + :returns: Connection manager + :rtype: ConnectionManager + """ return self._connection @connection.setter @@ -141,7 +193,11 @@ class KeycloakAdmin: @property def client_id(self): - """Get client id.""" + """Get client id. + + :returns: Client id + :rtype: str + """ return self._client_id @client_id.setter @@ -150,7 +206,11 @@ class KeycloakAdmin: @property def client_secret_key(self): - """Get client secret key.""" + """Get client secret key. + + :returns: Client secret key + :rtype: str + """ return self._client_secret_key @client_secret_key.setter @@ -159,7 +219,11 @@ class KeycloakAdmin: @property def verify(self): - """Get verify.""" + """Get verify. + + :returns: Verify indicator + :rtype: bool + """ return self._verify @verify.setter @@ -168,7 +232,11 @@ class KeycloakAdmin: @property def username(self): - """Get username.""" + """Get username. + + :returns: Admin username + :rtype: str + """ return self._username @username.setter @@ -177,7 +245,11 @@ class KeycloakAdmin: @property def password(self): - """Get password.""" + """Get password. + + :returns: Admin password + :rtype: str + """ return self._password @password.setter @@ -186,7 +258,11 @@ class KeycloakAdmin: @property def totp(self): - """Get totp.""" + """Get totp. + + :returns: TOTP + :rtype: str + """ return self._totp @totp.setter @@ -195,7 +271,11 @@ class KeycloakAdmin: @property def token(self): - """Get token.""" + """Get token. + + :returns: Access and refresh token + :rtype: dict + """ return self._token @token.setter @@ -204,12 +284,20 @@ class KeycloakAdmin: @property def auto_refresh_token(self): - """Get auto refresh token.""" + """Get auto refresh token. + + :returns: List of methods for automatic token refresh + :rtype: list + """ return self._auto_refresh_token @property def user_realm_name(self): - """Get user realm name.""" + """Get user realm name. + + :returns: User realm name + :rtype: str + """ return self._user_realm_name @user_realm_name.setter @@ -218,7 +306,11 @@ class KeycloakAdmin: @property def custom_headers(self): - """Get custom headers.""" + """Get custom headers. + + :returns: Custom headers + :rtype: dict + """ return self._custom_headers @custom_headers.setter @@ -247,13 +339,16 @@ class KeycloakAdmin: Wrapper function to paginate GET requests. :param url: The url on which the query is executed + :type url: str :param query: Existing query parameters (optional) + :type query: dict :return: Combined results of paginated queries + :rtype: list """ results = [] - # initalize query if it was called with None + # initialize query if it was called with None if not query: query = {} page = 0 @@ -274,8 +369,16 @@ class KeycloakAdmin: return results def __fetch_paginated(self, url, query=None): - query = query or {} + """Make a specific paginated request. + :param url: The url on which the query is executed + :type url: str + :param query: Pagination settings + :type query: dict + :returns: Response + :rtype: dict + """ + query = query or {} return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError) def import_realm(self, payload): @@ -287,8 +390,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation - + :type payload: dict :return: RealmRepresentation + :rtype: dict """ data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -299,10 +403,13 @@ class KeycloakAdmin: RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport - :param export-clients: Skip if not want to export realm clients - :param export-groups-and-roles: Skip if not want to export realm groups and roles + :param export_clients: Skip if not want to export realm clients + :type export_clients: bool + :param export_groups_and_role: Skip if not want to export realm groups and roles + :type export_groups_and_role: bool :return: realm configurations JSON + :rtype: dict """ params_path = { "realm-name": self.realm_name, @@ -318,6 +425,7 @@ class KeycloakAdmin: """List all realms in Keycloak deployment. :return: realms list + :rtype: list """ data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALMS) return raise_error_from_response(data_raw, KeycloakGetError) @@ -329,7 +437,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation :param realm_name: Realm name (not the realm id) + :type realm_name: str :return: RealmRepresentation + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path)) @@ -342,8 +452,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation + :type payload: dict :param skip_exists: Skip if Realm already exist. - :return: Keycloak server response (RealmRepresentation) + :type skip_exists: bool + :return: Keycloak server response (RealmRepresentation) + :rtype: dict """ data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response( @@ -353,15 +466,18 @@ class KeycloakAdmin: def update_realm(self, realm_name, payload): """Update a realm. - This wil only update top level attributes and will ignore any user, + This will only update top level attributes and will ignore any user, role, or client information in the payload. RealmRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param realm_name: Realm name (not the realm id) + :type realm_name: str :param payload: RealmRepresentation + :type payload: dict :return: Http response + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_put( @@ -373,7 +489,9 @@ class KeycloakAdmin: """Delete a realm. :param realm_name: Realm name (not the realm id) + :type realm_name: str :return: Http response + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path)) @@ -388,7 +506,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param query: Query parameters (optional) + :type query: dict :return: users list + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name} @@ -406,6 +526,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :param: payload: IdentityProviderRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -419,8 +542,12 @@ class KeycloakAdmin: IdentityProviderRepresentation https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource - :param: alias: alias for IdP to update + :param: idp_alias: alias for IdP to update + :type idp_alias: str :param: payload: The IdentityProviderRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} data_raw = self.raw_put( @@ -435,7 +562,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation :param: idp_alias: alias for Idp to add mapper in + :type idp_alias: str :param: payload: IdentityProviderMapperRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} data_raw = self.raw_post( @@ -450,9 +581,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update :param: idp_alias: alias for Idp to fetch mappers + :type idp_alias: str :param: mapper_id: Mapper Id to update + :type mapper_id: str :param: payload: IdentityProviderMapperRepresentation + :type payload: dict :return: Http response + :rtype: dict """ params_path = { "realm-name": self.realm_name, @@ -476,7 +611,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmappers :param: idp_alias: alias for Idp to fetch mappers + :type idp_alias: str :return: array IdentityProviderMapperRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)) @@ -491,6 +628,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :return: array IdentityProviderRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path)) @@ -500,6 +638,9 @@ class KeycloakAdmin: """Delete an ID Provider. :param: idp_alias: idp alias name + :type idp_alias: str + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path)) @@ -514,10 +655,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param payload: UserRepresentation + :type payload: dict :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID. + :type exist_ok: bool :return: UserRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name} @@ -540,8 +684,10 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource :param query: (dict) Query parameters for users count + :type query: dict :return: counter + :rtype: int """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -557,8 +703,10 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param username: id in UserRepresentation + :type username: str :return: user_id + :rtype: str """ lower_user_name = username.lower() users = self.get_users(query={"search": lower_user_name}) @@ -2870,7 +3018,7 @@ class KeycloakAdmin: def get_required_action_by_alias(self, action_alias): """Get a required action by its alias. - :param action_alias: the alias of the requried action. + :param action_alias: the alias of the required action. :type action_alias: str :return: the required action (RequiredActionProviderRepresentation). :rtype: dict diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 2d34a18..f259915 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1820,7 +1820,7 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): def test_get_required_actions(admin: KeycloakAdmin, realm: str): - """Test requried actions.""" + """Test required actions.""" admin.realm_name = realm ractions = admin.get_required_actions() assert isinstance(ractions, list) diff --git a/tox.ini b/tox.ini index 16768bb..06a0855 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ commands = black --check --diff src/keycloak tests docs isort -c --df src/keycloak tests docs flake8 src/keycloak tests docs + codespell src tests docs [testenv:apply-check] commands = @@ -50,3 +51,7 @@ commands = max-line-length = 99 docstring-convention = all ignore = D203, D213, W503 +docstring_style = sphinx + +[darglint] +enable = DAR104 \ No newline at end of file From f61b42a6ec45330fd239f323fcb294a5d4efd4bc Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sat, 30 Jul 2022 18:30:18 +0200 Subject: [PATCH 098/222] test: fixed tests, updated dependencies --- poetry.lock | 70 +++++++++++++++++------------------- test_keycloak_init.sh | 8 +++-- tests/test_keycloak_admin.py | 4 +-- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/poetry.lock b/poetry.lock index c636fa2..8af9cc0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -44,17 +44,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "babel" @@ -117,6 +117,14 @@ category = "dev" optional = false python-versions = ">=3.6.1" +[[package]] +name = "chardet" +version = "5.0.0" +description = "Universal encoding detector for Python 3" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "charset-normalizer" version = "2.1.0" @@ -150,7 +158,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "commitizen" -version = "2.28.0" +version = "2.29.2" description = "Python commitizen client tool" category = "dev" optional = false @@ -158,6 +166,7 @@ python-versions = ">=3.6.2,<4.0.0" [package.dependencies] argcomplete = ">=1.12.1,<2.0.0" +chardet = ">=5.0.0,<6.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" jinja2 = ">=2.10.3" @@ -291,7 +300,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.1" +version = "2.5.2" description = "File identification library for Python" category = "dev" optional = false @@ -731,7 +740,7 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rsa" -version = "4.8" +version = "4.9" description = "Pure-Python RSA implementation" category = "main" optional = false @@ -758,7 +767,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "5.0.2" +version = "5.1.1" description = "Python documentation generator" category = "main" optional = true @@ -768,7 +777,7 @@ python-versions = ">=3.6" alabaster = ">=0.7,<0.8" babel = ">=1.3" colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.19" +docutils = ">=0.14,<0.20" imagesize = "*" importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} Jinja2 = ">=2.3" @@ -785,16 +794,16 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.950)", "docutils-stubs", "types-typed-ast", "types-requests"] +lint = ["flake8 (>=3.5.0)", "flake8-comprehensions", "flake8-bugbear", "isort", "mypy (>=0.971)", "sphinx-lint", "docutils-stubs", "types-typed-ast", "types-requests"] test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinx-autoapi" -version = "1.8.4" +version = "1.9.0" description = "Sphinx API documentation generator" category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] astroid = ">=2.7" @@ -975,7 +984,7 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.10" +version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -988,22 +997,21 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.15.1" +version = "20.16.2" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] distlib = ">=0.3.1,<1" filelock = ">=3.2,<4" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} platformdirs = ">=2,<3" -six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] [[package]] name = "wcwidth" @@ -1052,10 +1060,7 @@ argcomplete = [ ] astroid = [] atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] +attrs = [] babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, @@ -1070,6 +1075,7 @@ cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] +chardet = [] charset-normalizer = [] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, @@ -1105,10 +1111,7 @@ flake8 = [ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] flake8-docstrings = [] -identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, -] +identify = [] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, @@ -1355,10 +1358,7 @@ recommonmark = [ ] requests = [] requests-toolbelt = [] -rsa = [ - {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, - {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, -] +rsa = [] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1367,14 +1367,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -sphinx = [ - {file = "Sphinx-5.0.2-py3-none-any.whl", hash = "sha256:d3e57663eed1d7c5c50895d191fdeda0b54ded6f44d5621b50709466c338d1e8"}, - {file = "Sphinx-5.0.2.tar.gz", hash = "sha256:b18e978ea7565720f26019c702cd85c84376e948370f1cd43d60265010e1c7b0"}, -] -sphinx-autoapi = [ - {file = "sphinx-autoapi-1.8.4.tar.gz", hash = "sha256:8c4ec5fbedc1e6e8f4692bcc4fcd1abcfb9e8dfca8a4ded60ad811a743c22ccc"}, - {file = "sphinx_autoapi-1.8.4-py2.py3-none-any.whl", hash = "sha256:007bf9e24cd2aa0ac0561f67e8bcd6a6e2e8911ef4b4fd54aaba799d8832c8d0"}, -] +sphinx = [] +sphinx-autoapi = [] sphinx-rtd-theme = [ {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index e9c6823..5dc5e53 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -4,8 +4,11 @@ CMD_ARGS=$1 KEYCLOAK_DOCKER_IMAGE="quay.io/keycloak/keycloak:latest" function keycloak_stop() { - docker stop unittest_keycloak &> /dev/null - docker rm unittest_keycloak &> /dev/null + if [ "$(docker ps -q -f name=unittest_keycloak)" ]; then + docker logs unittest_keycloak > keycloak_test_logs.txt + docker stop unittest_keycloak &> /dev/null + docker rm unittest_keycloak &> /dev/null + fi } function keycloak_start() { @@ -29,6 +32,5 @@ keycloak_start eval ${CMD_ARGS} RETURN_VALUE=$? -docker logs unittest_keycloak > keycloak_test_logs.txt exit ${RETURN_VALUE} diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 2d34a18..61685af 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -759,7 +759,6 @@ def test_clients(admin: KeycloakAdmin, realm: str): res = admin.get_client_authz_policies(client_id=auth_client_id) assert len(res) == 1, res assert res[0]["name"] == "Default Policy" - assert len(admin.get_client_authz_policies(client_id=client_id)) == 1 with pytest.raises(KeycloakGetError) as err: admin.get_client_authz_policies(client_id="does-not-exist") @@ -789,7 +788,6 @@ def test_clients(admin: KeycloakAdmin, realm: str): res = admin.get_client_authz_permissions(client_id=auth_client_id) assert len(res) == 1, res assert res[0]["name"] == "Default Permission" - assert len(admin.get_client_authz_permissions(client_id=client_id)) == 1 with pytest.raises(KeycloakGetError) as err: admin.get_client_authz_permissions(client_id="does-not-exist") @@ -1478,7 +1476,7 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str): # Test list of auth providers res = admin.get_authenticator_providers() - assert len(res) == 39 + assert len(res) == 38 res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie") assert res == { From c98189ca6951f12f1023ed3370c9aaa0d81e4aa4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 4 Aug 2022 14:43:56 +0000 Subject: [PATCH 099/222] docs: more docstring linting --- src/keycloak/exceptions.py | 32 +- src/keycloak/keycloak_admin.py | 550 ++++++++++++++++++++++++++++----- 2 files changed, 499 insertions(+), 83 deletions(-) diff --git a/src/keycloak/exceptions.py b/src/keycloak/exceptions.py index 9d947e3..fe46bf4 100644 --- a/src/keycloak/exceptions.py +++ b/src/keycloak/exceptions.py @@ -36,7 +36,15 @@ class KeycloakError(Exception): """ def __init__(self, error_message="", response_code=None, response_body=None): - """Init method.""" + """Init method. + + :param error_message: The error message + :type error_message: str + :param response_code: The code of the response + :type response_code: int + :param response_body: Body of the response + :type response_body: bytes + """ Exception.__init__(self, error_message) self.response_code = response_code @@ -44,7 +52,11 @@ class KeycloakError(Exception): self.error_message = error_message def __str__(self): - """Str method.""" + """Str method. + + :returns: String representation of the object + :rtype: str + """ if self.response_code is not None: return "{0}: {1}".format(self.response_code, self.error_message) else: @@ -136,7 +148,21 @@ class PermissionDefinitionError(Exception): def raise_error_from_response(response, error, expected_codes=None, skip_exists=False): - """Raise an exception for the response.""" + """Raise an exception for the response. + + :param response: The response object + :type response: Response + :param error: Error object to raise + :type error: dict or Exception + :param expected_codes: Set of expected codes, which should not raise the exception + :type expected_codes: Sequence[int] + :param skip_exists: Indicates whether the response on already existing object should be ignored + :type skip_exists: bool + + :returns: Content of the response message + :type: bytes or dict + :raises KeycloakError: In case of unexpected status codes + """ # noqa: DAR401,DAR402 if expected_codes is None: expected_codes = [200, 201, 204] diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 4d3209b..7a86a80 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -715,11 +715,11 @@ class KeycloakAdmin: def get_user(self, user_id): """Get representation of the user. - :param user_id: User id - UserRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation + :param user_id: User id + :type user_id: str :return: UserRepresentation """ params_path = {"realm-name": self.realm_name, "id": user_id} @@ -732,8 +732,9 @@ class KeycloakAdmin: Returns a list of groups of which the user is a member :param user_id: User id - + :type user_id: str :return: user groups list + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)) @@ -743,9 +744,12 @@ class KeycloakAdmin: """Update the user. :param user_id: User id + :type user_id: str :param payload: UserRepresentation + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_put( @@ -757,8 +761,9 @@ class KeycloakAdmin: """Delete the user. :param user_id: User id - + :type user_id: str :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER.format(**params_path)) @@ -774,10 +779,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_credentialrepresentation :param user_id: User id + :type user_id: str :param password: New password + :type password: str :param temporary: True if password is temporary - - :return: + :type temporary: bool + :returns: Response + :rtype: dict """ payload = {"type": "password", "temporary": temporary, "value": password} params_path = {"realm-name": self.realm_name, "id": user_id} @@ -795,7 +803,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id - :return: Keycloak server response (CredentialRepresentation) + :type user_id: str + :returns: Keycloak server response (CredentialRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) @@ -808,8 +818,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id + :type user_id: str :param: credential_id: credential id + :type credential_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -825,7 +838,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout :param user_id: User id - :return: + :type user_id: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post( @@ -840,7 +855,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation :param user_id: User id - :return: List of UserConsentRepresentations + :type user_id: str + :returns: List of UserConsentRepresentations + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)) @@ -852,7 +869,9 @@ class KeycloakAdmin: Returns a list of federated identities/social logins of which the user has been associated with :param user_id: User id - :return: federated identities list + :type user_id: str + :returns: Federated identities list + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get( @@ -864,10 +883,15 @@ class KeycloakAdmin: """Add a federated identity / social login provider to the user. :param user_id: User id + :type user_id: str :param provider_id: Social login provider id + :type provider_id: str :param provider_userid: userid specified by the provider + :type provider_userid: str :param provider_username: username specified by the provider - :return: + :type provider_username: str + :returns: Keycloak server response + :rtype: bytes """ payload = { "identityProvider": provider_id, @@ -885,8 +909,11 @@ class KeycloakAdmin: """Delete a federated identity / social login provider from the user. :param user_id: User id + :type user_id: str :param provider_id: Social login provider id - :return: + :type provider_id: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} data_raw = self.raw_delete( @@ -902,12 +929,18 @@ class KeycloakAdmin: An email contains a link the user can click to perform a set of required actions. :param user_id: User id + :type user_id: str :param payload: A list of actions for the user to complete + :type payload: list :param client_id: Client id (optional) + :type client_id: str :param lifespan: Number of seconds after which the generated token expires (optional) + :type lifespan: int :param redirect_uri: The redirect uri (optional) + :type redirect_uri: str - :return: + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} @@ -924,10 +957,14 @@ class KeycloakAdmin: An email contains a link the user can click to perform a set of required actions. :param user_id: User id + :type user_id: str :param client_id: Client id (optional) + :type client_id: str :param redirect_uri: Redirect uri (optional) + :type redirect_uri: str - :return: + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "redirect_uri": redirect_uri} @@ -941,12 +978,13 @@ class KeycloakAdmin: def get_sessions(self, user_id): """Get sessions associated with the user. - :param user_id: id of user - UserSessionRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation + :param user_id: Id of user + :type user_id: str :return: UserSessionRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)) @@ -959,6 +997,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :return: ServerInfoRepresentation + :rtype: dict """ data_raw = self.raw_get(urls_patterns.URL_ADMIN_SERVER_INFO) return raise_error_from_response(data_raw, KeycloakGetError) @@ -971,7 +1010,10 @@ class KeycloakAdmin: GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param query: Additional query options + :type query: dict :return: array GroupRepresentation + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name} @@ -991,7 +1033,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param group_id: The group id + :type group_id: str :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) @@ -1005,10 +1049,12 @@ class KeycloakAdmin: GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation - :param name: group (GroupRepresentation) + :param group: group (GroupRepresentation) + :type group: dict :param path: group path (string) - + :type path: str :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ for subgroup in group["subGroups"]: if subgroup["path"] == path: @@ -1030,9 +1076,12 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_userrepresentation :param group_id: The group id + :type group_id: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmembers) + :type query: dict :return: Keycloak server response (UserRepresentation) + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name, "id": group_id} @@ -1053,8 +1102,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param path: group path + :type path: str :param search_in_subgroups: True if want search in the subgroups + :type search_in_subgroups: bool :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ groups = self.get_groups() @@ -1074,14 +1126,18 @@ class KeycloakAdmin: def create_group(self, payload, parent=None, skip_exists=False): """Create a group in the Realm. + GroupRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param payload: GroupRepresentation + :type payload: dict :param parent: parent group's id. Required to create a sub-group. + :type parent: str :param skip_exists: If true then do not raise an error if it already exists - - GroupRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :type skip_exists: bool :return: Group id for newly created group or None for an existing group + :rtype: str """ if parent is None: params_path = {"realm-name": self.realm_name} @@ -1106,13 +1162,16 @@ class KeycloakAdmin: def update_group(self, group_id, payload): """Update group, ignores subgroups. - :param group_id: id of group - :param payload: GroupRepresentation with updated information. - GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param group_id: id of group + :type group_id: str + :param payload: GroupRepresentation with updated information. + :type payload: dict + :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( @@ -1126,8 +1185,11 @@ class KeycloakAdmin: Cannot delete group if disabled :param group_id: id of group - :param enabled: boolean + :type group_id: str + :param enabled: Enabled flag + :type enabled: bool :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( @@ -1140,8 +1202,11 @@ class KeycloakAdmin: """Add user to group (user_id and group_id). :param user_id: id of user + :type user_id: str :param group_id: id of group to add to + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_put( @@ -1153,8 +1218,11 @@ class KeycloakAdmin: """Remove user from group (user_id and group_id). :param user_id: id of user + :type user_id: str :param group_id: id of group to remove from + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)) @@ -1164,7 +1232,9 @@ class KeycloakAdmin: """Delete a group in the Realm. :param group_id: id of group to delete + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) @@ -1179,6 +1249,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response (ClientRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)) @@ -1191,7 +1262,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) @@ -1204,7 +1277,9 @@ class KeycloakAdmin: :param client_id: clientId in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: client_id (uuid as string) + :rtype: str """ clients = self.get_clients() @@ -1219,7 +1294,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1232,10 +1309,15 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param payload: ResourceRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_resourcerepresentation + :type payload: dict + :param skip_exists: Skip the creation in case the resource exists + :type skip_exists: bool :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1252,7 +1334,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1263,11 +1347,6 @@ class KeycloakAdmin: def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False): """Create role-based policy of client. - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param payload: No Document - :return: Keycloak server response - Payload example:: payload={ @@ -1282,6 +1361,16 @@ class KeycloakAdmin: ] } + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param payload: No Document + :type payload: dict + :param skip_exists: Skip creation in case the object exists + :type skip_exists: bool + :return: Keycloak server response + :rtype: bytes + """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1296,12 +1385,6 @@ class KeycloakAdmin: def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): """Create resource-based permission of client. - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param payload: PolicyRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_policyrepresentation - :return: Keycloak server response - Payload example:: payload={ @@ -1316,6 +1399,17 @@ class KeycloakAdmin: policy_id ] + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param payload: PolicyRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_policyrepresentation + :type payload: dict + :param skip_exists: Skip creation in case the object already exists + :type skip_exists: bool + :return: Keycloak server response + :rtype: bytes + """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1332,7 +1426,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) @@ -1343,7 +1439,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1356,7 +1454,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1369,7 +1469,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: UserRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1384,8 +1486,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param skip_exists: If true then do not raise an error if client already exists + :type skip_exists: bool :param payload: ClientRepresentation + :type payload: dict :return: Client ID + :rtype: str """ if skip_exists: client_id = self.get_client_id(client_id=payload["clientId"]) @@ -1407,9 +1512,12 @@ class KeycloakAdmin: """Update a client. :param client_id: Client id + :type client_id: str :param payload: ClientRepresentation + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_put( @@ -1424,7 +1532,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: keycloak client id (not oauth client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) @@ -1440,7 +1550,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :param client_id: Client id + :type client_id: str :param provider_id: provider id to specify response format + :type provider_id: str + :returns: Installation providers + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id} data_raw = self.raw_get( @@ -1455,6 +1569,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) @@ -1464,9 +1579,12 @@ class KeycloakAdmin: """Get role members of realm by role name. :param role_name: Name of the role. + :type role_name: str :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) + :type query: dict :return: Keycloak Server Response (UserRepresentation) + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1477,12 +1595,13 @@ class KeycloakAdmin: def get_client_roles(self, client_id): """Get all roles for the client. - :param client_id: id of client (not client-id) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) @@ -1493,13 +1612,15 @@ class KeycloakAdmin: This is required for further actions with this role. - :param client_id: id of client (not client-id) - :param role_name: role’s name (not id!) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + :param client_id: id of client (not client-id) + :type client_id: str + :param role_name: role’s name (not id!) + :type role_name: str :return: role_id + :rtype: str """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) @@ -1510,13 +1631,15 @@ class KeycloakAdmin: This is required for further actions with this role. - :param client_id: id of client (not client-id) - :param role_name: role’s name (not id!) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + :param client_id: id of client (not client-id) + :type client_id: str + :param role_name: role's name (not id!) + :type role_name: str :return: role_id + :rtype: str """ role = self.get_client_role(client_id, role_name) return role.get("id") @@ -1528,9 +1651,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param payload: RoleRepresentation + :type payload: dict :param skip_exists: If true then do not raise an error if client role already exists + :type skip_exists: bool :return: Client role name + :rtype: str """ if skip_exists: try: @@ -1553,9 +1680,13 @@ class KeycloakAdmin: """Add composite roles to client role. :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be updated + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} @@ -1572,8 +1703,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: role's name (not id!) + :type role_name: str :param payload: RoleRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_put( @@ -1588,7 +1724,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: role's name (not id!) + :type role_name: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) @@ -1598,9 +1738,13 @@ class KeycloakAdmin: """Assign a client role to a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} @@ -1614,10 +1758,14 @@ class KeycloakAdmin: """Get members by client role. :param client_id: The client id + :type client_id: str :param role_name: the name of role to be queried. + :type role_name: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + :type query: dict :return: Keycloak server response (UserRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} return self.__fetch_all( @@ -1628,10 +1776,14 @@ class KeycloakAdmin: """Get group members by client role. :param client_id: The client id + :type client_id: str :param role_name: the name of role to be queried. + :type role_name: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + :type query: dict :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} return self.__fetch_all( @@ -1642,8 +1794,11 @@ class KeycloakAdmin: """Create a new role for the realm or client. :param payload: The role (use RoleRepresentation) + :type payload: dict :param skip_exists: If true then do not raise an error if realm role already exists + :type skip_exists: bool :return: Realm role name + :rtype: str """ if skip_exists: try: @@ -1665,11 +1820,13 @@ class KeycloakAdmin: def get_realm_role(self, role_name): """Get realm role by role name. - :param role_name: role's name, not id! - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - :return: role_id + + :param role_name: role's name, not id! + :type role_name: str + :return: role + :rtype: dict """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( @@ -1681,8 +1838,11 @@ class KeycloakAdmin: """Update a role for the realm by name. :param role_name: The name of the role to be updated + :type role_name: str :param payload: The role (use RoleRepresentation) + :type payload: dict :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( @@ -1694,8 +1854,10 @@ class KeycloakAdmin: def delete_realm_role(self, role_name): """Delete a role for the realm by name. - :param payload: The role name {'role-name':'name-of-the-role'} + :param role_name: The role name + :type role_name: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( @@ -1707,8 +1869,11 @@ class KeycloakAdmin: """Add composite roles to the role. :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be updated + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1722,8 +1887,11 @@ class KeycloakAdmin: """Remove composite roles from the role. :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be removed + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1737,7 +1905,9 @@ class KeycloakAdmin: """Get composite roles of the role. :param role_name: The name of the role + :type role_name: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( @@ -1749,8 +1919,11 @@ class KeycloakAdmin: """Assign realm roles to a user. :param user_id: id of user + :type user_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} @@ -1764,8 +1937,11 @@ class KeycloakAdmin: """Delete realm roles of a user. :param user_id: id of user + :type user_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} @@ -1779,7 +1955,9 @@ class KeycloakAdmin: """Get all realm roles for a user. :param user_id: id of user + :type user_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)) @@ -1789,7 +1967,9 @@ class KeycloakAdmin: """Get all available (i.e. unassigned) realm roles for a user. :param user_id: id of user + :type user_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get( @@ -1801,7 +1981,9 @@ class KeycloakAdmin: """Get all composite (i.e. implicit) realm roles for a user. :param user_id: id of user + :type user_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get( @@ -1812,9 +1994,12 @@ class KeycloakAdmin: def assign_group_realm_roles(self, group_id, roles): """Assign realm roles to a group. - :param group_id: id of groupp + :param group_id: id of group + :type group_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} @@ -1828,8 +2013,11 @@ class KeycloakAdmin: """Delete realm roles of a group. :param group_id: id of group + :type group_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} @@ -1842,8 +2030,10 @@ class KeycloakAdmin: def get_group_realm_roles(self, group_id): """Get all realm roles for a group. - :param user_id: id of the group + :param group_id: id of the group + :type group_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path)) @@ -1853,9 +2043,13 @@ class KeycloakAdmin: """Assign client roles to a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} @@ -1869,8 +2063,11 @@ class KeycloakAdmin: """Get client roles of a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) @@ -1880,9 +2077,13 @@ class KeycloakAdmin: """Delete client roles of a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response (array RoleRepresentation) + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} @@ -1896,8 +2097,11 @@ class KeycloakAdmin: """Get all client roles for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ return self._get_client_roles_of_user( urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id @@ -1907,8 +2111,11 @@ class KeycloakAdmin: """Get available client role-mappings for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ return self._get_client_roles_of_user( urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id @@ -1918,14 +2125,28 @@ class KeycloakAdmin: """Get composite client role-mappings for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ return self._get_client_roles_of_user( urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id ) def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id): + """Get client roles of a single user helper. + + :param client_level_role_mapping_url: Url for the client role mapping + :type client_level_role_mapping_url: str + :param user_id: User id + :type user_id: str + :param client_id: Client id + :type client_id: str + :returns: Client roles of a user + :rtype: list + """ params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) @@ -1934,9 +2155,13 @@ class KeycloakAdmin: """Delete client roles from a user. :param user_id: id of user + :type user_id: str :param client_id: id of client containing role (not client-id) + :type client_id: str :param roles: roles list or role to delete (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} @@ -1955,6 +2180,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :return: Keycloak server response (AuthenticationFlowRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS.format(**params_path)) @@ -1969,7 +2195,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param flow_id: the id of a flow NOT it's alias + :type flow_id: str :return: Keycloak server response (AuthenticationFlowRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "flow-id": flow_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)) @@ -1982,8 +2210,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation + :type payload: dict :param skip_exists: Do not raise an error if authentication flow already exists + :type skip_exists: bool :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -1999,8 +2230,11 @@ class KeycloakAdmin: The new name is given as 'newName' attribute of the passed payload. :param payload: JSON containing 'newName' attribute + :type payload: dict :param flow_alias: the flow alias + :type flow_alias: str :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -2015,7 +2249,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationinforepresentation :param flow_id: authentication flow id + :type flow_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": flow_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOW.format(**params_path)) @@ -2027,7 +2263,9 @@ class KeycloakAdmin: Returns all execution steps :param flow_alias: the flow alias + :type flow_alias: str :return: Response(json) + :rtype: list """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)) @@ -2040,8 +2278,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put( @@ -2057,7 +2298,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: the execution ID + :type execution_id: str :return: Response(json) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": execution_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) @@ -2070,8 +2313,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -2087,7 +2333,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: keycloak client id (not oauth client-id) + :type execution_id: str :return: Keycloak server response (json) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": execution_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) @@ -2100,9 +2348,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :param skip_exists: Do not raise an error if authentication flow already exists + :type skip_exists: bool :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -2116,7 +2368,8 @@ class KeycloakAdmin: def get_authenticator_providers(self): """Get authenticator providers list. - :return: Response(json) + :return: Authenticator providers + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2131,7 +2384,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfiginforepresentation :param provider_id: Provider Id + :type provider_id: str :return: AuthenticatorConfigInfoRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "provider-id": provider_id} data_raw = self.raw_get( @@ -2145,7 +2400,9 @@ class KeycloakAdmin: Returns all configuration details. :param config_id: Authenticator config id + :type config_id: str :return: Response(json) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) @@ -2158,8 +2415,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfigrepresentation :param payload: AuthenticatorConfigRepresentation + :type payload: dict :param config_id: Authenticator config id + :type config_id: str :return: Response(json) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_put( @@ -2174,7 +2434,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authentication_management_resource :param config_id: Authenticator config id + :type config_id: str :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_delete( @@ -2186,8 +2448,11 @@ class KeycloakAdmin: """Trigger user sync from provider. :param storage_id: The id of the user storage provider + :type storage_id: str :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync" - :return: + :type action: str + :return: Keycloak server response + :rtype: bytes """ data = {"action": action} params_query = {"action": action} @@ -2207,6 +2472,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :return: Keycloak server response Array of (ClientScopeRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)) @@ -2219,7 +2485,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_id: The id of the client scope + :type client_scope_id: str :return: Keycloak server response (ClientScopeRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) @@ -2232,7 +2500,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_name: (str) Name of the client scope + :type client_scope_name: str :returns: ClientScopeRepresentation or None + :rtype: dict """ client_scopes = self.get_client_scopes() for client_scope in client_scopes: @@ -2248,8 +2518,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param payload: ClientScopeRepresentation + :type payload: dict :param skip_exists: If true then do not raise an error if client scope already exists + :type skip_exists: bool :return: Client scope id + :rtype: str """ if skip_exists: exists = self.get_client_scope_by_name(client_scope_name=payload["name"]) @@ -2274,8 +2547,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource :param client_scope_id: The id of the client scope + :type client_scope_id: str :param payload: ClientScopeRepresentation - :return: Keycloak server response (ClientScopeRepresentation) + :type payload: dict + :return: Keycloak server response (ClientScopeRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_put( @@ -2290,7 +2566,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource :param client_scope_id: The id of the client scope - :return: Keycloak server response + :type client_scope_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) @@ -2301,7 +2579,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: Client scope id + :type client_scope_id: str :returns: Keycloak server response (ProtocolMapperRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get( @@ -2315,8 +2595,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_scope_id: The id of the client scope + :type client_scope_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} @@ -2333,8 +2616,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope + :type client_scope_id: str :param protocol_mapper_id: Protocol mapper id + :type protocol_mapper_id: str :return: Keycloak server Response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2353,10 +2639,14 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: The id of the client scope + :type client_scope_id: str :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope and should to be updated + :type protocol_mapper_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2377,6 +2667,7 @@ class KeycloakAdmin: Return list of default default client scopes :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2388,7 +2679,9 @@ class KeycloakAdmin: """Delete default default client scope. :param scope_id: default default client scope id + :type scope_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete( @@ -2400,7 +2693,9 @@ class KeycloakAdmin: """Add default default client scope. :param scope_id: default default client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} @@ -2416,6 +2711,7 @@ class KeycloakAdmin: Return list of default optional client scopes :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2427,7 +2723,9 @@ class KeycloakAdmin: """Delete default optional client scope. :param scope_id: default optional client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete( @@ -2439,7 +2737,9 @@ class KeycloakAdmin: """Add default optional client scope. :param scope_id: default optional client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} @@ -2455,7 +2755,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocolmapperrepresentation :param client_id: Client id + :type client_id: str :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -2471,8 +2773,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_id: The id of the client + :type client_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -2487,9 +2792,13 @@ class KeycloakAdmin: """Update client mapper. :param client_id: The id of the client - :param client_mapper_id: The id of the mapper to be deleted + :type client_id: str + :param mapper_id: The id of the mapper to be deleted + :type mapper_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2508,9 +2817,13 @@ class KeycloakAdmin: """Remove a mapper from the client. https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_protocol_mappers_resource + :param client_id: The id of the client + :type client_id: str :param client_mapper_id: The id of the mapper to be deleted + :type client_mapper_id: str :return: Keycloak server response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2529,7 +2842,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_regeneratesecret :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -2543,7 +2858,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsecret :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)) @@ -2558,7 +2875,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param query: Query parameters (optional) + :type query: dict :return: components list + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -2574,7 +2893,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param payload: ComponentRepresentation + :type payload: dict :return: Component id + :rtype: str """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -2592,7 +2913,10 @@ class KeycloakAdmin: ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation + :param component_id: Id of the component + :type component_id: str :return: ComponentRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) @@ -2602,10 +2926,12 @@ class KeycloakAdmin: """Update the component. :param component_id: Component id + :type component_id: str :param payload: ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation - + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_put( @@ -2617,8 +2943,9 @@ class KeycloakAdmin: """Delete the component. :param component_id: Component id - + :type component_id: str :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) @@ -2633,6 +2960,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_key_resource :return: keys list + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None) @@ -2646,7 +2974,10 @@ class KeycloakAdmin: EventRepresentation array https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_eventrepresentation + :param query: Additional query parameters + :type query: dict :return: events list + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -2661,7 +2992,10 @@ class KeycloakAdmin: RealmEventsConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmeventsconfigrepresentation + :param payload: Payload object for the events configuration + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_put( @@ -2674,6 +3008,13 @@ class KeycloakAdmin: If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token and try *get* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_get(*args, **kwargs) if "get" in self.auto_refresh_token and r.status_code == 401: @@ -2686,6 +3027,13 @@ class KeycloakAdmin: If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token and try *post* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_post(*args, **kwargs) if "post" in self.auto_refresh_token and r.status_code == 401: @@ -2698,6 +3046,13 @@ class KeycloakAdmin: If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token and try *put* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_put(*args, **kwargs) if "put" in self.auto_refresh_token and r.status_code == 401: @@ -2710,6 +3065,13 @@ class KeycloakAdmin: If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token and try *delete* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_delete(*args, **kwargs) if "delete" in self.auto_refresh_token and r.status_code == 401: @@ -2718,7 +3080,10 @@ class KeycloakAdmin: return r def get_token(self): - """Get admin token.""" + """Get admin token. + + The admin token is then set in the `token` attribute. + """ if self.user_realm_name: token_realm_name = self.user_realm_name elif self.realm_name: @@ -2766,7 +3131,10 @@ class KeycloakAdmin: ) def refresh_token(self): - """Refresh the token.""" + """Refresh the token. + + :raises KeycloakPostError: In case the refresh token request failed. + """ refresh_token = self.token.get("refresh_token", None) if refresh_token is None: self.get_token() @@ -2791,12 +3159,13 @@ class KeycloakAdmin: def get_client_all_sessions(self, client_id): """Get sessions associated with the client. - :param client_id: id of client - UserSessionRepresentation http://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation + :param client_id: id of client + :type client_id: str :return: UserSessionRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) @@ -2808,6 +3177,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsessionstats :return: Dict of clients and session count + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) @@ -2818,7 +3188,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -2832,17 +3204,19 @@ class KeycloakAdmin: ManagementPermissionReference https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_managementpermissionreference - :param payload: ManagementPermissionReference - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :return: Keycloak server response - - Payload example:: payload={ "enabled": true } + + :param payload: ManagementPermissionReference + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_put( @@ -2856,8 +3230,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param policy_id: No Document + :type policy_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} data_raw = self.raw_get( @@ -2870,8 +3247,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param policy_id: No Document + :type policy_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} data_raw = self.raw_get( @@ -2884,8 +3264,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param scope_id: No Document + :type scope_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} data_raw = self.raw_get( @@ -2896,13 +3279,6 @@ class KeycloakAdmin: def update_client_authz_scope_permission(self, payload, client_id, scope_id): """Update permissions for a given scope. - :param payload: No Document - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param scope_id: No Document - :return: Keycloak server response - - Payload example:: payload={ @@ -2915,6 +3291,16 @@ class KeycloakAdmin: "scopes": [some_scope_id], "policies": [some_policy_id], } + + :param payload: No Document + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param scope_id: No Document + :type scope_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} data_raw = self.raw_put( @@ -2928,7 +3314,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -2939,12 +3327,6 @@ class KeycloakAdmin: def create_client_authz_client_policy(self, payload, client_id): """Create a new policy for a given client. - :param payload: No Document - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :return: Keycloak server response (RoleRepresentation) - - Payload example:: payload={ @@ -2954,6 +3336,14 @@ class KeycloakAdmin: "name": "My Policy", "clients": [other_client_id], } + + :param payload: No Document + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( From a14a47dd4005435f6efb57a4769b153887df93d3 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 4 Aug 2022 14:44:10 +0000 Subject: [PATCH 100/222] chore: dependency update --- poetry.lock | 198 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 41 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9fb3952..dd69dab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -44,17 +44,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "babel" @@ -117,6 +117,14 @@ category = "dev" optional = false python-versions = ">=3.6.1" +[[package]] +name = "chardet" +version = "5.0.0" +description = "Universal encoding detector for Python 3" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "charset-normalizer" version = "2.1.0" @@ -149,8 +157,8 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] hard-encoding-detection = ["chardet"] +dev = ["pytest-dependency", "pytest-cov", "pytest", "flake8", "check-manifest"] [[package]] name = "colorama" @@ -162,7 +170,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "commitizen" -version = "2.28.0" +version = "2.29.3" description = "Python commitizen client tool" category = "dev" optional = false @@ -170,6 +178,7 @@ python-versions = ">=3.6.2,<4.0.0" [package.dependencies] argcomplete = ">=1.12.1,<2.0.0" +chardet = ">=5.0.0,<6.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" jinja2 = ">=2.10.3" @@ -311,7 +320,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.2" +version = "2.5.3" description = "File identification library for Python" category = "dev" optional = false @@ -778,7 +787,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "5.0.2" +version = "5.1.1" description = "Python documentation generator" category = "main" optional = true @@ -788,7 +797,7 @@ python-versions = ">=3.6" alabaster = ">=0.7,<0.8" babel = ">=1.3" colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.19" +docutils = ">=0.14,<0.20" imagesize = "*" importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} Jinja2 = ">=2.3" @@ -805,16 +814,16 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.950)", "docutils-stubs", "types-typed-ast", "types-requests"] +lint = ["flake8 (>=3.5.0)", "flake8-comprehensions", "flake8-bugbear", "isort", "mypy (>=0.971)", "sphinx-lint", "docutils-stubs", "types-typed-ast", "types-requests"] test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinx-autoapi" -version = "1.8.4" +version = "1.9.0" description = "Sphinx API documentation generator" category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] astroid = ">=2.7" @@ -995,7 +1004,7 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.10" +version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1008,22 +1017,21 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.15.1" +version = "20.16.2" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] distlib = ">=0.3.1,<1" filelock = ">=3.2,<4" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} platformdirs = ">=2,<3" -six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] [[package]] name = "wcwidth" @@ -1072,25 +1080,115 @@ argcomplete = [ ] astroid = [] atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] +attrs = [] babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] -black = [] +black = [ + {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, + {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, + {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, + {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, + {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, + {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, + {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, + {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, + {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, + {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, + {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, + {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, + {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, + {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, + {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, + {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, + {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, + {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, +] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] -cffi = [] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] -charset-normalizer = [] +chardet = [] +charset-normalizer = [ + {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, + {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, +] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -1126,13 +1224,19 @@ flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] -flake8-docstrings = [] +flake8-docstrings = [ + {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, + {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, +] identify = [] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] -imagesize = [] +imagesize = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, @@ -1271,7 +1375,10 @@ pluggy = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] pre-commit = [] -prompt-toolkit = [] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, + {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, +] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -1295,8 +1402,14 @@ pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] -pycparser = [] -pydocstyle = [] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pydocstyle = [ + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, +] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, @@ -1372,7 +1485,10 @@ recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, ] -requests = [] +requests = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] requests-toolbelt = [] rsa = [] six = [ @@ -1383,14 +1499,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -sphinx = [ - {file = "Sphinx-5.0.2-py3-none-any.whl", hash = "sha256:d3e57663eed1d7c5c50895d191fdeda0b54ded6f44d5621b50709466c338d1e8"}, - {file = "Sphinx-5.0.2.tar.gz", hash = "sha256:b18e978ea7565720f26019c702cd85c84376e948370f1cd43d60265010e1c7b0"}, -] -sphinx-autoapi = [ - {file = "sphinx-autoapi-1.8.4.tar.gz", hash = "sha256:8c4ec5fbedc1e6e8f4692bcc4fcd1abcfb9e8dfca8a4ded60ad811a743c22ccc"}, - {file = "sphinx_autoapi-1.8.4-py2.py3-none-any.whl", hash = "sha256:007bf9e24cd2aa0ac0561f67e8bcd6a6e2e8911ef4b4fd54aaba799d8832c8d0"}, -] +sphinx = [] +sphinx-autoapi = [] sphinx-rtd-theme = [ {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, @@ -1431,7 +1541,10 @@ tomli = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tomlkit = [] -tox = [] +tox = [ + {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, + {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, +] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, @@ -1458,7 +1571,10 @@ typed-ast = [ {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] -typing-extensions = [] +typing-extensions = [ + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, +] unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, From 843113921cfb4e549b927053c8202826ea2b1f06 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 4 Aug 2022 14:44:44 +0000 Subject: [PATCH 101/222] chore: linting on full repo --- docs/source/changelog.rst | 1 - pyproject.toml | 2 +- tox.ini | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 102c113..ab37940 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,2 +1 @@ .. mdinclude:: ../../CHANGELOG.md - \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7874ca4..a115c32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,4 +84,4 @@ line_length = 99 profile = "black" [tool.darglint] -enable = "DAR104" \ No newline at end of file +enable = "DAR104" diff --git a/tox.ini b/tox.ini index 06a0855..044f8b6 100644 --- a/tox.ini +++ b/tox.ini @@ -54,4 +54,4 @@ ignore = D203, D213, W503 docstring_style = sphinx [darglint] -enable = DAR104 \ No newline at end of file +enable = DAR104 From 7ea8ef6cbb314c1112299839c03882fb3a18f2ec Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Thu, 4 Aug 2022 14:58:11 +0000 Subject: [PATCH 102/222] ci: dont do incremental updats to the changelog --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 044f8b6..2b3db36 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ commands = setenv = file|tox.env passenv = CONTAINER_HOST commands = - cz changelog --incremental + cz changelog [flake8] max-line-length = 99 From a1591ba50ae3c0cedbec9c1f62cda993ddd1872e Mon Sep 17 00:00:00 2001 From: Romuald OUATTARA Date: Mon, 8 Aug 2022 19:28:53 +0200 Subject: [PATCH 103/222] docs: added change of realm example --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d300fa9..99eba7d 100644 --- a/README.md +++ b/README.md @@ -319,4 +319,10 @@ idps = keycloak_admin.get_idps() # Create a new Realm keycloak_admin.create_realm(payload={"realm": "demo"}, skip_exists=False) +# Changing Realm +keycloak_admin = KeycloakAdmin(realm_name="main", ...) +keycloak_admin.get_users() # Get user in main realm +keycloak_admin.realm_name = "demo" # Change realm to 'demo' +keycloak_admin.get_users() # Get users in realm 'demo' +keycloak_admin.create_user(...) # Creates a new user in 'demo' ``` From 0fb6c2058d96d2c1f194665243eef4e7f10c0ae8 Mon Sep 17 00:00:00 2001 From: Antonio Lucas Neres Date: Thu, 11 Aug 2022 10:35:53 -0300 Subject: [PATCH 104/222] feat: add client scope-mappings realm roles operations --- README.md | 56 ++++++++++++++++++-------------- src/keycloak/keycloak_admin.py | 42 ++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 1 + tests/test_keycloak_admin.py | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 99eba7d..9666ba9 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ from keycloak import KeycloakOpenID # Configure client keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", - client_id="example_client", - realm_name="example_realm", - client_secret_key="secret") + client_id="example_client", + realm_name="example_realm", + client_secret_key="secret") # Get WellKnow config_well_known = keycloak_openid.well_known() @@ -110,7 +110,7 @@ rpt = keycloak_openid.entitlement(token['access_token'], "resource_id") # Instropect RPT token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['access_token'], rpt=rpt['rpt'], - token_type_hint="requesting_party_token")) + token_type_hint="requesting_party_token")) # Introspect Token token_info = keycloak_openid.introspect(token['access_token']) @@ -153,37 +153,37 @@ keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", # Add user new_user = keycloak_admin.create_user({"email": "example@example.com", - "username": "example@example.com", - "enabled": True, - "firstName": "Example", - "lastName": "Example"}) + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example"}) # Add user and raise exception if username already exists # exist_ok currently defaults to True for backwards compatibility reasons new_user = keycloak_admin.create_user({"email": "example@example.com", - "username": "example@example.com", - "enabled": True, - "firstName": "Example", - "lastName": "Example"}, - exist_ok=False) + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example"}, + exist_ok=False) # Add user and set password new_user = keycloak_admin.create_user({"email": "example@example.com", - "username": "example@example.com", - "enabled": True, - "firstName": "Example", - "lastName": "Example", + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example", "credentials": [{"value": "secret","type": "password",}]}) # Add user and specify a locale new_user = keycloak_admin.create_user({"email": "example@example.fr", - "username": "example@example.fr", - "enabled": True, - "firstName": "Example", - "lastName": "Example", - "attributes": { - "locale": ["fr"] - }}) + "username": "example@example.fr", + "enabled": True, + "firstName": "Example", + "lastName": "Example", + "attributes": { + "locale": ["fr"] + }}) # User counter count_users = keycloak_admin.users_count() @@ -312,6 +312,14 @@ keycloak_admin.assign_client_role(client_id=client_id, user_id=user_id, role_id= # Assign realm roles to user keycloak_admin.assign_realm_roles(user_id=user_id, roles=realm_roles) +# Assign realm roles to client's scope +keycloak_admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=realm_roles) + +# Get realm roles assigned to client's scope +keycloak_admin.get_realm_roles_of_client_scope(client_id=client_id) + +# Remove realm roles assigned to client's scope +keycloak_admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=realm_roles) # Get all ID Providers idps = keycloak_admin.get_idps() diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 7d16ad5..50299b6 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1597,6 +1597,48 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) + def assign_realm_roles_to_client_scope(self, client_id, roles): + """Assign realm roles to a client's scope. + + :param client_id: id of client (not client-id) + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) + + def delete_realm_roles_of_client_scope(self, client_id, roles): + """Delete realm roles of a client's scope. + + :param client_id: id of client (not client-id) + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) + + def get_realm_roles_of_client_scope(self, client_id): + """Get all realm roles for a client's scope. + + :param client_id: id of client (not client-id) + :return: Keycloak server response (array RoleRepresentation) + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + def assign_realm_roles(self, user_id, roles): """Assign realm roles to a user. diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 3f4151e..f7d4f65 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -91,6 +91,7 @@ URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE = URL_ADMIN_CLIENT_ROLE + "/composi URL_ADMIN_CLIENT_ROLE_MEMBERS = URL_ADMIN_CLIENT + "/roles/{role-name}/users" URL_ADMIN_CLIENT_ROLE_GROUPS = URL_ADMIN_CLIENT + "/roles/{role-name}/groups" URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS = URL_ADMIN_CLIENT + "/management/permissions" +URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES = URL_ADMIN_CLIENT + "/scope-mappings/realm" URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 61685af..e9b093c 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1030,6 +1030,64 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"Could not find role"}\'') +def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): + """Test client realm roles.""" + admin.realm_name = realm + + # Test get realm roles + roles = admin.get_realm_roles() + assert len(roles) == 3, roles + role_names = [x["name"] for x in roles] + assert "uma_authorization" in role_names, role_names + assert "offline_access" in role_names, role_names + + # create realm role for test + role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True) + assert role_id, role_id + + # Test realm role client assignment + client_id = admin.create_client( + payload={"name": "role-testing-client", "clientId": "role-testing-client"} + ) + with pytest.raises(KeycloakPostError) as err: + admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_realm_roles_to_client_scope( + client_id=client_id, + roles=[ + admin.get_realm_role(role_name="offline_access"), + admin.get_realm_role(role_name="test-realm-role"), + ], + ) + assert res == dict(), res + + roles = admin.get_realm_roles_of_client_scope(client_id=client_id) + assert len(roles) == 2 + client_role_names = [x["name"] for x in roles] + assert "offline_access" in client_role_names, client_role_names + assert "test-realm-role" in client_role_names, client_role_names + assert "uma_authorization" not in client_role_names, client_role_names + + # Test remove realm role of client + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"]) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_realm_roles_of_client_scope( + client_id=client_id, roles=[admin.get_realm_role(role_name="offline_access")] + ) + assert res == dict(), res + roles = admin.get_realm_roles_of_client_scope(client_id=client_id) + assert len(roles) == 1 + assert "test-realm-role" in [x["name"] for x in roles] + + res = admin.delete_realm_roles_of_client_scope( + client_id=client_id, roles=[admin.get_realm_role(role_name="test-realm-role")] + ) + assert res == dict(), res + roles = admin.get_realm_roles_of_client_scope(client_id=client_id) + assert len(roles) == 0 + + def test_client_roles(admin: KeycloakAdmin, client: str): """Test client roles.""" # Test get client roles From 2e2578b1b425b5da34e84a6d6f77cbce6e7fd7e8 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Fri, 12 Aug 2022 06:55:00 +0000 Subject: [PATCH 105/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251c6d5..958d8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v2.2.0 (2022-08-12) + +### Feat + +- add client scope-mappings realm roles operations + ## v2.1.1 (2022-07-19) ### Refactor From d14fbd6b5dff94808b5f92db9539a16a53db6dce Mon Sep 17 00:00:00 2001 From: Subramaniam Ramasubramanian Date: Fri, 12 Aug 2022 10:46:15 +0000 Subject: [PATCH 106/222] feat: Add token_type/scope to token exchange api --- src/keycloak/keycloak_openid.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 82e980c..055085d 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -275,7 +275,15 @@ class KeycloakOpenID: data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakPostError) - def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: + def exchange_token( + self, + token: str, + client_id: str, + audience: str, + subject: str, + requested_token_type: str = "urn:ietf:params:oauth:token-type:refresh_token", + scope: str = "", + ) -> dict: """Exchange user token. Use a token to obtain an entirely different token. See @@ -285,6 +293,8 @@ class KeycloakOpenID: :param client_id: :param audience: :param subject: + :param requested_token_type: + :param scope: :return: """ params_path = {"realm-name": self.realm_name} @@ -292,9 +302,10 @@ class KeycloakOpenID: "grant_type": ["urn:ietf:params:oauth:grant-type:token-exchange"], "client_id": client_id, "subject_token": token, - "requested_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "requested_token_type": requested_token_type, "audience": audience, "requested_subject": subject, + "scope": scope, } payload = self._add_secret_key(payload) data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) From 0b93336f1582e17b105fbcf69899443268f1e11b Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Sat, 13 Aug 2022 00:39:02 +0000 Subject: [PATCH 107/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 958d8fa..cf8293b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v2.3.0 (2022-08-13) + +### Feat + +- Add token_type/scope to token exchange api + ## v2.2.0 (2022-08-12) ### Feat From 7c486ccb4fce28ff7e9fb5a5d4b6b95e94343926 Mon Sep 17 00:00:00 2001 From: Antonio Lucas Neres Date: Fri, 19 Aug 2022 14:13:27 -0300 Subject: [PATCH 108/222] feat: add client scope-mappings client roles operations --- README.md | 11 +++++++ src/keycloak/keycloak_admin.py | 57 ++++++++++++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 3 ++ tests/test_keycloak_admin.py | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) diff --git a/README.md b/README.md index 9666ba9..3558d30 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,17 @@ keycloak_admin.get_realm_roles_of_client_scope(client_id=client_id) # Remove realm roles assigned to client's scope keycloak_admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=realm_roles) +another_client_id = keycloak_admin.get_client_id("my-client-2") + +# Assign client roles to client's scope +keycloak_admin.assign_client_roles_to_client_scope(client_id=another_client_id, client_roles_owner_id=client_id, roles=client_roles) + +# Get client roles assigned to client's scope +keycloak_admin.get_client_roles_of_client_scope(client_id=another_client_id, client_roles_owner_id=client_id) + +# Remove client roles assigned to client's scope +keycloak_admin.delete_client_roles_of_client_scope(client_id=another_client_id, client_roles_owner_id=client_id, roles=client_roles) + # Get all ID Providers idps = keycloak_admin.get_idps() diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 50299b6..15423a1 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1639,6 +1639,63 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) + def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles): + """Assign client roles to a client's scope. + + :param client_id: id of client (not client-id) who is assigned the roles + :param client_roles_owner_id: id of client (not client-id) who has the roles + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) + + def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles): + """Delete client roles of a client's scope. + + :param client_id: id of client (not client-id) who is assigned the roles + :param client_roles_owner_id: id of client (not client-id) who has the roles + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) + + def get_client_roles_of_client_scope(self, client_id, client_roles_owner_id): + """Get all client roles for a client's scope. + + :param client_id: id of client (not client-id) + :param client_roles_owner_id: id of client (not client-id) who has the roles + :return: Keycloak server response (array RoleRepresentation) + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + def assign_realm_roles(self, user_id, roles): """Assign realm roles to a user. diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index f7d4f65..f2a2188 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -92,6 +92,9 @@ URL_ADMIN_CLIENT_ROLE_MEMBERS = URL_ADMIN_CLIENT + "/roles/{role-name}/users" URL_ADMIN_CLIENT_ROLE_GROUPS = URL_ADMIN_CLIENT + "/roles/{role-name}/groups" URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS = URL_ADMIN_CLIENT + "/management/permissions" URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES = URL_ADMIN_CLIENT + "/scope-mappings/realm" +URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES = ( + URL_ADMIN_CLIENT + "/scope-mappings/clients/{client}" +) URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index e9b093c..0f5af95 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1088,6 +1088,62 @@ def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): assert len(roles) == 0 +def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str): + """Test client assignment of other client roles.""" + admin.realm_name = realm + + client_id = admin.create_client( + payload={"name": "role-testing-client", "clientId": "role-testing-client"} + ) + + # Test get client roles + roles = admin.get_client_roles_of_client_scope(client_id, client) + assert len(roles) == 0, roles + + # create client role for test + client_role_id = admin.create_client_role( + client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True + ) + assert client_role_id, client_role_id + + # Test client role assignment to other client + with pytest.raises(KeycloakPostError) as err: + admin.assign_client_roles_to_client_scope( + client_id=client_id, client_roles_owner_id=client, roles=["bad"] + ) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_client_roles_to_client_scope( + client_id=client_id, + client_roles_owner_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test")], + ) + assert res == dict(), res + + roles = admin.get_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client + ) + assert len(roles) == 1 + client_role_names = [x["name"] for x in roles] + assert "client-role-test" in client_role_names, client_role_names + + # Test remove realm role of client + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client, roles=["bad"] + ) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_client_roles_of_client_scope( + client_id=client_id, + client_roles_owner_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test")], + ) + assert res == dict(), res + roles = admin.get_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client + ) + assert len(roles) == 0 + + def test_client_roles(admin: KeycloakAdmin, client: str): """Test client roles.""" # Test get client roles From acd457ef39ca04c8a1c7fb4e0dc44642945cc046 Mon Sep 17 00:00:00 2001 From: Merle Nerger Date: Fri, 19 Aug 2022 14:47:57 +0200 Subject: [PATCH 109/222] docs: fixed docstrings stating incorrect return types for get_client_role(s) and get_realm_role(s) --- src/keycloak/keycloak_admin.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 50299b6..9efa445 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1306,7 +1306,7 @@ class KeycloakAdmin: RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - :return: Keycloak server response (RoleRepresentation) + :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) @@ -1329,12 +1329,11 @@ class KeycloakAdmin: def get_client_roles(self, client_id): """Get all roles for the client. - :param client_id: id of client (not client-id) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - :return: Keycloak server response (RoleRepresentation) + :param client_id: id of client (not client-id) + :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) @@ -1345,13 +1344,12 @@ class KeycloakAdmin: This is required for further actions with this role. - :param client_id: id of client (not client-id) - :param role_name: role’s name (not id!) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - :return: role_id + :param client_id: id of client (not client-id) + :param role_name: role’s name (not id!) + :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) @@ -1517,11 +1515,11 @@ class KeycloakAdmin: def get_realm_role(self, role_name): """Get realm role by role name. - :param role_name: role's name, not id! - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation - :return: role_id + + :param role_name: role's name, not id! + :return: Keycloak server response (RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( From 739e9abfbebaeff42df981b0ebfadd31fe538f61 Mon Sep 17 00:00:00 2001 From: Merle Nerger Date: Thu, 18 Aug 2022 17:04:17 +0200 Subject: [PATCH 110/222] feat: added missing functionality to include attributes when returning realm roles according to specifications --- src/keycloak/keycloak_admin.py | 57 ++++++++++++++++++++++++---------- tests/test_keycloak_admin.py | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 9efa445..770d3ce 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -578,17 +578,21 @@ class KeycloakAdmin: data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def get_user_groups(self, user_id): + def get_user_groups(self, user_id, brief_representation=True): """Get user groups. Returns a list of groups of which the user is a member :param user_id: User id + :param brief_representation: whether to omit attributes in the response :return: user groups list """ + params = {"briefRepresentation": brief_representation} params_path = {"realm-name": self.realm_name, "id": user_id} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)) + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path), **params + ) return raise_error_from_response(data_raw, KeycloakGetError) def update_user(self, user_id, payload): @@ -1300,16 +1304,20 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) - def get_realm_roles(self): + def get_realm_roles(self, brief_representation=True): """Get all roles for the realm or client. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + :param brief_representation: whether to omit role attributes in the response :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) + params = {"briefRepresentation": brief_representation} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_realm_role_members(self, role_name, query=None): @@ -1326,17 +1334,21 @@ class KeycloakAdmin: urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query ) - def get_client_roles(self, client_id): + def get_client_roles(self, client_id, brief_representation=True): """Get all roles for the client. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_id: id of client (not client-id) + :param brief_representation: whether to omit role attributes in the response :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": client_id} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) + params = {"briefRepresentation": brief_representation} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), **params + ) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role(self, client_id, role_name): @@ -1689,15 +1701,17 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) - def get_composite_realm_roles_of_user(self, user_id): + def get_composite_realm_roles_of_user(self, user_id, brief_representation=True): """Get all composite (i.e. implicit) realm roles for a user. :param user_id: id of user + :param brief_representation: whether to omit role attributes in the response :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": user_id} + params = {"briefRepresentation": brief_representation} data_raw = self.raw_get( - urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path) + urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path), **params ) return raise_error_from_response(data_raw, KeycloakGetError) @@ -1731,14 +1745,18 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) - def get_group_realm_roles(self, group_id): + def get_group_realm_roles(self, group_id, brief_representation=True): """Get all realm roles for a group. :param user_id: id of the group + :param brief_representation: whether to omit role attributes in the response :return: Keycloak server response (array RoleRepresentation) """ params_path = {"realm-name": self.realm_name, "id": group_id} - data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path)) + params = {"briefRepresentation": brief_representation} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), **params + ) return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_client_roles(self, group_id, client_id, roles): @@ -1806,20 +1824,24 @@ class KeycloakAdmin: urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id ) - def get_composite_client_roles_of_user(self, user_id, client_id): + def get_composite_client_roles_of_user(self, user_id, client_id, brief_representation=False): """Get composite client role-mappings for a user. :param user_id: id of user :param client_id: id of client (not client-id) + :param brief_representation: whether to omit attributes in the response :return: Keycloak server response (array RoleRepresentation) """ + params = {"briefRepresentation": brief_representation} return self._get_client_roles_of_user( - urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id + urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id, **params ) - def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id): + def _get_client_roles_of_user( + self, client_level_role_mapping_url, user_id, client_id, **params + ): params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} - data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path)) + data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path), **params) return raise_error_from_response(data_raw, KeycloakGetError) def delete_client_roles_of_user(self, user_id, client_id, roles): @@ -2854,19 +2876,22 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) - def get_composite_client_roles_of_group(self, client_id, group_id): + def get_composite_client_roles_of_group(self, client_id, group_id, brief_representation=True): """Get the composite client roles of the given group for the given client. :param client_id: id of the client. :type client_id: str :param group_id: id of the group. :type group_id: str + :param brief_representation: whether to omit attributes in the response + :type brief_representation: bool :return: the composite client roles of the group (list of RoleRepresentation). :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} + params = {"briefRepresentation": brief_representation} data_raw = self.raw_get( - urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path) + urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path), **params ) return raise_error_from_response(data_raw, KeycloakGetError) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index e9b093c..cf5d8b2 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1030,6 +1030,63 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): assert err.match('404: b\'{"error":"Could not find role"}\'') +@pytest.mark.parametrize( + "testcase, arg_brief_repr, includes_attributes", + [ + ("brief True", {"brief_representation": True}, False), + ("brief False", {"brief_representation": False}, True), + ("default", {}, False), + ], +) +def test_role_attributes( + admin: KeycloakAdmin, + realm: str, + client: str, + arg_brief_repr: dict, + includes_attributes: bool, + testcase: str, +): + """Test getting role attributes for bulk calls.""" + # setup + attribute_role = "test-realm-role-w-attr" + test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]} + role_id = admin.create_realm_role( + payload={"name": attribute_role, "attributes": test_attrs}, + skip_exists=True, + ) + assert role_id, role_id + + cli_role_id = admin.create_client_role( + client, + payload={"name": attribute_role, "attributes": test_attrs}, + skip_exists=True, + ) + assert cli_role_id, cli_role_id + + if not includes_attributes: + test_attrs = None + + # tests + roles = admin.get_realm_roles(**arg_brief_repr) + roles_filtered = [role for role in roles if role["name"] == role_id] + assert roles_filtered, roles_filtered + role = roles_filtered[0] + assert role.get("attributes") == test_attrs, testcase + + roles = admin.get_client_roles(client, **arg_brief_repr) + roles_filtered = [role for role in roles if role["name"] == cli_role_id] + assert roles_filtered, roles_filtered + role = roles_filtered[0] + assert role.get("attributes") == test_attrs, testcase + + # cleanup + res = admin.delete_realm_role(role_name=attribute_role) + assert res == dict(), res + + res = admin.delete_client_role(client, role_name=attribute_role) + assert res == dict(), res + + def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): """Test client realm roles.""" admin.realm_name = realm From 3c4e0f1443b9215ac360571d4d9c9889ea799328 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Fri, 19 Aug 2022 19:42:03 +0000 Subject: [PATCH 111/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf8293b..ca19f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v2.4.0 (2022-08-19) + +### Feat + +- add client scope-mappings client roles operations + ## v2.3.0 (2022-08-13) ### Feat From dfd5381f6e64fcfa8b0a2d09a55a494ff598de30 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Fri, 19 Aug 2022 19:49:58 +0000 Subject: [PATCH 112/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca19f4e..0f90b91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v2.5.0 (2022-08-19) + +### Feat + +- added missing functionality to include attributes when returning realm roles according to specifications + ## v2.4.0 (2022-08-19) ### Feat From 40ac02ae3bf986190304dd8436b470055e490d7e Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sat, 27 Aug 2022 17:56:38 +0000 Subject: [PATCH 113/222] docs: finished off docstrings in the source --- src/keycloak/keycloak_openid.py | 214 ++++++++++++++++++++++++-------- src/keycloak/uma_permissions.py | 96 +++++++++++--- 2 files changed, 240 insertions(+), 70 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 82e980c..1c81871 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -81,7 +81,25 @@ class KeycloakOpenID: proxies=None, timeout=60, ): - """Init method.""" + """Init method. + + :param server_url: Keycloak server url + :type server_url: str + :param client_id: client id + :type client_id: str + :param realm_name: realm name + :type realm_name: str + :param client_secret_key: client secret key + :type client_secret_key: str + :param verify: True if want check connection SSL + :type verify: bool + :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict + :param proxies: dict of proxies to sent the request by. + :type proxies: dict + :param timeout: connection timeout in seconds + :type timeout: int + """ self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name @@ -94,7 +112,11 @@ class KeycloakOpenID: @property def client_id(self): - """Get client id.""" + """Get client id. + + :returns: Client id + :rtype: str + """ return self._client_id @client_id.setter @@ -103,7 +125,11 @@ class KeycloakOpenID: @property def client_secret_key(self): - """Get the client secret key.""" + """Get the client secret key. + + :returns: Client secret key + :rtype: str + """ return self._client_secret_key @client_secret_key.setter @@ -112,7 +138,11 @@ class KeycloakOpenID: @property def realm_name(self): - """Get the realm name.""" + """Get the realm name. + + :returns: Realm name + :rtype: str + """ return self._realm_name @realm_name.setter @@ -121,7 +151,11 @@ class KeycloakOpenID: @property def connection(self): - """Get connection.""" + """Get connection. + + :returns: Connection manager object + :rtype: ConnectionManager + """ return self._connection @connection.setter @@ -130,7 +164,11 @@ class KeycloakOpenID: @property def authorization(self): - """Get authorization.""" + """Get authorization. + + :returns: The authorization manager + :rtype: Authorization + """ return self._authorization @authorization.setter @@ -140,8 +178,10 @@ class KeycloakOpenID: def _add_secret_key(self, payload): """Add secret key if exists. - :param payload: - :return: + :param payload: Payload + :type payload: dict + :returns: Payload with the secret key + :rtype: dict """ if self.client_secret_key: payload.update({"client_secret": self.client_secret_key}) @@ -151,18 +191,24 @@ class KeycloakOpenID: def _build_name_role(self, role): """Build name of a role. - :param role: - :return: + :param role: Role name + :type role: str + :returns: Role path + :rtype: str """ return self.client_id + "/" + role def _token_info(self, token, method_token_info, **kwargs): """Getter for the token data. - :param token: - :param method_token_info: - :param kwargs: - :return: + :param token: Token + :type token: str + :param method_token_info: Token info method to use + :type method_token_info: str + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Token info + :rtype: dict """ if method_token_info == "introspect": token_info = self.introspect(token) @@ -178,7 +224,8 @@ class KeycloakOpenID: endpoint. It lists endpoints and other configuration options relevant to the OpenID Connect implementation in Keycloak. - :return It lists endpoints and other configuration options relevant. + :returns: It lists endpoints and other configuration options relevant + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) @@ -190,9 +237,9 @@ class KeycloakOpenID: :param redirect_uri: Redirect url to receive oauth code :type redirect_uri: str :param scope: Scope of authorization request, split with the blank space - :type: scope: str + :type scope: str :param state: State will be returned to the redirect_uri - :type: str + :type state: str :returns: Authorization URL Full Build :rtype: str """ @@ -224,13 +271,22 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - :param username: - :param password: - :param grant_type: - :param code: - :param redirect_uri: - :param totp: - :return: + :param username: Username + :type username: str + :param password: Password + :type password: str + :param grant_type: Grant type + :type grant_type: str + :param code: Code + :type code: str + :param redirect_uri: Redirect URI + :type redirect_uri: str + :param totp: Time-based one-time password + :type totp: int + :param extra: Additional extra arguments + :type extra: dict + :returns: Keycloak token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -261,9 +317,12 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - :param refresh_token: - :param grant_type: - :return: + :param refresh_token: Refresh token from Keycloak + :type refresh_token: str + :param grant_type: Grant type + :type grant_type: str + :returns: New token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -281,11 +340,16 @@ class KeycloakOpenID: Use a token to obtain an entirely different token. See https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange - :param token: - :param client_id: - :param audience: - :param subject: - :return: + :param token: Access token + :type token: str + :param client_id: Client id + :type client_id: str + :param audience: Audience + :type audience: str + :param subject: Subject + :type subject: str + :returns: Exchanged token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -308,8 +372,10 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#UserInfo - :param token: - :return: + :param token: Access token + :type token: str + :returns: Userinfo object + :rtype: dict """ self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name} @@ -319,8 +385,10 @@ class KeycloakOpenID: def logout(self, refresh_token): """Log out the authenticated user. - :param refresh_token: - :return: + :param refresh_token: Refresh token from Keycloak + :type refresh_token: str + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = {"client_id": self.client_id, "refresh_token": refresh_token} @@ -337,7 +405,8 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7517 - :return: + :returns: Certificates + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_CERTS.format(**params_path)) @@ -348,7 +417,8 @@ class KeycloakOpenID: The public key is exposed by the realm page directly. - :return: + :returns: The public key + :rtype: str """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_REALM.format(**params_path)) @@ -363,7 +433,12 @@ class KeycloakOpenID: authorization policies associated with the resources being requested. With an RPT, client applications can gain access to protected resources at the resource server. - :return: + :param token: Access token + :type token: str + :param resource_server_id: Resource server ID + :type resource_server_id: str + :returns: Entitlements + :rtype: dict """ self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name, "resource-server-id": resource_server_id} @@ -382,11 +457,16 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7662 - :param token: - :param rpt: - :param token_type_hint: + :param token: Access token + :type token: str + :param rpt: Requesting party token + :type rpt: str + :param token_type_hint: Token type hint + :type token_type_hint: str - :return: + :returns: Token info + :rtype: dict + :raises KeycloakRPTNotFound: In case of RPT not specified """ params_path = {"realm-name": self.realm_name} payload = {"client_id": self.client_id, "token": token} @@ -415,10 +495,16 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7517 - :param token: - :param key: - :param algorithms: - :return: + :param token: Keycloak token + :type token: str + :param key: Decode key + :type key: str + :param algorithms: Algorithms to use for decoding + :type algorithms: list[str] + :param kwargs: Keyword arguments + :type kwargs: dict + :returns: Decoded token + :rtype: dict """ return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) @@ -426,7 +512,7 @@ class KeycloakOpenID: """Load Keycloak settings (authorization). :param path: settings file (json) - :return: + :type path: str """ with open(path, "r") as fp: authorization_json = json.load(fp) @@ -436,8 +522,16 @@ class KeycloakOpenID: def get_policies(self, token, method_token_info="introspect", **kwargs): """Get policies by user token. - :param token: user token - :return: policies list + :param token: User token + :type token: str + :param method_token_info: Method for token info decoding + :type method_token_info: str + :param kwargs: Additional keyword arguments + :type kwargs: dict + :return: Policies + :rtype: dict + :raises KeycloakAuthorizationConfigError: In case of bad authorization configuration + :raises KeycloakInvalidTokenError: In case of bad token """ if not self.authorization.policies: raise KeycloakAuthorizationConfigError( @@ -467,9 +561,15 @@ class KeycloakOpenID: """Get permission by user token. :param token: user token + :type token: str :param method_token_info: Decode token method + :type method_token_info: str :param kwargs: parameters for decode - :return: permissions list + :type kwargs: dict + :returns: permissions list + :rtype: list + :raises KeycloakAuthorizationConfigError: In case of bad authorization configuration + :raises KeycloakInvalidTokenError: In case of bad token """ if not self.authorization.policies: raise KeycloakAuthorizationConfigError( @@ -504,8 +604,11 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint :param token: user token + :type token: str :param permissions: list of uma permissions list(resource:scope) requested by the user - :return: permissions list + :type permissions: str + :returns: Keycloak server response + :rtype: dict """ permission = build_permission_param(permissions) @@ -525,8 +628,13 @@ class KeycloakOpenID: """Determine whether user has uma permissions with specified user token. :param token: user token + :type token: str :param permissions: list of uma permissions (resource:scope) - :return: auth status + :type permissions: str + :return: Authentication status + :rtype: AuthStatus + :raises KeycloakAuthenticationError: In case of failed authentication + :raises KeycloakPostError: In case of failed request to Keycloak """ needed = build_permission_param(permissions) try: diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 94779f1..eadcd72 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -48,7 +48,16 @@ class UMAPermission: """ def __init__(self, permission=None, resource="", scope=""): - """Init method.""" + """Init method. + + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str + :raises PermissionDefinitionError: In case bad permission definition + """ self.resource = resource self.scope = scope @@ -63,26 +72,55 @@ class UMAPermission: self.scope = str(permission.scope) def __str__(self): - """Str method.""" + """Str method. + + :returns: String representation + :rtype: str + """ scope = self.scope if scope: scope = "#" + scope return "{}{}".format(self.resource, scope) def __eq__(self, __o: object) -> bool: - """Eq method.""" + """Eq method. + + :param __o: The other object + :type __o: object + :returns: Equality boolean + :rtype: bool + """ return str(self) == str(__o) def __repr__(self) -> str: - """Repr method.""" + """Repr method. + + :returns: The object representation + :rtype: str + """ return self.__str__() def __hash__(self) -> int: - """Hash method.""" + """Hash method. + + :returns: Hash of the object + :rtype: int + """ return hash(str(self)) - def __call__(self, permission=None, resource="", scope="") -> object: - """Call method.""" + def __call__(self, permission=None, resource="", scope="") -> "UMAPermission": + """Call method. + + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str + :returns: The combined UMA permission + :rtype: UMAPermission + :raises PermissionDefinitionError: In case bad permission definition + """ result_resource = self.resource result_scope = self.scope @@ -114,7 +152,11 @@ class Resource(UMAPermission): """ def __init__(self, resource): - """Init method.""" + """Init method. + + :param resource: Resource + :type resource: str + """ super().__init__(resource=resource) @@ -128,7 +170,11 @@ class Scope(UMAPermission): """ def __init__(self, scope): - """Init method.""" + """Init method. + + :param scope: Scope + :type scope: str + """ super().__init__(scope=scope) @@ -147,17 +193,33 @@ class AuthStatus: """ def __init__(self, is_logged_in, is_authorized, missing_permissions): - """Init method.""" + """Init method. + + :param is_logged_in: Is logged in indicator + :type is_logged_in: bool + :param is_authorized: Is authorized indicator + :type is_authorized: bool + :param missing_permissions: Missing permissions + :type missing_permissions: set + """ self.is_logged_in = is_logged_in self.is_authorized = is_authorized self.missing_permissions = missing_permissions def __bool__(self): - """Bool method.""" + """Bool method. + + :returns: Boolean representation + :rtype: bool + """ return self.is_authorized def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: The object representation + :rtype: str + """ return ( f"AuthStatus(" f"is_authorized={self.is_authorized}, " @@ -169,11 +231,11 @@ class AuthStatus: def build_permission_param(permissions): """Transform permissions to a set, so they are usable for requests. - :param permissions: either str (resource#scope), - iterable[str] (resource#scope), - dict[str,str] (resource: scope), - dict[str,iterable[str]] (resource: scopes) - :return: result bool + :param permissions: Permissions + :type permissions: str | Iterable[str] | dict[str, str] | dict[str, Iterabble[str]] + :returns: Permission parameters + :rtype: set + :raises KeycloakPermissionFormatError: In case of bad permission format """ if permissions is None or permissions == "": return set() From 0b79b5771ff7accf988e554de4eda195a77d5fba Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sat, 27 Aug 2022 18:28:32 +0000 Subject: [PATCH 114/222] test: updated docstrings to pass checks --- tests/conftest.py | 180 ++++++++++++++++++++--- tests/test_keycloak_admin.py | 263 +++++++++++++++++++++++++++++----- tests/test_keycloak_openid.py | 110 +++++++++++--- 3 files changed, 484 insertions(+), 69 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 39bd584..e0d93ea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,17 @@ class KeycloakTestEnv(object): username: str = os.environ["KEYCLOAK_ADMIN"], password: str = os.environ["KEYCLOAK_ADMIN_PASSWORD"], ): - """Init method.""" + """Init method. + + :param host: Hostname + :type host: str + :param port: Port + :type port: str + :param username: Admin username + :type username: str + :param password: Admin password + :type password: str + """ self.KEYCLOAK_HOST = host self.KEYCLOAK_PORT = port self.KEYCLOAK_ADMIN = username @@ -43,54 +53,96 @@ class KeycloakTestEnv(object): @property def KEYCLOAK_HOST(self): - """Hostname getter.""" + """Hostname getter. + + :returns: Keycloak host + :rtype: str + """ return self._KEYCLOAK_HOST @KEYCLOAK_HOST.setter def KEYCLOAK_HOST(self, value: str): - """Hostname setter.""" + """Hostname setter. + + :param value: Keycloak host + :type value: str + """ self._KEYCLOAK_HOST = value @property def KEYCLOAK_PORT(self): - """Port getter.""" + """Port getter. + + :returns: Keycloak port + :rtype: str + """ return self._KEYCLOAK_PORT @KEYCLOAK_PORT.setter def KEYCLOAK_PORT(self, value: str): - """Port setter.""" + """Port setter. + + :param value: Keycloak port + :type value: str + """ self._KEYCLOAK_PORT = value @property def KEYCLOAK_ADMIN(self): - """Admin username getter.""" + """Admin username getter. + + :returns: Admin username + :rtype: str + """ return self._KEYCLOAK_ADMIN @KEYCLOAK_ADMIN.setter def KEYCLOAK_ADMIN(self, value: str): - """Admin username setter.""" + """Admin username setter. + + :param value: Admin username + :type value: str + """ self._KEYCLOAK_ADMIN = value @property def KEYCLOAK_ADMIN_PASSWORD(self): - """Admin password getter.""" + """Admin password getter. + + :returns: Admin password + :rtype: str + """ return self._KEYCLOAK_ADMIN_PASSWORD @KEYCLOAK_ADMIN_PASSWORD.setter def KEYCLOAK_ADMIN_PASSWORD(self, value: str): - """Admin password setter.""" + """Admin password setter. + + :param value: Admin password + :type value: str + """ self._KEYCLOAK_ADMIN_PASSWORD = value @pytest.fixture def env(): - """Fixture for getting the test environment configuration object.""" + """Fixture for getting the test environment configuration object. + + :returns: Keycloak test environment object + :rtype: KeycloakTestEnv + """ return KeycloakTestEnv() @pytest.fixture def admin(env: KeycloakTestEnv): - """Fixture for initialized KeycloakAdmin class.""" + """Fixture for initialized KeycloakAdmin class. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :returns: Keycloak admin + :rtype: KeycloakAdmin + """ return KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -100,7 +152,17 @@ def admin(env: KeycloakTestEnv): @pytest.fixture def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for initialized KeycloakOpenID class.""" + """Fixture for initialized KeycloakOpenID class. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client + :rtype: KeycloakOpenID + """ # Set the realm admin.realm_name = realm # Create client @@ -126,7 +188,17 @@ def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): @pytest.fixture def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + """Fixture for an initialized KeycloakOpenID class and a random user credentials. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client with user credentials + :rtype: Tuple[KeycloakOpenID, str, str] + """ # Set the realm admin.realm_name = realm # Create client @@ -173,7 +245,17 @@ def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin) @pytest.fixture def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + """Fixture for an initialized KeycloakOpenID class and a random user credentials. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client configured as an authorization server with client credentials + :rtype: Tuple[KeycloakOpenID, str, str] + """ # Set the realm admin.realm_name = realm # Create client @@ -229,7 +311,13 @@ def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: Keycloak @pytest.fixture def realm(admin: KeycloakAdmin) -> str: - """Fixture for a new random realm.""" + """Fixture for a new random realm. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak realm + :rtype: str + """ realm_name = str(uuid.uuid4()) admin.create_realm(payload={"realm": realm_name, "enabled": True}) yield realm_name @@ -238,7 +326,15 @@ def realm(admin: KeycloakAdmin) -> str: @pytest.fixture def user(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random user.""" + """Fixture for a new random user. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak user + :rtype: str + """ admin.realm_name = realm username = str(uuid.uuid4()) user_id = admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) @@ -248,7 +344,15 @@ def user(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def group(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random group.""" + """Fixture for a new random group. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak group + :rtype: str + """ admin.realm_name = realm group_name = str(uuid.uuid4()) group_id = admin.create_group(payload={"name": group_name}) @@ -258,7 +362,15 @@ def group(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random client.""" + """Fixture for a new random client. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak client id + :rtype: str + """ admin.realm_name = realm client = str(uuid.uuid4()) client_id = admin.create_client(payload={"name": client, "clientId": client}) @@ -268,7 +380,17 @@ def client(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client_role(admin: KeycloakAdmin, realm: str, client: str) -> str: - """Fixture for a new random client role.""" + """Fixture for a new random client role. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :yields: Keycloak client role + :rtype: str + """ admin.realm_name = realm role = str(uuid.uuid4()) admin.create_client_role(client, {"name": role, "composite": False}) @@ -278,7 +400,19 @@ def client_role(admin: KeycloakAdmin, realm: str, client: str) -> str: @pytest.fixture def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_role: str) -> str: - """Fixture for a new random composite client role.""" + """Fixture for a new random composite client role. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param client_role: Keycloak client role + :type client_role: str + :yields: Composite client role + :rtype: str + """ admin.realm_name = realm role = str(uuid.uuid4()) admin.create_client_role(client, {"name": role, "composite": True}) @@ -290,7 +424,11 @@ def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_ @pytest.fixture def selfsigned_cert(): - """Generate self signed certificate for a hostname, and optional IP addresses.""" + """Generate self signed certificate for a hostname, and optional IP addresses. + + :returns: Selfsigned certificate + :rtype: Tuple[str, str] + """ hostname = "testcert" ip_addresses = None key = None diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index b55e1c7..f2865f9 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -22,7 +22,11 @@ def test_keycloak_version(): def test_keycloak_admin_bad_init(env): - """Test keycloak admin bad init.""" + """Test keycloak admin bad init. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ with pytest.raises(TypeError) as err: KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -43,7 +47,11 @@ def test_keycloak_admin_bad_init(env): def test_keycloak_admin_init(env): - """Test keycloak admin init.""" + """Test keycloak admin init. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ admin = KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -118,7 +126,11 @@ def test_keycloak_admin_init(env): def test_realms(admin: KeycloakAdmin): - """Test realms.""" + """Test realms. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ # Get realms realms = admin.get_realms() assert len(realms) == 1, realms @@ -183,7 +195,13 @@ def test_realms(admin: KeycloakAdmin): def test_import_export_realms(admin: KeycloakAdmin, realm: str): - """Test import and export of realms.""" + """Test import and export of realms. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True) @@ -201,7 +219,13 @@ def test_import_export_realms(admin: KeycloakAdmin, realm: str): def test_users(admin: KeycloakAdmin, realm: str): - """Test users.""" + """Test users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Check no users present @@ -293,7 +317,13 @@ def test_users(admin: KeycloakAdmin, realm: str): def test_users_pagination(admin: KeycloakAdmin, realm: str): - """Test user pagination.""" + """Test user pagination. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm for ind in range(admin.PAGE_SIZE + 50): @@ -311,7 +341,13 @@ def test_users_pagination(admin: KeycloakAdmin, realm: str): def test_idps(admin: KeycloakAdmin, realm: str): - """Test IDPs.""" + """Test IDPs. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Create IDP @@ -383,7 +419,13 @@ def test_idps(admin: KeycloakAdmin, realm: str): def test_user_credentials(admin: KeycloakAdmin, user: str): - """Test user credentials.""" + """Test user credentials. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ res = admin.set_user_password(user_id=user, password="booya", temporary=True) assert res == dict(), res @@ -411,7 +453,13 @@ def test_user_credentials(admin: KeycloakAdmin, user: str): def test_social_logins(admin: KeycloakAdmin, user: str): - """Test social logins.""" + """Test social logins. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ res = admin.add_user_social_login( user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test" ) @@ -451,7 +499,11 @@ def test_social_logins(admin: KeycloakAdmin, user: str): def test_server_info(admin: KeycloakAdmin): - """Test server info.""" + """Test server info. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ info = admin.get_server_info() assert set(info.keys()) == { "systemInfo", @@ -471,7 +523,13 @@ def test_server_info(admin: KeycloakAdmin): def test_groups(admin: KeycloakAdmin, user: str): - """Test groups.""" + """Test groups. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ # Test get groups groups = admin.get_groups() assert len(groups) == 0 @@ -615,7 +673,13 @@ def test_groups(admin: KeycloakAdmin, user: str): def test_clients(admin: KeycloakAdmin, realm: str): - """Test clients.""" + """Test clients. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get clients @@ -875,7 +939,13 @@ def test_clients(admin: KeycloakAdmin, realm: str): def test_realm_roles(admin: KeycloakAdmin, realm: str): - """Test realm roles.""" + """Test realm roles. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get realm roles @@ -1031,7 +1101,13 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): def test_client_roles(admin: KeycloakAdmin, client: str): - """Test client roles.""" + """Test client roles. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param client: Keycloak client + :type client: str + """ # Test get client roles res = admin.get_client_roles(client_id=client) assert len(res) == 0 @@ -1194,7 +1270,14 @@ def test_client_roles(admin: KeycloakAdmin, client: str): def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): - """Test enable token exchange.""" + """Test enable token exchange. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :raises AssertionError: In case of bad configuration + """ # Test enabling token exchange between two confidential clients admin.realm_name = realm @@ -1283,7 +1366,13 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): def test_email(admin: KeycloakAdmin, user: str): - """Test email.""" + """Test email. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ # Emails will fail as we don't have SMTP test setup with pytest.raises(KeycloakPutError) as err: admin.send_update_account(user_id=user, payload=dict()) @@ -1296,7 +1385,11 @@ def test_email(admin: KeycloakAdmin, user: str): def test_get_sessions(admin: KeycloakAdmin): - """Test get sessions.""" + """Test get sessions. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username)) assert len(sessions) >= 1 with pytest.raises(KeycloakGetError) as err: @@ -1305,7 +1398,13 @@ def test_get_sessions(admin: KeycloakAdmin): def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): - """Test get client installation provider.""" + """Test get client installation provider. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param client: Keycloak client + :type client: str + """ with pytest.raises(KeycloakGetError) as err: admin.get_client_installation_provider(client_id=client, provider_id="bad") assert err.match('404: b\'{"error":"Unknown Provider"}\'') @@ -1324,7 +1423,13 @@ def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): def test_auth_flows(admin: KeycloakAdmin, realm: str): - """Test auth flows.""" + """Test auth flows. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm res = admin.get_authentication_flows() @@ -1471,7 +1576,13 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str): def test_authentication_configs(admin: KeycloakAdmin, realm: str): - """Test authentication configs.""" + """Test authentication configs. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test list of auth providers @@ -1503,7 +1614,13 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str): def test_sync_users(admin: KeycloakAdmin, realm: str): - """Test sync users.""" + """Test sync users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Only testing the error message @@ -1513,7 +1630,13 @@ def test_sync_users(admin: KeycloakAdmin, realm: str): def test_client_scopes(admin: KeycloakAdmin, realm: str): - """Test client scopes.""" + """Test client scopes. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get client scopes @@ -1651,7 +1774,13 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): def test_components(admin: KeycloakAdmin, realm: str): - """Test components.""" + """Test components. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get components @@ -1702,7 +1831,13 @@ def test_components(admin: KeycloakAdmin, realm: str): def test_keys(admin: KeycloakAdmin, realm: str): - """Test keys.""" + """Test keys. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} assert {k["algorithm"] for k in admin.get_keys()["keys"]} == { @@ -1714,7 +1849,13 @@ def test_keys(admin: KeycloakAdmin, realm: str): def test_events(admin: KeycloakAdmin, realm: str): - """Test events.""" + """Test events. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm events = admin.get_events() @@ -1734,7 +1875,13 @@ def test_events(admin: KeycloakAdmin, realm: str): def test_auto_refresh(admin: KeycloakAdmin, realm: str): - """Test auto refresh token.""" + """Test auto refresh token. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ # Test get refresh admin.auto_refresh_token = list() admin.connection = ConnectionManager( @@ -1818,7 +1965,13 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): def test_get_required_actions(admin: KeycloakAdmin, realm: str): - """Test required actions.""" + """Test required actions. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ractions = admin.get_required_actions() assert isinstance(ractions, list) @@ -1836,7 +1989,13 @@ def test_get_required_actions(admin: KeycloakAdmin, realm: str): def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): - """Test get required action by alias.""" + """Test get required action by alias. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ractions = admin.get_required_actions() ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") @@ -1846,7 +2005,13 @@ def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): def test_update_required_action(admin: KeycloakAdmin, realm: str): - """Test update required action.""" + """Test update required action. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") old = copy.deepcopy(ra) @@ -1860,7 +2025,19 @@ def test_update_required_action(admin: KeycloakAdmin, realm: str): def test_get_composite_client_roles_of_group( admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str ): - """Test get composite client roles of group.""" + """Test get composite client roles of group. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param group: Keycloak group + :type group: str + :param composite_client_role: Composite client role + :type composite_client_role: str + """ admin.realm_name = realm role = admin.get_client_role(client, composite_client_role) admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role]) @@ -1871,7 +2048,19 @@ def test_get_composite_client_roles_of_group( def test_get_role_client_level_children( admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str ): - """Test get children of composite client role.""" + """Test get children of composite client role. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param composite_client_role: Composite client role + :type composite_client_role: str + :param client_role: Client role + :type client_role: str + """ admin.realm_name = realm child = admin.get_client_role(client, client_role) parent = admin.get_client_role(client, composite_client_role) @@ -1880,7 +2069,17 @@ def test_get_role_client_level_children( def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): - """Test upload certificate.""" + """Test upload certificate. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param selfsigned_cert: Selfsigned certificates + :type selfsigned_cert: tuple + """ admin.realm_name = realm cert, _ = selfsigned_cert cert = cert.decode("utf-8").strip() diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 6bee648..5a6a2ed 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -22,7 +22,11 @@ from keycloak.keycloak_openid import KeycloakOpenID def test_keycloak_openid_init(env): - """Test KeycloakOpenId's init method.""" + """Test KeycloakOpenId's init method. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ oid = KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name="master", @@ -37,7 +41,11 @@ def test_keycloak_openid_init(env): def test_well_known(oid: KeycloakOpenID): - """Test the well_known method.""" + """Test the well_known method. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ res = oid.well_known() assert res is not None assert res != dict() @@ -100,7 +108,13 @@ def test_well_known(oid: KeycloakOpenID): def test_auth_url(env, oid: KeycloakOpenID): - """Test the auth_url method.""" + """Test the auth_url method. + + :param env: Environment fixture + :type env: KeycloakTestEnv + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ res = oid.auth_url(redirect_uri="http://test.test/*") assert ( res @@ -111,7 +125,11 @@ def test_auth_url(env, oid: KeycloakOpenID): def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test the token method.""" + """Test the token method. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) assert token == { @@ -155,7 +173,13 @@ def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_exchange_token( oid_with_credentials: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test the exchange token method.""" + """Test the exchange token method. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ # Verify existing user oid, username, password = oid_with_credentials @@ -197,7 +221,11 @@ def test_exchange_token( def test_logout(oid_with_credentials): - """Test logout.""" + """Test logout. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -209,19 +237,34 @@ def test_logout(oid_with_credentials): def test_certs(oid: KeycloakOpenID): - """Test certificates.""" + """Test certificates. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ assert len(oid.certs()["keys"]) == 2 def test_public_key(oid: KeycloakOpenID): - """Test public key.""" + """Test public key. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ assert oid.public_key() is not None def test_entitlement( oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test entitlement.""" + """Test entitlement. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) resource_server_id = admin.get_client_authz_resources( @@ -233,7 +276,11 @@ def test_entitlement( def test_introspect(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test introspect.""" + """Test introspect. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -247,7 +294,11 @@ def test_introspect(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_decode_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test decode token.""" + """Test decode token. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -262,7 +313,12 @@ def test_decode_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_load_authorization_config(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test load authorization config.""" + """Test load authorization config. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz oid.load_authorization_config(path="tests/data/authz_settings.json") @@ -277,7 +333,12 @@ def test_load_authorization_config(oid_with_credentials_authz: Tuple[KeycloakOpe def test_get_policies(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test get policies.""" + """Test get policies. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -310,7 +371,12 @@ def test_get_policies(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str def test_get_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test get policies.""" + """Test get policies. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -354,7 +420,12 @@ def test_get_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, def test_uma_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test UMA permissions.""" + """Test UMA permissions. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -365,7 +436,14 @@ def test_uma_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, def test_has_uma_access( oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test has UMA access.""" + """Test has UMA access. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) From fd1e4b839b0def45874aa7f042b75986ba67abe4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sat, 27 Aug 2022 18:47:21 +0000 Subject: [PATCH 115/222] chore: dependency updates --- poetry.lock | 713 ++++++++-------------------------------------------- 1 file changed, 108 insertions(+), 605 deletions(-) diff --git a/poetry.lock b/poetry.lock index dd69dab..c94a679 100644 --- a/poetry.lock +++ b/poetry.lock @@ -117,17 +117,9 @@ category = "dev" optional = false python-versions = ">=3.6.1" -[[package]] -name = "chardet" -version = "5.0.0" -description = "Universal encoding detector for Python 3" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "charset-normalizer" -version = "2.1.0" +version = "2.1.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -150,15 +142,15 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "codespell" -version = "2.1.0" +version = "2.2.1" description = "Codespell" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.extras] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] hard-encoding-detection = ["chardet"] -dev = ["pytest-dependency", "pytest-cov", "pytest", "flake8", "check-manifest"] [[package]] name = "colorama" @@ -170,7 +162,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "commitizen" -version = "2.29.3" +version = "2.32.2" description = "Python commitizen client tool" category = "dev" optional = false @@ -178,7 +170,7 @@ python-versions = ">=3.6.2,<4.0.0" [package.dependencies] argcomplete = ">=1.12.1,<2.0.0" -chardet = ">=5.0.0,<6.0.0" +charset-normalizer = ">=2.1.0,<3.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" jinja2 = ">=2.10.3" @@ -198,11 +190,11 @@ optional = true python-versions = "*" [package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] [[package]] name = "coverage" -version = "6.4.2" +version = "6.4.4" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -251,7 +243,7 @@ python-versions = ">=3.6" [[package]] name = "distlib" -version = "0.3.5" +version = "0.3.6" description = "Distribution utilities" category = "dev" optional = false @@ -282,15 +274,15 @@ gmpy2 = ["gmpy2"] [[package]] name = "filelock" -version = "3.7.1" +version = "3.8.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" @@ -514,8 +506,8 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["pytest-benchmark", "pytest"] +dev = ["tox", "pre-commit"] [[package]] name = "pre-commit" @@ -601,12 +593,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.12.0" +version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = true python-versions = ">=3.6" +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pyparsing" version = "3.0.9" @@ -653,7 +648,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] [[package]] name = "python-jose" @@ -675,7 +670,7 @@ pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"] [[package]] name = "pytz" -version = "2022.1" +version = "2022.2.1" description = "World timezone definitions, modern and historical" category = "main" optional = true @@ -850,7 +845,7 @@ docutils = "<0.18" sphinx = ">=1.6" [package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] [[package]] name = "sphinxcontrib-applehelp" @@ -861,8 +856,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -873,8 +868,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -885,8 +880,8 @@ optional = true python-versions = ">=3.6" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +test = ["html5lib", "pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -897,7 +892,7 @@ optional = true python-versions = ">=3.5" [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["mypy", "flake8", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -908,8 +903,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -920,8 +915,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "termcolor" @@ -949,7 +944,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.1" +version = "0.11.4" description = "Style preserving TOML library" category = "dev" optional = false @@ -1004,7 +999,7 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.11" +version = "1.26.12" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1012,26 +1007,26 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.16.2" +version = "20.16.3" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -platformdirs = ">=2,<3" +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] name = "wcwidth" @@ -1070,585 +1065,93 @@ python-versions = "^3.7" content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0" [metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -argcomplete = [ - {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, - {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, -] +alabaster = [] +argcomplete = [] astroid = [] atomicwrites = [] attrs = [] -babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] -black = [ - {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, - {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, - {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, - {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, - {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, - {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, - {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, - {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, - {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, - {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, - {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, - {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, - {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, - {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, - {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, - {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, - {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, - {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, - {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, - {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, - {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, - {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, - {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, -] -certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, -] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -chardet = [] -charset-normalizer = [ - {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, - {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] +babel = [] +black = [] +certifi = [] +cffi = [] +cfgv = [] +charset-normalizer = [] +click = [] codespell = [] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] +colorama = [] commitizen = [] -commonmark = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] +commonmark = [] coverage = [] cryptography = [] darglint = [] -decli = [ - {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, - {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, -] +decli = [] distlib = [] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] +docutils = [] ecdsa = [] -filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -flake8-docstrings = [ - {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, - {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, -] +filelock = [] +flake8 = [] +flake8-docstrings = [] identify = [] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -imagesize = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, - {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, -] -m2r2 = [ - {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, - {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mistune = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] -mock = [ - {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, - {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, -] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] +idna = [] +imagesize = [] +importlib-metadata = [] +iniconfig = [] +isort = [] +jinja2 = [] +lazy-object-proxy = [] +m2r2 = [] +markupsafe = [] +mccabe = [] +mistune = [] +mock = [] +mypy-extensions = [] +nodeenv = [] +packaging = [] +pathspec = [] +platformdirs = [] +pluggy = [] pre-commit = [] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, - {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, -] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, -] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pytest = [ - {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, - {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -python-jose = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, -] -pytz = [ - {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, - {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -questionary = [ - {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, - {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, -] -readthedocs-sphinx-ext = [ - {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, - {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, -] -recommonmark = [ - {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, - {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, -] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] +prompt-toolkit = [] +py = [] +pyasn1 = [] +pycodestyle = [] +pycparser = [] +pydocstyle = [] +pyflakes = [] +pygments = [] +pyparsing = [] +pytest = [] +pytest-cov = [] +python-jose = [] +pytz = [] +pyyaml = [] +questionary = [] +readthedocs-sphinx-ext = [] +recommonmark = [] +requests = [] requests-toolbelt = [] rsa = [] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] +six = [] +snowballstemmer = [] sphinx = [] sphinx-autoapi = [] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, - {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -termcolor = [ - {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] +sphinx-rtd-theme = [] +sphinxcontrib-applehelp = [] +sphinxcontrib-devhelp = [] +sphinxcontrib-htmlhelp = [] +sphinxcontrib-jsmath = [] +sphinxcontrib-qthelp = [] +sphinxcontrib-serializinghtml = [] +termcolor = [] +toml = [] +tomli = [] tomlkit = [] -tox = [ - {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, - {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, -] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] -typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] -unidecode = [ - {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, - {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, -] +tox = [] +typed-ast = [] +typing-extensions = [] +unidecode = [] urllib3 = [] virtualenv = [] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -wrapt = [ - {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, - {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, - {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, - {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, - {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, - {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, - {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, - {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, - {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, - {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, - {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, - {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, - {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, - {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, - {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, - {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, -] +wcwidth = [] +wrapt = [] zipp = [] From ea8a0b441659b423491358cbe2e071da5d154020 Mon Sep 17 00:00:00 2001 From: Ice Lake <30789544+istiak101@users.noreply.github.com> Date: Fri, 2 Sep 2022 15:16:14 +0600 Subject: [PATCH 116/222] docs: change to username --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3558d30..117dd97 100644 --- a/README.md +++ b/README.md @@ -191,8 +191,8 @@ count_users = keycloak_admin.users_count() # Get users Returns a list of users, filtered according to query parameters users = keycloak_admin.get_users({}) -# Get user ID from name -user_id_keycloak = keycloak_admin.get_user_id("example@example.com") +# Get user ID from username +user_id_keycloak = keycloak_admin.get_user_id("username-keycloak") # Get User user = keycloak_admin.get_user("user-id-keycloak") From 31e51e394f26b254fa8f24524a284ed1e07bc42c Mon Sep 17 00:00:00 2001 From: Ice Lake <30789544+istiak101@users.noreply.github.com> Date: Fri, 2 Sep 2022 18:43:55 +0600 Subject: [PATCH 117/222] docs: fix send_update_account example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3558d30..1d4a55e 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ consents = keycloak_admin.consents_user(user_id="user-id-keycloak") # Send User Action response = keycloak_admin.send_update_account(user_id="user-id-keycloak", - payload=json.dumps(['UPDATE_PASSWORD'])) + payload=['UPDATE_PASSWORD']) # Send Verify Email response = keycloak_admin.send_verify_email(user_id="user-id-keycloak") From 754cd042748d57c71f23d14c3d21d9c4ab774c7a Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 2 Sep 2022 14:41:51 +0000 Subject: [PATCH 118/222] chore: update dependencies --- poetry.lock | 936 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 789 insertions(+), 147 deletions(-) diff --git a/poetry.lock b/poetry.lock index c94a679..caf1ad0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -30,18 +30,11 @@ python-versions = ">=3.6.2" [package.dependencies] lazy-object-proxy = ">=1.4.0" +setuptools = ">=20.0" typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<2" -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "attrs" version = "22.1.0" @@ -51,13 +44,13 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] -name = "babel" +name = "Babel" version = "2.10.3" description = "Internationalization utilities" category = "main" @@ -69,7 +62,7 @@ pytz = ">=2015.7" [[package]] name = "black" -version = "22.6.0" +version = "22.8.0" description = "The uncompromising code formatter." category = "dev" optional = false @@ -190,7 +183,7 @@ optional = true python-versions = "*" [package.extras] -test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" @@ -218,12 +211,12 @@ python-versions = ">=3.6" cffi = ">=1.12" [package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] [[package]] name = "darglint" @@ -350,9 +343,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" @@ -371,13 +364,13 @@ optional = false python-versions = ">=3.6.1,<4.0" [package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] plugins = ["setuptools"] +requirements_deprecated_finder = ["pip-api", "pipreqs"] [[package]] -name = "jinja2" +name = "Jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" @@ -411,7 +404,7 @@ docutils = "*" mistune = "0.8.4" [[package]] -name = "markupsafe" +name = "MarkupSafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -443,7 +436,7 @@ optional = true python-versions = ">=3.6" [package.extras] -build = ["twine", "wheel", "blurb"] +build = ["blurb", "twine", "wheel"] docs = ["sphinx"] test = ["pytest (<5.4)", "pytest-cov"] @@ -463,6 +456,9 @@ category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +[package.dependencies] +setuptools = "*" + [[package]] name = "packaging" version = "21.3" @@ -476,11 +472,11 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pathspec" -version = "0.9.0" +version = "0.10.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.7" [[package]] name = "platformdirs" @@ -491,8 +487,8 @@ optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "pluggy" @@ -506,8 +502,8 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] -testing = ["pytest-benchmark", "pytest"] -dev = ["tox", "pre-commit"] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" @@ -528,7 +524,7 @@ virtualenv = ">=20.0.8" [[package]] name = "prompt-toolkit" -version = "3.0.30" +version = "3.0.31" description = "Library for building powerful interactive command lines in Python" category = "dev" optional = false @@ -592,7 +588,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "pygments" +name = "Pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" @@ -611,18 +607,17 @@ optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "7.1.2" +version = "7.1.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -648,7 +643,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "python-jose" @@ -665,8 +660,8 @@ rsa = "*" [package.extras] cryptography = ["cryptography (>=3.4.0)"] -pycrypto = ["pycrypto (>=2.6.0,<2.7.0)", "pyasn1"] -pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] [[package]] name = "pytz" @@ -677,7 +672,7 @@ optional = true python-versions = "*" [[package]] -name = "pyyaml" +name = "PyYAML" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -696,7 +691,7 @@ python-versions = ">=3.6,<4.0" prompt_toolkit = ">=2.0,<4.0" [package.extras] -docs = ["Sphinx (>=3.3,<4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)"] +docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)"] [[package]] name = "readthedocs-sphinx-ext" @@ -764,6 +759,19 @@ python-versions = ">=3.6,<4" [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "setuptools" +version = "65.3.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -781,7 +789,7 @@ optional = false python-versions = "*" [[package]] -name = "sphinx" +name = "Sphinx" version = "5.1.1" description = "Python documentation generator" category = "main" @@ -809,8 +817,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "flake8-comprehensions", "flake8-bugbear", "isort", "mypy (>=0.971)", "sphinx-lint", "docutils-stubs", "types-typed-ast", "types-requests"] -test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "isort", "mypy (>=0.971)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed-ast"] [[package]] name = "sphinx-autoapi" @@ -856,8 +864,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -868,8 +876,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -880,8 +888,8 @@ optional = true python-versions = ">=3.6" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -892,7 +900,7 @@ optional = true python-versions = ">=3.5" [package.extras] -test = ["mypy", "flake8", "pytest"] +test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -903,8 +911,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -915,8 +923,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "termcolor" @@ -971,7 +979,7 @@ virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2, [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "psutil (>=5.6.1)", "pathlib2 (>=2.3.3)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] [[package]] name = "typed-ast" @@ -990,7 +998,7 @@ optional = false python-versions = ">=3.7" [[package]] -name = "unidecode" +name = "Unidecode" version = "1.3.4" description = "ASCII transliterations of Unicode text" category = "main" @@ -1006,13 +1014,13 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.16.3" +version = "20.16.4" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -1036,6 +1044,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "wheel" +version = "0.37.1" +description = "A built-package format for Python" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.extras] +test = ["pytest (>=3.0.0)", "pytest-cov"] + [[package]] name = "wrapt" version = "1.14.1" @@ -1053,8 +1072,8 @@ optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] +testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [extras] docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] @@ -1065,93 +1084,716 @@ python-versions = "^3.7" content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0" [metadata.files] -alabaster = [] -argcomplete = [] -astroid = [] -atomicwrites = [] -attrs = [] -babel = [] -black = [] -certifi = [] -cffi = [] -cfgv = [] -charset-normalizer = [] -click = [] -codespell = [] -colorama = [] -commitizen = [] -commonmark = [] -coverage = [] -cryptography = [] -darglint = [] -decli = [] -distlib = [] -docutils = [] -ecdsa = [] -filelock = [] -flake8 = [] -flake8-docstrings = [] -identify = [] -idna = [] -imagesize = [] -importlib-metadata = [] -iniconfig = [] -isort = [] -jinja2 = [] -lazy-object-proxy = [] -m2r2 = [] -markupsafe = [] -mccabe = [] -mistune = [] -mock = [] -mypy-extensions = [] -nodeenv = [] -packaging = [] -pathspec = [] -platformdirs = [] -pluggy = [] -pre-commit = [] -prompt-toolkit = [] -py = [] -pyasn1 = [] -pycodestyle = [] -pycparser = [] -pydocstyle = [] -pyflakes = [] -pygments = [] -pyparsing = [] -pytest = [] -pytest-cov = [] -python-jose = [] -pytz = [] -pyyaml = [] -questionary = [] -readthedocs-sphinx-ext = [] -recommonmark = [] -requests = [] -requests-toolbelt = [] -rsa = [] -six = [] -snowballstemmer = [] -sphinx = [] -sphinx-autoapi = [] -sphinx-rtd-theme = [] -sphinxcontrib-applehelp = [] -sphinxcontrib-devhelp = [] -sphinxcontrib-htmlhelp = [] -sphinxcontrib-jsmath = [] -sphinxcontrib-qthelp = [] -sphinxcontrib-serializinghtml = [] -termcolor = [] -toml = [] -tomli = [] -tomlkit = [] -tox = [] -typed-ast = [] -typing-extensions = [] -unidecode = [] -urllib3 = [] -virtualenv = [] -wcwidth = [] -wrapt = [] -zipp = [] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] +argcomplete = [ + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, +] +astroid = [ + {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, + {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, +] +attrs = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] +Babel = [ + {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, + {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +] +black = [ + {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, + {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, + {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, + {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, + {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, + {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, + {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, + {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, + {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, + {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, + {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, + {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, + {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, + {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, + {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, + {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, + {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, + {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, + {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, +] +certifi = [ + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, +] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] +cfgv = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +codespell = [ + {file = "codespell-2.2.1-py3-none-any.whl", hash = "sha256:0c53a70f466952706407383d87142a78f319a0e18602802c4aadd3d93158bfc6"}, + {file = "codespell-2.2.1.tar.gz", hash = "sha256:569b67e5e5c3ade02a1e23f6bbc56c64b608a3ab48ddd943ece0a03e6c346ed1"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +commitizen = [ + {file = "commitizen-2.32.2-py3-none-any.whl", hash = "sha256:44a07dc8bb5c6fb737471c1e92985b604ba5cad826ea928936537ff75c7b4a0c"}, + {file = "commitizen-2.32.2.tar.gz", hash = "sha256:44be55e35e727fdcbbb35fbe62e4cafedd8844393461906d753f7afc8ab62b0d"}, +] +commonmark = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] +coverage = [ + {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, + {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, + {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, + {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, + {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, + {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, + {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, + {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, + {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, + {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, + {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, + {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, + {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, + {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, + {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, + {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, +] +cryptography = [ + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, + {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, + {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, + {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, +] +darglint = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] +decli = [ + {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, + {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, +] +distlib = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] +docutils = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] +ecdsa = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] +filelock = [ + {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, + {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, +] +flake8 = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] +flake8-docstrings = [ + {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, + {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, +] +identify = [ + {file = "identify-2.5.3-py2.py3-none-any.whl", hash = "sha256:25851c8c1370effb22aaa3c987b30449e9ff0cece408f810ae6ce408fdd20893"}, + {file = "identify-2.5.3.tar.gz", hash = "sha256:887e7b91a1be152b0d46bbf072130235a8117392b9f1828446079a816a05ef44"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +imagesize = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, + {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +Jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +lazy-object-proxy = [ + {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, + {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, +] +m2r2 = [ + {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, + {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, +] +MarkupSafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +mock = [ + {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, + {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +nodeenv = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, + {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +pre-commit = [ + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.31-py3-none-any.whl", hash = "sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d"}, + {file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pydocstyle = [ + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, +] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] +Pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, + {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +python-jose = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] +pytz = [ + {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, + {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, +] +PyYAML = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +questionary = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] +readthedocs-sphinx-ext = [ + {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, + {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, +] +recommonmark = [ + {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, + {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, +] +requests = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] +rsa = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] +setuptools = [ + {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, + {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] +Sphinx = [ + {file = "Sphinx-5.1.1-py3-none-any.whl", hash = "sha256:309a8da80cb6da9f4713438e5b55861877d5d7976b69d87e336733637ea12693"}, + {file = "Sphinx-5.1.1.tar.gz", hash = "sha256:ba3224a4e206e1fbdecf98a4fae4992ef9b24b85ebf7b584bb340156eaf08d89"}, +] +sphinx-autoapi = [ + {file = "sphinx-autoapi-1.9.0.tar.gz", hash = "sha256:c897ea337df16ad0cde307cbdfe2bece207788dde1587fa4fc8b857d1fc5dcba"}, + {file = "sphinx_autoapi-1.9.0-py2.py3-none-any.whl", hash = "sha256:d217953273b359b699d8cb81a5a72985a3e6e15cfe3f703d9a3c201ffc30849b"}, +] +sphinx-rtd-theme = [ + {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, + {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, +] +sphinxcontrib-applehelp = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] +sphinxcontrib-devhelp = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] +sphinxcontrib-htmlhelp = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] +sphinxcontrib-jsmath = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] +sphinxcontrib-qthelp = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] +sphinxcontrib-serializinghtml = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] +termcolor = [ + {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tomlkit = [ + {file = "tomlkit-0.11.4-py3-none-any.whl", hash = "sha256:25d4e2e446c453be6360c67ddfb88838cfc42026322770ba13d1fbd403a93a5c"}, + {file = "tomlkit-0.11.4.tar.gz", hash = "sha256:3235a9010fae54323e727c3ac06fb720752fe6635b3426e379daec60fbd44a83"}, +] +tox = [ + {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, + {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, +] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] +typing-extensions = [ + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, +] +Unidecode = [ + {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, + {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, +] +urllib3 = [ + {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, +] +virtualenv = [ + {file = "virtualenv-20.16.4-py3-none-any.whl", hash = "sha256:035ed57acce4ac35c82c9d8802202b0e71adac011a511ff650cbcf9635006a22"}, + {file = "virtualenv-20.16.4.tar.gz", hash = "sha256:014f766e4134d0008dcaa1f95bafa0fb0f575795d07cae50b1bee514185d6782"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +wheel = [ + {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, + {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, +] +wrapt = [ + {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, + {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, + {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, + {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, + {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, + {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, + {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, + {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, + {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, + {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, + {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, + {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, + {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, + {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, + {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, + {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, +] +zipp = [ + {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, + {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, +] From d307825885f81d890f6697736a030dda4280c373 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 2 Sep 2022 14:49:54 +0000 Subject: [PATCH 119/222] docs: debug --- .readthedocs.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 4379fbf..0c9a2ba 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,3 +12,5 @@ build: - poetry config virtualenvs.create false post_install: - poetry install -E docs + - pip list + - poetry show From e04a99de4a7730740bb3a574c13cae601a9566f6 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 2 Sep 2022 14:56:46 +0000 Subject: [PATCH 120/222] docs: debug --- .readthedocs.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 0c9a2ba..fd6a120 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,9 +6,7 @@ build: python: "3.10" jobs: pre_create_environment: - - asdf plugin add poetry - - asdf install poetry latest - - asdf global poetry latest + - which python - poetry config virtualenvs.create false post_install: - poetry install -E docs From 3391871980c3b92b0aef2b79e2501e885135a3bf Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 2 Sep 2022 14:58:48 +0000 Subject: [PATCH 121/222] docs: debug --- .readthedocs.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index fd6a120..62fc38c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,10 +5,9 @@ build: tools: python: "3.10" jobs: - pre_create_environment: - - which python - - poetry config virtualenvs.create false post_install: - - poetry install -E docs + - python -m pip install poetry + - python -m poetry config virtualenvs.create false + - python -m poetry install -E docs - pip list - poetry show From f4eda508304f251df02b08b85c32bf154dcc29b8 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 2 Sep 2022 15:02:40 +0000 Subject: [PATCH 122/222] docs: fix docs build --- .readthedocs.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 62fc38c..960276c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,8 +6,6 @@ build: python: "3.10" jobs: post_install: - - python -m pip install poetry - - python -m poetry config virtualenvs.create false - - python -m poetry install -E docs - - pip list - - poetry show + - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m pip install poetry + - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m poetry config virtualenvs.create false + - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m poetry install -E docs From fc6a70f459f3caafbd85bc29fa423890cd68caf0 Mon Sep 17 00:00:00 2001 From: Fredrik Lindner Date: Mon, 3 Oct 2022 11:36:40 +0200 Subject: [PATCH 123/222] feat: attack detection API implementation --- src/keycloak/keycloak_admin.py | 38 ++++++++++ src/keycloak/urls_patterns.py | 5 ++ tests/test_keycloak_admin.py | 127 +++++++++++++++++++++++++++++++-- 3 files changed, 164 insertions(+), 6 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 798ba72..994b97d 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -3597,3 +3597,41 @@ class KeycloakAdmin: urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload ) return raise_error_from_response(data_raw, KeycloakPutError) + + def get_bruteforce_detection_status(self, user_id): + """Get bruteforce detection status for user. + + :param user_id: User id + :type user_id: str + :return: Bruteforce status. + :rtype: dict + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def clear_bruteforce_attempts_for_user(self, user_id): + """Clear bruteforce attempts for user. + + :param user_id: User id + :type user_id: str + :return: empty dictionary. + :rtype: dict + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakDeleteError) + + def clear_all_bruteforce_attempts(self): + """Clear bruteforce attempts for all users in realm. + + :return: empty dictionary. + :rtype: dict + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.raw_delete(urls_patterns.URL_ADMIN_ATTACK_DETECTION.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakDeleteError) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index f2a2188..b5f3277 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -195,3 +195,8 @@ URL_ADMIN_CLIENT_ROLE_CHILDREN = ( URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + "/upload-certificate" URL_ADMIN_REQUIRED_ACTIONS = URL_ADMIN_REALM + "/authentication/required-actions" URL_ADMIN_REQUIRED_ACTIONS_ALIAS = URL_ADMIN_REQUIRED_ACTIONS + "/{action-alias}" + +URL_ADMIN_ATTACK_DETECTION = "admin/realms/{realm-name}/attack-detection/brute-force/users" +URL_ADMIN_ATTACK_DETECTION_USER = ( + "admin/realms/{realm-name}/attack-detection/brute-force/users/{id}" +) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index b3ad951..b1625f0 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1,11 +1,12 @@ """Test the keycloak admin object.""" import copy +from typing import Tuple import pytest import keycloak -from keycloak import KeycloakAdmin +from keycloak import KeycloakAdmin, KeycloakOpenID from keycloak.connection import ConnectionManager from keycloak.exceptions import ( KeycloakAuthenticationError, @@ -1135,15 +1136,12 @@ def test_role_attributes( attribute_role = "test-realm-role-w-attr" test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]} role_id = admin.create_realm_role( - payload={"name": attribute_role, "attributes": test_attrs}, - skip_exists=True, + payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True ) assert role_id, role_id cli_role_id = admin.create_client_role( - client, - payload={"name": attribute_role, "attributes": test_attrs}, - skip_exists=True, + client, payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True ) assert cli_role_id, cli_role_id @@ -2285,3 +2283,120 @@ def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfs admin.upload_certificate(client, cert) cl = admin.get_client(client) assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1]) + + +def test_get_bruteforce_status_for_user( + admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str +): + """Test users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + :param realm: Keycloak realm + :type realm: str + """ + oid, username, password = oid_with_credentials + admin.realm_name = realm + + # Turn on bruteforce protection + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is True + + # Test login user with wrong credentials + try: + oid.token(username=username, password="wrongpassword") + except KeycloakAuthenticationError: + pass + + user_id = admin.get_user_id(username) + bruteforce_status = admin.get_bruteforce_detection_status(user_id) + + assert bruteforce_status["numFailures"] == 1 + + # Cleanup + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is False + + +def test_clear_bruteforce_attempts_for_user( + admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str +): + """Test users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + :param realm: Keycloak realm + :type realm: str + """ + oid, username, password = oid_with_credentials + admin.realm_name = realm + + # Turn on bruteforce protection + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is True + + # Test login user with wrong credentials + try: + oid.token(username=username, password="wrongpassword") + except KeycloakAuthenticationError: + pass + + user_id = admin.get_user_id(username) + bruteforce_status = admin.get_bruteforce_detection_status(user_id) + assert bruteforce_status["numFailures"] == 1 + + res = admin.clear_bruteforce_attempts_for_user(user_id) + bruteforce_status = admin.get_bruteforce_detection_status(user_id) + assert bruteforce_status["numFailures"] == 0 + + # Cleanup + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is False + + +def test_clear_bruteforce_attempts_for_all_users( + admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str +): + """Test users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + :param realm: Keycloak realm + :type realm: str + """ + oid, username, password = oid_with_credentials + admin.realm_name = realm + + # Turn on bruteforce protection + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is True + + # Test login user with wrong credentials + try: + oid.token(username=username, password="wrongpassword") + except KeycloakAuthenticationError: + pass + + user_id = admin.get_user_id(username) + bruteforce_status = admin.get_bruteforce_detection_status(user_id) + assert bruteforce_status["numFailures"] == 1 + + res = admin.clear_all_bruteforce_attempts() + bruteforce_status = admin.get_bruteforce_detection_status(user_id) + assert bruteforce_status["numFailures"] == 0 + + # Cleanup + res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False}) + res = admin.get_realm(realm_name=realm) + assert res["bruteForceProtected"] is False From a63982188ae14e1a0948f324bb3e9a8387650e65 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Mon, 3 Oct 2022 17:07:54 +0000 Subject: [PATCH 124/222] docs: changelog update --- CHANGELOG.md | 137 +++++++++++---------------------------------------- 1 file changed, 30 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f90b91..8674555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ -# Changelog - -## v2.5.0 (2022-08-19) +## v2.6.0 (2022-10-03) ### Feat -- added missing functionality to include attributes when returning realm roles according to specifications +- attack detection API implementation + +## v2.5.0 (2022-08-19) ## v2.4.0 (2022-08-19) ### Feat - add client scope-mappings client roles operations +- added missing functionality to include attributes when returning realm roles according to specifications ## v2.3.0 (2022-08-13) @@ -26,19 +27,18 @@ ## v2.1.1 (2022-07-19) -### Refactor - -- applied linting - ### Fix - removed whitespace from urls +### Refactor + +- applied linting + ## v2.1.0 (2022-07-18) ### Feat -- add functions covering some missing REST API calls - add unit tests - add docstrings - add functions covering some missing REST API calls @@ -55,15 +55,14 @@ ## v2.0.0 (2022-07-17) -### Fix - -- check client existence based on clientId -- check client existence based on clientId - ### BREAKING CHANGE - Renamed parameter client_name to client_id in get_client_id method +### Fix + +- check client existence based on clientId + ## v1.9.1 (2022-07-13) ### Fix @@ -82,17 +81,15 @@ ## v1.8.1 (2022-07-13) +### Feat + +- added flake8-docstrings and upgraded dependencies + ### Fix -- Support the auth_url method called with scope & state params now - Support the auth_url method called with scope & state params now - raise correct exceptions -### Feat - -- added flake8-docstrings and upgraded dependencies -- use poetry for package management - ### Refactor - slight restructure of the base fixtures @@ -101,7 +98,6 @@ ### Feat -- Ability to set custom timeout for KeycloakOpenId and KeycloakAdmin - Ability to set custom timeout for KCOpenId and KCAdmin ## v1.7.0 (2022-06-16) @@ -120,14 +116,12 @@ ### Feat -- Add update_idp - Add update_idp ## v1.4.0 (2022-06-02) ### Feat -- Add update_mapper_in_idp - Add update_mapper_in_idp ## v1.3.0 (2022-05-31) @@ -157,24 +151,22 @@ ### Fix -- allow query parameters for users count - allow query parameters for users count ## v1.0.0 (2022-05-25) -### Fix - -- correct spelling of public API method - ### BREAKING CHANGE - Renames `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` +### Fix + +- correct spelling of public API method + ## v0.29.1 (2022-05-24) ### Fix -- allow client_credentials token if username and password not spec… - allow client_credentials token if username and password not specified ## v0.29.0 (2022-05-23) @@ -185,15 +177,14 @@ ## v0.28.3 (2022-05-23) -### Fix - -- import classes in the base module -- import classes in the base module - ### Feat - added UMA-permission request functionality +### Fix + +- import classes in the base module + ## v0.28.2 (2022-05-19) ### Fix @@ -204,7 +195,6 @@ ### Fix -- Add missing keycloak.authorization package - Add missing keycloak.authorization package ## v0.28.0 (2022-05-19) @@ -217,16 +207,16 @@ - fixed admin client to pass the tests - initial setup of CICD and linting -### Refactor - -- isort conf.py -- Merge branch 'master' into feature/cicd - ### Fix - full tox fix ready - raise correct errors +### Refactor + +- isort conf.py +- Merge branch 'master' into feature/cicd + ## v0.27.1 (2022-05-18) ### Fix @@ -243,7 +233,6 @@ ### Feat -- add KeycloakAdmin.set_events - add KeycloakAdmin.set_events ## v0.25.0 (2021-05-05) @@ -267,69 +256,3 @@ ## v0.18.0 (2019-12-10) ## v0.17.6 (2019-10-10) - -## v0.5.0 (2017-08-21) - -### Feat - -- Basic functions for Keycloak API (well_know, token, userinfo, logout, certs, - entitlement, instropect) - -## v0.6.0 (2017-08-23) - -### Feat - -- Added load authorization settings - -## v0.7.0 (2017-08-23) - -### Feat - -- Added polices - -## v0.8.0 (2017-08-23) - -### Feat - -- Added permissions - -## v0.9.0 (2017-09-05) - -### Feat - -- Added functions for Admin Keycloak API - -## v0.10.0 (2017-10-23) - -### Feat - -- Updated libraries versions -- Updated Docs - -## v0.11.0 (2017-12-12) - -### Feat - -- Changed Instropect RPT - -## v0.12.0 (2018-01-25) - -### Feat - -- Add groups functions -- Add Admin Tasks for user and client role management -- Function to trigger user sync from provider - -## v0.12.1 (2018-08-04) - -### Feat - -- Add get_idps -- Rework group functions - -## master - -### Feat - -- Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` -- Add `KeycloakOpenID.token_exchange` to support Token Exchange From 6b30631378ad4d8d7b64d219d313be4e10429ba2 Mon Sep 17 00:00:00 2001 From: Igli Manaj Date: Fri, 14 Oct 2022 02:34:31 +0200 Subject: [PATCH 125/222] feat: helping functions for disabling users --- src/keycloak/keycloak_admin.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 994b97d..1b8426c 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -762,6 +762,28 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + def disable_user(self, user_id): + """Disable the user from the realm. Disabled users can not log in. + + :param user_id: User id + :type user_id: str + + :return: Http response + :rtype: bytes + """ + return self.update_user(user_id=user_id, payload={"enabled": False}) + + + def disable_all_users(self): + """Disable all existing users. + """ + users = self.get_users() + for user in users: + user_id = user["id"] + print(f"Disabling user with id: {user_id}") + self.disable_user(user_id=user_id) + + def delete_user(self, user_id): """Delete the user. From 477e0c5a3c712b74c29953f82b8c925e8ea567df Mon Sep 17 00:00:00 2001 From: Igli Manaj Date: Fri, 14 Oct 2022 02:44:02 +0200 Subject: [PATCH 126/222] feat: option for enabling users --- src/keycloak/keycloak_admin.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 1b8426c..55bb6d6 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -772,7 +772,17 @@ class KeycloakAdmin: :rtype: bytes """ return self.update_user(user_id=user_id, payload={"enabled": False}) + + def enable_user(self, user_id): + """Enable the user from the realm. + + :param user_id: User id + :type user_id: str + :return: Http response + :rtype: bytes + """ + return self.update_user(user_id=user_id, payload={"enabled": True}) def disable_all_users(self): """Disable all existing users. @@ -783,6 +793,14 @@ class KeycloakAdmin: print(f"Disabling user with id: {user_id}") self.disable_user(user_id=user_id) + def enable_all_users(self): + """Disable all existing users. + """ + users = self.get_users() + for user in users: + user_id = user["id"] + print(f"Enabling user with id: {user_id}") + self.enable_user(user_id=user_id) def delete_user(self, user_id): """Delete the user. From 1eaa4afc484caff8629463d69d5fb2ff1e890c48 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 25 Oct 2022 06:03:00 +0000 Subject: [PATCH 127/222] chore: dependency update --- poetry.lock | 353 ++++++++++++++++++++++++++-------------------------- 1 file changed, 178 insertions(+), 175 deletions(-) diff --git a/poetry.lock b/poetry.lock index caf1ad0..893fa2c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,11 +8,11 @@ python-versions = "*" [[package]] name = "argcomplete" -version = "1.12.3" +version = "2.0.0" description = "Bash tab completion for argparse" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} @@ -47,10 +47,10 @@ python-versions = ">=3.5" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] -name = "Babel" +name = "babel" version = "2.10.3" description = "Internationalization utilities" category = "main" @@ -62,11 +62,11 @@ pytz = ">=2015.7" [[package]] name = "black" -version = "22.8.0" +version = "22.10.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" @@ -85,7 +85,7 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.9.24" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -119,7 +119,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "click" @@ -135,34 +135,35 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "codespell" -version = "2.2.1" +version = "2.2.2" description = "Codespell" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency", "tomli"] hard-encoding-detection = ["chardet"] +toml = ["tomli"] [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "commitizen" -version = "2.32.2" +version = "2.35.0" description = "Python commitizen client tool" category = "dev" optional = false python-versions = ">=3.6.2,<4.0.0" [package.dependencies] -argcomplete = ">=1.12.1,<2.0.0" +argcomplete = ">=1.12.1,<2.1" charset-normalizer = ">=2.1.0,<3.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" @@ -170,7 +171,7 @@ jinja2 = ">=2.10.3" packaging = ">=19,<22" pyyaml = ">=3.08" questionary = ">=1.4.0,<2.0.0" -termcolor = ">=1.1,<2.0" +termcolor = {version = ">=1.1,<3", markers = "python_version >= \"3.7\""} tomlkit = ">=0.5.3,<1.0.0" typing-extensions = ">=4.0.1,<5.0.0" @@ -187,7 +188,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "6.4.4" +version = "6.5.0" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -305,7 +306,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.3" +version = "2.5.7" description = "File identification library for Python" category = "dev" optional = false @@ -316,7 +317,7 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.3" +version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -332,7 +333,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "4.12.0" +version = "4.13.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -343,9 +344,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" @@ -365,12 +366,12 @@ python-versions = ">=3.6.1,<4.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] plugins = ["setuptools"] -requirements_deprecated_finder = ["pip-api", "pipreqs"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] -name = "Jinja2" +name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" @@ -404,7 +405,7 @@ docutils = "*" mistune = "0.8.4" [[package]] -name = "MarkupSafe" +name = "markupsafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -588,7 +589,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "Pygments" +name = "pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" @@ -665,14 +666,14 @@ pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] [[package]] name = "pytz" -version = "2022.2.1" +version = "2022.5" description = "World timezone definitions, modern and historical" category = "main" optional = true python-versions = "*" [[package]] -name = "PyYAML" +name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -695,7 +696,7 @@ docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphin [[package]] name = "readthedocs-sphinx-ext" -version = "2.1.8" +version = "2.1.9" description = "Sphinx extension for Read the Docs overrides" category = "main" optional = true @@ -735,7 +736,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" @@ -761,14 +762,14 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "65.3.0" +version = "65.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -789,8 +790,8 @@ optional = false python-versions = "*" [[package]] -name = "Sphinx" -version = "5.1.1" +name = "sphinx" +version = "5.3.0" description = "Python documentation generator" category = "main" optional = true @@ -798,16 +799,16 @@ python-versions = ">=3.6" [package.dependencies] alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.14,<0.20" -imagesize = "*" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.12" requests = ">=2.5.0" -snowballstemmer = ">=1.1" +snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" @@ -817,8 +818,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "isort", "mypy (>=0.971)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed-ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] [[package]] name = "sphinx-autoapi" @@ -928,11 +929,14 @@ test = ["pytest"] [[package]] name = "termcolor" -version = "1.1.0" -description = "ANSII Color formatting for output in terminal." +version = "2.0.1" +description = "ANSI color formatting for output in terminal" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" + +[package.extras] +tests = ["pytest", "pytest-cov"] [[package]] name = "toml" @@ -952,7 +956,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.4" +version = "0.11.5" description = "Style preserving TOML library" category = "dev" optional = false @@ -960,7 +964,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "tox" -version = "3.25.1" +version = "3.26.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -974,7 +978,7 @@ packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" six = ">=1.14.0" -toml = ">=0.9.4" +tomli = {version = ">=2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""} virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" [package.extras] @@ -991,15 +995,15 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" [[package]] -name = "Unidecode" -version = "1.3.4" +name = "unidecode" +version = "1.3.6" description = "ASCII transliterations of Unicode text" category = "main" optional = true @@ -1020,7 +1024,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.16.4" +version = "20.16.5" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -1065,15 +1069,15 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "zipp" -version = "3.8.1" +version = "3.10.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [extras] docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] @@ -1089,8 +1093,8 @@ alabaster = [ {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] argcomplete = [ - {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, - {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, + {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, + {file = "argcomplete-2.0.0.tar.gz", hash = "sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20"}, ] astroid = [ {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, @@ -1100,38 +1104,36 @@ attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] -Babel = [ +babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] black = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, ] certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, ] cffi = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, @@ -1212,72 +1214,72 @@ click = [ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] codespell = [ - {file = "codespell-2.2.1-py3-none-any.whl", hash = "sha256:0c53a70f466952706407383d87142a78f319a0e18602802c4aadd3d93158bfc6"}, - {file = "codespell-2.2.1.tar.gz", hash = "sha256:569b67e5e5c3ade02a1e23f6bbc56c64b608a3ab48ddd943ece0a03e6c346ed1"}, + {file = "codespell-2.2.2-py3-none-any.whl", hash = "sha256:87dfcd9bdc9b3cb8b067b37f0af22044d7a84e28174adfc8eaa203056b7f9ecc"}, + {file = "codespell-2.2.2.tar.gz", hash = "sha256:c4d00c02b5a2a55661f00d5b4b3b5a710fa803ced9a9d7e45438268b099c319c"}, ] colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] commitizen = [ - {file = "commitizen-2.32.2-py3-none-any.whl", hash = "sha256:44a07dc8bb5c6fb737471c1e92985b604ba5cad826ea928936537ff75c7b4a0c"}, - {file = "commitizen-2.32.2.tar.gz", hash = "sha256:44be55e35e727fdcbbb35fbe62e4cafedd8844393461906d753f7afc8ab62b0d"}, + {file = "commitizen-2.35.0-py3-none-any.whl", hash = "sha256:ced3e161decf290c5263373dda440040405ed7f8b701b463d81e2ecc1e31d92c"}, + {file = "commitizen-2.35.0.tar.gz", hash = "sha256:34a7462c2279fc4e22929c03a9bb89242ab45dc501c0f17d1174e65c7fb9d793"}, ] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] coverage = [ - {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, - {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, - {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, - {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, - {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, - {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, - {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, - {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, - {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, - {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, - {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, - {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, - {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, - {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, - {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, - {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, - {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, - {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, - {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, - {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, - {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, - {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, - {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, - {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, - {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, - {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, - {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, - {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] cryptography = [ {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, @@ -1336,20 +1338,20 @@ flake8-docstrings = [ {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, ] identify = [ - {file = "identify-2.5.3-py2.py3-none-any.whl", hash = "sha256:25851c8c1370effb22aaa3c987b30449e9ff0cece408f810ae6ce408fdd20893"}, - {file = "identify-2.5.3.tar.gz", hash = "sha256:887e7b91a1be152b0d46bbf072130235a8117392b9f1828446079a816a05ef44"}, + {file = "identify-2.5.7-py2.py3-none-any.whl", hash = "sha256:7a67b2a6208d390fd86fd04fb3def94a3a8b7f0bcbd1d1fcd6736f4defe26390"}, + {file = "identify-2.5.7.tar.gz", hash = "sha256:5b8fd1e843a6d4bf10685dd31f4520a7f1c7d0e14e9bc5d34b1d6f111cabc011"}, ] idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, - {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1359,7 +1361,7 @@ isort = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] -Jinja2 = [ +jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] @@ -1406,7 +1408,7 @@ m2r2 = [ {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, ] -MarkupSafe = [ +markupsafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -1527,7 +1529,7 @@ pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] -Pygments = [ +pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] @@ -1548,10 +1550,10 @@ python-jose = [ {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, ] pytz = [ - {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, - {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, + {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, + {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, ] -PyYAML = [ +pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -1591,8 +1593,8 @@ questionary = [ {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, ] readthedocs-sphinx-ext = [ - {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, - {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, + {file = "readthedocs-sphinx-ext-2.1.9.tar.gz", hash = "sha256:470aadad72a6ccdc803466e45381a5b5d33ac5389553c6efee17ec0268a6986c"}, + {file = "readthedocs_sphinx_ext-2.1.9-py2.py3-none-any.whl", hash = "sha256:1a98ad8f054e331fe6691f2e62a466acf1753a2d747042001125c8b90a13db9b"}, ] recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, @@ -1611,8 +1613,8 @@ rsa = [ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] setuptools = [ - {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, - {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, + {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, + {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -1622,9 +1624,9 @@ snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -Sphinx = [ - {file = "Sphinx-5.1.1-py3-none-any.whl", hash = "sha256:309a8da80cb6da9f4713438e5b55861877d5d7976b69d87e336733637ea12693"}, - {file = "Sphinx-5.1.1.tar.gz", hash = "sha256:ba3224a4e206e1fbdecf98a4fae4992ef9b24b85ebf7b584bb340156eaf08d89"}, +sphinx = [ + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] sphinx-autoapi = [ {file = "sphinx-autoapi-1.9.0.tar.gz", hash = "sha256:c897ea337df16ad0cde307cbdfe2bece207788dde1587fa4fc8b857d1fc5dcba"}, @@ -1659,7 +1661,8 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] termcolor = [ - {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, + {file = "termcolor-2.0.1-py3-none-any.whl", hash = "sha256:7e597f9de8e001a3208c4132938597413b9da45382b6f1d150cff8d062b7aaa3"}, + {file = "termcolor-2.0.1.tar.gz", hash = "sha256:6b2cf769e93364a2676e1de56a7c0cff2cf5bd07f37e9cc80b0dd6320ebfe388"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, @@ -1670,12 +1673,12 @@ tomli = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tomlkit = [ - {file = "tomlkit-0.11.4-py3-none-any.whl", hash = "sha256:25d4e2e446c453be6360c67ddfb88838cfc42026322770ba13d1fbd403a93a5c"}, - {file = "tomlkit-0.11.4.tar.gz", hash = "sha256:3235a9010fae54323e727c3ac06fb720752fe6635b3426e379daec60fbd44a83"}, + {file = "tomlkit-0.11.5-py3-none-any.whl", hash = "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"}, + {file = "tomlkit-0.11.5.tar.gz", hash = "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c"}, ] tox = [ - {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, - {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, + {file = "tox-3.26.0-py2.py3-none-any.whl", hash = "sha256:bf037662d7c740d15c9924ba23bb3e587df20598697bb985ac2b49bdc2d847f6"}, + {file = "tox-3.26.0.tar.gz", hash = "sha256:44f3c347c68c2c68799d7d44f1808f9d396fc8a1a500cbc624253375c7ae107e"}, ] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, @@ -1704,20 +1707,20 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -Unidecode = [ - {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, - {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, +unidecode = [ + {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, + {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, ] urllib3 = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] virtualenv = [ - {file = "virtualenv-20.16.4-py3-none-any.whl", hash = "sha256:035ed57acce4ac35c82c9d8802202b0e71adac011a511ff650cbcf9635006a22"}, - {file = "virtualenv-20.16.4.tar.gz", hash = "sha256:014f766e4134d0008dcaa1f95bafa0fb0f575795d07cae50b1bee514185d6782"}, + {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, + {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -1794,6 +1797,6 @@ wrapt = [ {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] zipp = [ - {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, - {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, + {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, + {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, ] From b1d682623392859e5494bde52f234599b22af993 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 25 Oct 2022 06:03:11 +0000 Subject: [PATCH 128/222] ci: fix readthedocs build --- .readthedocs.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 960276c..9347d5d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,6 +6,6 @@ build: python: "3.10" jobs: post_install: - - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m pip install poetry - - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m poetry config virtualenvs.create false - - /home/docs/checkouts/readthedocs.org/user_builds/python-keycloak/envs/latest/bin/python -m poetry install -E docs + - pip install -U poetry + - poetry config virtualenvs.create false + - poetry install -E docs From eda6a2762ca36d933c5656f25c8d013d8e62d93b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 25 Oct 2022 06:38:59 +0000 Subject: [PATCH 129/222] ci: fix docs build --- docs/source/conf.py | 10 +++++++++- poetry.lock | 10 +++++----- pyproject.toml | 6 +++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c6bcb60..6216cce 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -180,7 +180,15 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "python-keycloak", "python-keycloak Documentation", [author], 1)] +man_pages = [ + ( + master_doc, + "python-keycloak", + "python-keycloak Documentation", + [author], + 1, + ) +] # -- Options for Texinfo output ------------------------------------------- diff --git a/poetry.lock b/poetry.lock index 893fa2c..6c67e94 100644 --- a/poetry.lock +++ b/poetry.lock @@ -823,7 +823,7 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] [[package]] name = "sphinx-autoapi" -version = "1.9.0" +version = "2.0.0" description = "Sphinx API documentation generator" category = "main" optional = true @@ -833,7 +833,7 @@ python-versions = ">=3.7" astroid = ">=2.7" Jinja2 = "*" PyYAML = "*" -sphinx = ">=3.0" +sphinx = ">=4.0" unidecode = "*" [package.extras] @@ -1085,7 +1085,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0" +content-hash = "da04d73122fef0b5938fc53dccdc95dbdd245f983ccd02a570c5569d945e89c1" [metadata.files] alabaster = [ @@ -1629,8 +1629,8 @@ sphinx = [ {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] sphinx-autoapi = [ - {file = "sphinx-autoapi-1.9.0.tar.gz", hash = "sha256:c897ea337df16ad0cde307cbdfe2bece207788dde1587fa4fc8b857d1fc5dcba"}, - {file = "sphinx_autoapi-1.9.0-py2.py3-none-any.whl", hash = "sha256:d217953273b359b699d8cb81a5a72985a3e6e15cfe3f703d9a3c201ffc30849b"}, + {file = "sphinx-autoapi-2.0.0.tar.gz", hash = "sha256:97dcf1b5b54cd0d8efef867594e4a4f3e2d3a2c0ec1e5a891e0a61bc77046006"}, + {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, ] sphinx-rtd-theme = [ {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, diff --git a/pyproject.toml b/pyproject.toml index a115c32..f863643 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,11 +37,11 @@ mock = {version = "^4.0.3", optional = true} alabaster = {version = "^0.7.12", optional = true} commonmark = {version = "^0.9.1", optional = true} recommonmark = {version = "^0.7.1", optional = true} -Sphinx = {version = "^5.0.2", optional = true} +Sphinx = {version = "^5.3.0", optional = true} sphinx-rtd-theme = {version = "^1.0.0", optional = true} -readthedocs-sphinx-ext = {version = "^2.1.8", optional = true} +readthedocs-sphinx-ext = {version = "^2.1.9", optional = true} m2r2 = {version = "^0.3.2", optional = true} -sphinx-autoapi = {version = "^1.8.4", optional = true} +sphinx-autoapi = {version = "^2.0.0", optional = true} requests-toolbelt = "^0.9.1" [tool.poetry.dev-dependencies] From 7bb9d643aac714f5710128842d522e4be48ca72d Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 25 Oct 2022 07:10:31 +0000 Subject: [PATCH 130/222] style: lint --- .github/workflows/bump.yaml | 42 +++++------ .github/workflows/daily.yaml | 28 ++++---- .github/workflows/lint.yaml | 124 ++++++++++++++++----------------- .github/workflows/publish.yaml | 66 +++++++++--------- 4 files changed, 130 insertions(+), 130 deletions(-) diff --git a/.github/workflows/bump.yaml b/.github/workflows/bump.yaml index e562346..c623d74 100644 --- a/.github/workflows/bump.yaml +++ b/.github/workflows/bump.yaml @@ -2,8 +2,8 @@ name: Bump version on: workflow_run: - workflows: [ "Lint" ] - branches: [ master ] + workflows: ["Lint"] + branches: [master] types: - completed @@ -11,22 +11,22 @@ jobs: tag-version: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - token: ${{ secrets.PAT_TOKEN }} - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: determine-version - run: | - VERSION=$(npx semantic-release --branches master --dry-run | { grep -i 'the next release version is' || test $? = 1; } | sed -E 's/.* ([[:digit:].]+)$/\1/') - echo "VERSION=$VERSION" >> $GITHUB_ENV - id: version - - uses: rickstaa/action-create-tag@v1 - continue-on-error: true - env: - GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} - with: - tag: v${{ env.VERSION }} - message: "Releasing v${{ env.VERSION }}" - github_token: ${{ secrets.PAT_TOKEN }} + - uses: actions/checkout@v3 + with: + token: ${{ secrets.PAT_TOKEN }} + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: determine-version + run: | + VERSION=$(npx semantic-release --branches master --dry-run | { grep -i 'the next release version is' || test $? = 1; } | sed -E 's/.* ([[:digit:].]+)$/\1/') + echo "VERSION=$VERSION" >> $GITHUB_ENV + id: version + - uses: rickstaa/action-create-tag@v1 + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + with: + tag: v${{ env.VERSION }} + message: "Releasing v${{ env.VERSION }}" + github_token: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml index 7ddc622..d53caf0 100644 --- a/.github/workflows/daily.yaml +++ b/.github/workflows/daily.yaml @@ -2,7 +2,7 @@ name: Daily check on: schedule: - - cron: '0 4 * * *' + - cron: "0 4 * * *" jobs: test: @@ -12,16 +12,16 @@ jobs: matrix: python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - uses: docker-practice/actions-setup-docker@master - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Run tests - run: | - tox -e tests + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - uses: docker-practice/actions-setup-docker@master + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tests + run: | + tox -e tests diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 61534ac..e156b1b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -2,51 +2,51 @@ name: Lint on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: check-commits: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: webiny/action-conventional-commits@v1.0.3 + - uses: actions/checkout@v3 + - uses: webiny/action-conventional-commits@v1.0.3 check-linting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Check linting, formatting - run: | - tox -e check + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Check linting, formatting + run: | + tox -e check check-docs: runs-on: ubuntu-latest needs: - - check-commits - - check-linting + - check-commits + - check-linting steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Check documentation build - run: | - tox -e docs + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Check documentation build + run: | + tox -e docs test: runs-on: ubuntu-latest @@ -55,41 +55,41 @@ jobs: matrix: python-version: ["3.7", "3.8", "3.9", "3.10"] needs: - - check-commits - - check-linting + - check-commits + - check-linting steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - uses: docker-practice/actions-setup-docker@master - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Run tests - run: | - tox -e tests - - name: Keycloak logs - run: | - cat keycloak_test_logs.txt + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - uses: docker-practice/actions-setup-docker@master + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tests + run: | + tox -e tests + - name: Keycloak logs + run: | + cat keycloak_test_logs.txt build: runs-on: ubuntu-latest needs: - - test - - check-docs + - test + - check-docs steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Run build - run: | - tox -e build + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run build + run: | + tox -e build diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 5c5f97c..c073b31 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -3,41 +3,41 @@ name: Publish on: push: tags: - - 'v*' + - "v*" jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: '0' - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox wheel twine - - name: Apply the tag version - run: | - version=${{ github.ref_name }} - sed -Ei '/^version = /s|= "[0-9.]+"$|= "'${version:-1}'"|' pyproject.toml - - name: Run build - run: | - tox -e build - - name: Publish to PyPi - env: - TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} - TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} - run: | - twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* - - name: Run changelog - run: | - tox -e changelog - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "docs: changelog update" - branch: master - file_pattern: CHANGELOG.md + - uses: actions/checkout@v3 + with: + fetch-depth: "0" + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox wheel twine + - name: Apply the tag version + run: | + version=${{ github.ref_name }} + sed -Ei '/^version = /s|= "[0-9.]+"$|= "'${version:-1}'"|' pyproject.toml + - name: Run build + run: | + tox -e build + - name: Publish to PyPi + env: + TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} + TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + run: | + twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* + - name: Run changelog + run: | + tox -e changelog + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "docs: changelog update" + branch: master + file_pattern: CHANGELOG.md From 6554235b505cf1b23c79ec53ef3e8f66826297c8 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 25 Oct 2022 07:10:46 +0000 Subject: [PATCH 131/222] ci: added python 3.11 --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index e156b1b..8a740af 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] needs: - check-commits - check-linting From 9ae8efd5ebb06a74ef0f2111ff2565096f4c266a Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:44:17 +0000 Subject: [PATCH 132/222] chore: dependency update --- poetry.lock | 290 +++++++++++++++++++++++----------------------------- 1 file changed, 130 insertions(+), 160 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6c67e94..a31d3c6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -51,7 +51,7 @@ tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy [[package]] name = "babel" -version = "2.10.3" +version = "2.11.0" description = "Internationalization utilities" category = "main" optional = true @@ -62,7 +62,7 @@ pytz = ">=2015.7" [[package]] name = "black" -version = "22.10.0" +version = "22.12.0" description = "The uncompromising code formatter." category = "dev" optional = false @@ -85,7 +85,7 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.9.24" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -156,7 +156,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 [[package]] name = "commitizen" -version = "2.35.0" +version = "2.38.0" description = "Python commitizen client tool" category = "dev" optional = false @@ -168,7 +168,7 @@ charset-normalizer = ">=2.1.0,<3.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" jinja2 = ">=2.10.3" -packaging = ">=19,<22" +packaging = ">=19" pyyaml = ">=3.08" questionary = ">=1.4.0,<2.0.0" termcolor = {version = ">=1.1,<3", markers = "python_version >= \"3.7\""} @@ -266,17 +266,28 @@ six = ">=1.9.0" gmpy = ["gmpy"] gmpy2 = ["gmpy2"] +[[package]] +name = "exceptiongroup" +version = "1.0.4" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "filelock" -version = "3.8.0" +version = "3.8.2" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.9.29)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=6.5)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" @@ -306,7 +317,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.7" +version = "2.5.9" description = "File identification library for Python" category = "dev" optional = false @@ -358,11 +369,11 @@ python-versions = "*" [[package]] name = "isort" -version = "5.10.1" +version = "5.11.2" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6.1,<4.0" +python-versions = ">=3.7.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] @@ -386,11 +397,11 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "lazy-object-proxy" -version = "1.7.1" +version = "1.8.0" description = "A fast and thorough lazy object proxy." category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "m2r2" @@ -462,18 +473,15 @@ setuptools = "*" [[package]] name = "packaging" -version = "21.3" +version = "22.0" description = "Core utilities for Python packages" category = "main" optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +python-versions = ">=3.7" [[package]] name = "pathspec" -version = "0.10.1" +version = "0.10.3" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -481,15 +489,15 @@ python-versions = ">=3.7" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.6.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] +test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -525,7 +533,7 @@ virtualenv = ">=20.0.8" [[package]] name = "prompt-toolkit" -version = "3.0.31" +version = "3.0.36" description = "Library for building powerful interactive command lines in Python" category = "dev" optional = false @@ -599,20 +607,9 @@ python-versions = ">=3.6" [package.extras] plugins = ["importlib-metadata"] -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - [[package]] name = "pytest" -version = "7.1.3" +version = "7.2.0" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -621,12 +618,12 @@ python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] @@ -666,7 +663,7 @@ pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] [[package]] name = "pytz" -version = "2022.5" +version = "2022.6" description = "World timezone definitions, modern and historical" category = "main" optional = true @@ -696,7 +693,7 @@ docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphin [[package]] name = "readthedocs-sphinx-ext" -version = "2.1.9" +version = "2.2.0" description = "Sphinx extension for Read the Docs overrides" category = "main" optional = true @@ -762,7 +759,7 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "65.5.0" +version = "65.6.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false @@ -770,7 +767,7 @@ python-versions = ">=3.7" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -843,18 +840,18 @@ go = ["sphinxcontrib-golangdomain"] [[package]] name = "sphinx-rtd-theme" -version = "1.0.0" +version = "1.1.1" description = "Read the Docs theme for Sphinx" category = "main" optional = true -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.dependencies] docutils = "<0.18" -sphinx = ">=1.6" +sphinx = ">=1.6,<6" [package.extras] -dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" @@ -929,7 +926,7 @@ test = ["pytest"] [[package]] name = "termcolor" -version = "2.0.1" +version = "2.1.1" description = "ANSI color formatting for output in terminal" category = "dev" optional = false @@ -956,15 +953,15 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.5" +version = "0.11.6" description = "Style preserving TOML library" category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6" [[package]] name = "tox" -version = "3.26.0" +version = "3.27.1" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -1011,11 +1008,11 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.12" +version = "1.26.13" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] @@ -1024,20 +1021,20 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.16.5" +version = "20.17.1" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -distlib = ">=0.3.5,<1" +distlib = ">=0.3.6,<1" filelock = ">=3.4.1,<4" importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] @@ -1069,7 +1066,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "zipp" -version = "3.10.0" +version = "3.11.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false @@ -1105,35 +1102,26 @@ attrs = [ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, ] black = [ - {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, - {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, - {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, - {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, - {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, - {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, - {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, - {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, - {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, - {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, - {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, - {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, - {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, - {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, - {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, - {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, - {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, - {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, - {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, - {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, - {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, ] certifi = [ - {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, - {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] cffi = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, @@ -1222,8 +1210,8 @@ colorama = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] commitizen = [ - {file = "commitizen-2.35.0-py3-none-any.whl", hash = "sha256:ced3e161decf290c5263373dda440040405ed7f8b701b463d81e2ecc1e31d92c"}, - {file = "commitizen-2.35.0.tar.gz", hash = "sha256:34a7462c2279fc4e22929c03a9bb89242ab45dc501c0f17d1174e65c7fb9d793"}, + {file = "commitizen-2.38.0-py3-none-any.whl", hash = "sha256:401e1d6907d752dbb00fd5a8b0d0201ff36bc110870168776d49de20bf5b8b61"}, + {file = "commitizen-2.38.0.tar.gz", hash = "sha256:7daa217f703f330c18548304400d133a834840fd01bc79ef2966426c74bdbf1f"}, ] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, @@ -1325,9 +1313,13 @@ ecdsa = [ {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, ] +exceptiongroup = [ + {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, +] filelock = [ - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, + {file = "filelock-3.8.2-py3-none-any.whl", hash = "sha256:8df285554452285f79c035efb0c861eb33a4bcfa5b7a137016e32e6a90f9792c"}, + {file = "filelock-3.8.2.tar.gz", hash = "sha256:7565f628ea56bfcd8e54e42bdc55da899c85c1abfe1b5bcfd147e9188cebb3b2"}, ] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, @@ -1338,8 +1330,8 @@ flake8-docstrings = [ {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, ] identify = [ - {file = "identify-2.5.7-py2.py3-none-any.whl", hash = "sha256:7a67b2a6208d390fd86fd04fb3def94a3a8b7f0bcbd1d1fcd6736f4defe26390"}, - {file = "identify-2.5.7.tar.gz", hash = "sha256:5b8fd1e843a6d4bf10685dd31f4520a7f1c7d0e14e9bc5d34b1d6f111cabc011"}, + {file = "identify-2.5.9-py2.py3-none-any.whl", hash = "sha256:a390fb696e164dbddb047a0db26e57972ae52fbd037ae68797e5ae2f4492485d"}, + {file = "identify-2.5.9.tar.gz", hash = "sha256:906036344ca769539610436e40a684e170c3648b552194980bb7b617a8daeb9f"}, ] idna = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, @@ -1358,51 +1350,33 @@ iniconfig = [ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, + {file = "isort-5.11.2-py3-none-any.whl", hash = "sha256:e486966fba83f25b8045f8dd7455b0a0d1e4de481e1d7ce4669902d9fb85e622"}, + {file = "isort-5.11.2.tar.gz", hash = "sha256:dd8bbc5c0990f2a095d754e50360915f73b4c26fc82733eb5bfc6b48396af4d2"}, ] jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, + {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"}, + {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"}, + {file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"}, + {file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"}, + {file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"}, + {file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"}, + {file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"}, + {file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"}, + {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"}, + {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"}, + {file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"}, + {file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"}, + {file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"}, + {file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"}, + {file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"}, + {file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"}, + {file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"}, + {file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"}, + {file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"}, ] m2r2 = [ {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, @@ -1471,16 +1445,16 @@ nodeenv = [ {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, + {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"}, + {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"}, ] pathspec = [ - {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, - {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, + {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, + {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, ] platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, + {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -1491,8 +1465,8 @@ pre-commit = [ {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.31-py3-none-any.whl", hash = "sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d"}, - {file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"}, + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, @@ -1533,13 +1507,9 @@ pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] pytest = [ - {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, - {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, + {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, + {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, ] pytest-cov = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, @@ -1550,8 +1520,8 @@ python-jose = [ {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, ] pytz = [ - {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, - {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, + {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, + {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, ] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, @@ -1593,8 +1563,8 @@ questionary = [ {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, ] readthedocs-sphinx-ext = [ - {file = "readthedocs-sphinx-ext-2.1.9.tar.gz", hash = "sha256:470aadad72a6ccdc803466e45381a5b5d33ac5389553c6efee17ec0268a6986c"}, - {file = "readthedocs_sphinx_ext-2.1.9-py2.py3-none-any.whl", hash = "sha256:1a98ad8f054e331fe6691f2e62a466acf1753a2d747042001125c8b90a13db9b"}, + {file = "readthedocs-sphinx-ext-2.2.0.tar.gz", hash = "sha256:e5effcd825816111a377ab7a897b819215138f8e5e8acc86f99218328f957240"}, + {file = "readthedocs_sphinx_ext-2.2.0-py2.py3-none-any.whl", hash = "sha256:d801f0bfb125d2837f18f40451462528d4a97eefd8de8a12ad526b4f1ce14205"}, ] recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, @@ -1613,8 +1583,8 @@ rsa = [ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] setuptools = [ - {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, - {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, + {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, + {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -1633,8 +1603,8 @@ sphinx-autoapi = [ {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, ] sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, - {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, + {file = "sphinx_rtd_theme-1.1.1-py2.py3-none-any.whl", hash = "sha256:31faa07d3e97c8955637fc3f1423a5ab2c44b74b8cc558a51498c202ce5cbda7"}, + {file = "sphinx_rtd_theme-1.1.1.tar.gz", hash = "sha256:6146c845f1e1947b3c3dd4432c28998a1693ccc742b4f9ad7c63129f0757c103"}, ] sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, @@ -1661,8 +1631,8 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] termcolor = [ - {file = "termcolor-2.0.1-py3-none-any.whl", hash = "sha256:7e597f9de8e001a3208c4132938597413b9da45382b6f1d150cff8d062b7aaa3"}, - {file = "termcolor-2.0.1.tar.gz", hash = "sha256:6b2cf769e93364a2676e1de56a7c0cff2cf5bd07f37e9cc80b0dd6320ebfe388"}, + {file = "termcolor-2.1.1-py3-none-any.whl", hash = "sha256:fa852e957f97252205e105dd55bbc23b419a70fec0085708fc0515e399f304fd"}, + {file = "termcolor-2.1.1.tar.gz", hash = "sha256:67cee2009adc6449c650f6bcf3bdeed00c8ba53a8cda5362733c53e0a39fb70b"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, @@ -1673,12 +1643,12 @@ tomli = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tomlkit = [ - {file = "tomlkit-0.11.5-py3-none-any.whl", hash = "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"}, - {file = "tomlkit-0.11.5.tar.gz", hash = "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c"}, + {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, + {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, ] tox = [ - {file = "tox-3.26.0-py2.py3-none-any.whl", hash = "sha256:bf037662d7c740d15c9924ba23bb3e587df20598697bb985ac2b49bdc2d847f6"}, - {file = "tox-3.26.0.tar.gz", hash = "sha256:44f3c347c68c2c68799d7d44f1808f9d396fc8a1a500cbc624253375c7ae107e"}, + {file = "tox-3.27.1-py2.py3-none-any.whl", hash = "sha256:f52ca66eae115fcfef0e77ef81fd107133d295c97c52df337adedb8dfac6ab84"}, + {file = "tox-3.27.1.tar.gz", hash = "sha256:b2a920e35a668cc06942ffd1cf3a4fb221a4d909ca72191fb6d84b0b18a7be04"}, ] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, @@ -1715,12 +1685,12 @@ unidecode = [ {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, ] urllib3 = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, + {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, + {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, ] virtualenv = [ - {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, - {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -1797,6 +1767,6 @@ wrapt = [ {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] zipp = [ - {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, - {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, ] From 90298f7af65af376c27ea374b6d3349b7a3a88f1 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:45:00 +0000 Subject: [PATCH 133/222] style: formatting applied --- docs/source/conf.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 6216cce..c6bcb60 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -180,15 +180,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ( - master_doc, - "python-keycloak", - "python-keycloak Documentation", - [author], - 1, - ) -] +man_pages = [(master_doc, "python-keycloak", "python-keycloak Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- From f1809970bcc49930fc7711294a6d04d8f8e4cb86 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:45:39 +0000 Subject: [PATCH 134/222] test: fix the tests for latest keycloak --- test_keycloak_init.sh | 2 +- tests/test_keycloak_admin.py | 39 +++++++++++++++++++---------------- tests/test_keycloak_openid.py | 3 +++ tox.ini | 1 + 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index 5dc5e53..07df97d 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -13,7 +13,7 @@ function keycloak_stop() { function keycloak_start() { echo "Starting keycloak docker container" - docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -e KC_FEATURES="token-exchange" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev + docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -e KC_FEATURES="token-exchange,admin-fine-grained-authz" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev SECONDS=0 until curl --silent --output /dev/null localhost:$KEYCLOAK_PORT; do sleep 5; diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index b1625f0..1420c56 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -506,21 +506,24 @@ def test_server_info(admin: KeycloakAdmin): :type admin: KeycloakAdmin """ info = admin.get_server_info() - assert set(info.keys()) == { - "systemInfo", - "memoryInfo", - "profileInfo", - "themes", - "socialProviders", - "identityProviders", - "providers", - "protocolMapperTypes", - "builtinProtocolMappers", - "clientInstallations", - "componentTypes", - "passwordPolicies", - "enums", - }, info.keys() + assert set(info.keys()).issubset( + { + "systemInfo", + "memoryInfo", + "profileInfo", + "themes", + "socialProviders", + "identityProviders", + "providers", + "protocolMapperTypes", + "builtinProtocolMappers", + "clientInstallations", + "componentTypes", + "passwordPolicies", + "enums", + "cryptoInfo", + } + ), info.keys() def test_groups(admin: KeycloakAdmin, user: str): @@ -790,7 +793,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakGetError) as err: admin.get_client_authz_settings(client_id=client_id) - assert err.match('500: b\'{"error":"HTTP 500 Internal Server Error"}\'') + assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') # Authz resources res = admin.get_client_authz_resources(client_id=auth_client_id) @@ -799,7 +802,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakGetError) as err: admin.get_client_authz_resources(client_id=client_id) - assert err.match('500: b\'{"error":"unknown_error"}\'') + assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') res = admin.create_client_authz_resource( client_id=auth_client_id, payload={"name": "test-resource"} @@ -885,7 +888,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakGetError) as err: admin.get_client_authz_scopes(client_id=client_id) - assert err.match('500: b\'{"error":"unknown_error"}\'') + assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') # Test service account user res = admin.get_client_service_account_user(client_id=auth_client_id) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 5a6a2ed..e1a1421 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -135,6 +135,7 @@ def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): assert token == { "access_token": mock.ANY, "expires_in": 300, + "id_token": mock.ANY, "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, @@ -148,6 +149,7 @@ def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): assert token == { "access_token": mock.ANY, "expires_in": 300, + "id_token": mock.ANY, "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, @@ -161,6 +163,7 @@ def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): assert token == { "access_token": mock.ANY, "expires_in": 300, + "id_token": mock.ANY, "not-before-policy": 0, "refresh_expires_in": 1800, "refresh_token": mock.ANY, diff --git a/tox.ini b/tox.ini index 2b3db36..174d074 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ requires = tox-poetry poetry + tox<4.0.0 envlist = check, apply-check, docs, tests, build, changelog [testenv] From 451a22a1030bb7266b6501c743a66436eee072b4 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:45:54 +0000 Subject: [PATCH 135/222] fix: default scope to openid --- src/keycloak/keycloak_openid.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 9a79474..56e0315 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -260,6 +260,7 @@ class KeycloakOpenID: code="", redirect_uri="", totp=None, + scope="openid", **extra ): """Retrieve user token. @@ -283,6 +284,8 @@ class KeycloakOpenID: :type redirect_uri: str :param totp: Time-based one-time password :type totp: int + :param scope: Scope, defaults to openid + :type scope: str :param extra: Additional extra arguments :type extra: dict :returns: Keycloak token @@ -296,6 +299,7 @@ class KeycloakOpenID: "grant_type": grant_type, "code": code, "redirect_uri": redirect_uri, + "scope": scope, } if extra: payload.update(extra) @@ -341,7 +345,7 @@ class KeycloakOpenID: audience: str, subject: str, requested_token_type: str = "urn:ietf:params:oauth:token-type:refresh_token", - scope: str = "", + scope: str = "openid", ) -> dict: """Exchange user token. @@ -358,7 +362,7 @@ class KeycloakOpenID: :type subject: str :param requested_token_type: Token type specification :type requested_token_type: str - :param scope: Scope + :param scope: Scope, defaults to openid :type scope: str :returns: Exchanged token :rtype: dict From fa955b759f6d7ce6e19ee833db0ce3cf961c114b Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:46:07 +0000 Subject: [PATCH 136/222] fix: use version from the package --- src/keycloak/_version.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/keycloak/_version.py b/src/keycloak/_version.py index f3403b2..d6030e7 100644 --- a/src/keycloak/_version.py +++ b/src/keycloak/_version.py @@ -21,4 +21,6 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__version__ = "0.0.0" +import pkg_resources + +__version__ = pkg_resources.get_distribution("python-keycloak").version From a86ae023f72ec4c21db220c05883b84a66d46db0 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:54:02 +0000 Subject: [PATCH 137/222] chore: added twine to poetry --- poetry.lock | 231 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 29 ++++--- 2 files changed, 242 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index a31d3c6..72a5065 100644 --- a/poetry.lock +++ b/poetry.lock @@ -83,6 +83,22 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "bleach" +version = "5.0.1" +description = "An easy safelist-based HTML-sanitizing tool." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.2)"] +dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] + [[package]] name = "certifi" version = "2022.12.7" @@ -180,7 +196,7 @@ name = "commonmark" version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" category = "main" -optional = true +optional = false python-versions = "*" [package.extras] @@ -248,7 +264,7 @@ name = "docutils" version = "0.17.1" description = "Docutils -- Python Documentation Utilities" category = "main" -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] @@ -381,6 +397,33 @@ pipfile-deprecated-finder = ["pipreqs", "requirementslib"] plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] +[[package]] +name = "jaraco-classes" +version = "3.2.3" +description = "Utility functions for Python class constructs" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + [[package]] name = "jinja2" version = "3.1.2" @@ -395,6 +438,25 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "keyring" +version = "23.11.0" +description = "Store and access your passwords safely." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "lazy-object-proxy" version = "1.8.0" @@ -452,6 +514,14 @@ build = ["blurb", "twine", "wheel"] docs = ["sphinx"] test = ["pytest (<5.4)", "pytest-cov"] +[[package]] +name = "more-itertools" +version = "9.0.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "mypy-extensions" version = "0.4.3" @@ -487,6 +557,17 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "pkginfo" +version = "1.9.2" +description = "Query metadatdata from sdists / bdists / installed packages." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +testing = ["pytest", "pytest-cov"] + [[package]] name = "platformdirs" version = "2.6.0" @@ -601,7 +682,7 @@ name = "pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" -optional = true +optional = false python-versions = ">=3.6" [package.extras] @@ -669,6 +750,14 @@ category = "main" optional = true python-versions = "*" +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "pyyaml" version = "6.0" @@ -691,6 +780,22 @@ prompt_toolkit = ">=2.0,<4.0" [package.extras] docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)"] +[[package]] +name = "readme-renderer" +version = "37.3" +description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +bleach = ">=2.1.0" +docutils = ">=0.13.1" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.8.0)"] + [[package]] name = "readthedocs-sphinx-ext" version = "2.2.0" @@ -746,6 +851,33 @@ python-versions = "*" [package.dependencies] requests = ">=2.0.1,<3.0.0" +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rich" +version = "12.6.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" +optional = false +python-versions = ">=3.6.3,<4.0.0" + +[package.dependencies] +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + [[package]] name = "rsa" version = "4.9" @@ -757,6 +889,18 @@ python-versions = ">=3.6,<4" [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + [[package]] name = "setuptools" version = "65.6.3" @@ -982,6 +1126,25 @@ virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2, docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] +[[package]] +name = "twine" +version = "4.0.2" +description = "Collection of utilities for publishing packages on PyPI" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = ">=3.6" +keyring = ">=15.1" +pkginfo = ">=1.8.1" +readme-renderer = ">=35.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +rich = ">=12.0.0" +urllib3 = ">=1.26.0" + [[package]] name = "typed-ast" version = "1.5.4" @@ -1045,6 +1208,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "wheel" version = "0.37.1" @@ -1082,7 +1253,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "da04d73122fef0b5938fc53dccdc95dbdd245f983ccd02a570c5569d945e89c1" +content-hash = "48a656650bc98b40759fa591d4878a407f5af1fe65d1035ab7bb6430bf9fae29" [metadata.files] alabaster = [ @@ -1119,6 +1290,10 @@ black = [ {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, ] +bleach = [ + {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, + {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, +] certifi = [ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, @@ -1353,10 +1528,22 @@ isort = [ {file = "isort-5.11.2-py3-none-any.whl", hash = "sha256:e486966fba83f25b8045f8dd7455b0a0d1e4de481e1d7ce4669902d9fb85e622"}, {file = "isort-5.11.2.tar.gz", hash = "sha256:dd8bbc5c0990f2a095d754e50360915f73b4c26fc82733eb5bfc6b48396af4d2"}, ] +jaraco-classes = [ + {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, + {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, +] +jeepney = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] +keyring = [ + {file = "keyring-23.11.0-py3-none-any.whl", hash = "sha256:3dd30011d555f1345dec2c262f0153f2f0ca6bca041fb1dc4588349bb4c0ac1e"}, + {file = "keyring-23.11.0.tar.gz", hash = "sha256:ad192263e2cdd5f12875dedc2da13534359a7e760e77f8d04b50968a821c2361"}, +] lazy-object-proxy = [ {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"}, {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"}, @@ -1436,6 +1623,10 @@ mock = [ {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, ] +more-itertools = [ + {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"}, + {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"}, +] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, @@ -1452,6 +1643,10 @@ pathspec = [ {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, ] +pkginfo = [ + {file = "pkginfo-1.9.2-py3-none-any.whl", hash = "sha256:d580059503f2f4549ad6e4c106d7437356dbd430e2c7df99ee1efe03d75f691e"}, + {file = "pkginfo-1.9.2.tar.gz", hash = "sha256:ac03e37e4d601aaee40f8087f63fc4a2a6c9814dda2c8fa6aab1b1829653bdfa"}, +] platformdirs = [ {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, @@ -1523,6 +1718,10 @@ pytz = [ {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, ] +pywin32-ctypes = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, @@ -1562,6 +1761,10 @@ questionary = [ {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, ] +readme-renderer = [ + {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, + {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, +] readthedocs-sphinx-ext = [ {file = "readthedocs-sphinx-ext-2.2.0.tar.gz", hash = "sha256:e5effcd825816111a377ab7a897b819215138f8e5e8acc86f99218328f957240"}, {file = "readthedocs_sphinx_ext-2.2.0-py2.py3-none-any.whl", hash = "sha256:d801f0bfb125d2837f18f40451462528d4a97eefd8de8a12ad526b4f1ce14205"}, @@ -1578,10 +1781,22 @@ requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] +rfc3986 = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] +rich = [ + {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"}, + {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"}, +] rsa = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] +secretstorage = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] setuptools = [ {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, @@ -1650,6 +1865,10 @@ tox = [ {file = "tox-3.27.1-py2.py3-none-any.whl", hash = "sha256:f52ca66eae115fcfef0e77ef81fd107133d295c97c52df337adedb8dfac6ab84"}, {file = "tox-3.27.1.tar.gz", hash = "sha256:b2a920e35a668cc06942ffd1cf3a4fb221a4d909ca72191fb6d84b0b18a7be04"}, ] +twine = [ + {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, + {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, +] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, @@ -1696,6 +1915,10 @@ wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] wheel = [ {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, diff --git a/pyproject.toml b/pyproject.toml index f863643..685d8da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,20 @@ m2r2 = {version = "^0.3.2", optional = true} sphinx-autoapi = {version = "^2.0.0", optional = true} requests-toolbelt = "^0.9.1" -[tool.poetry.dev-dependencies] +[tool.poetry.extras] +docs = [ + "mock", + "alabaster", + "commonmark", + "recommonmark", + "sphinx", + "sphinx-rtd-theme", + "readthedocs-sphinx-ext", + "m2r2", + "sphinx-autoapi", +] + +[tool.poetry.group.dev.dependencies] tox = "^3.25.0" pytest = "^7.1.2" pytest-cov = "^3.0.0" @@ -58,19 +71,7 @@ commitizen = "^2.28.0" cryptography = "^37.0.4" codespell = "^2.1.0" darglint = "^1.8.1" - -[tool.poetry.extras] -docs = [ - "mock", - "alabaster", - "commonmark", - "recommonmark", - "sphinx", - "sphinx-rtd-theme", - "readthedocs-sphinx-ext", - "m2r2", - "sphinx-autoapi", -] +twine = "^4.0.2" [build-system] requires = ["poetry-core>=1.0.0"] From 8496356103be92768921b500f646579c044593d7 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 13 Dec 2022 21:55:00 +0000 Subject: [PATCH 138/222] ci: use tox from poetry --- .github/workflows/daily.yaml | 5 +++-- .github/workflows/lint.yaml | 20 ++++++++++++-------- .github/workflows/publish.yaml | 9 +++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml index d53caf0..3b3fb0f 100644 --- a/.github/workflows/daily.yaml +++ b/.github/workflows/daily.yaml @@ -21,7 +21,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox + python -m pip install poetry + poetry install - name: Run tests run: | - tox -e tests + poetry run tox -e tests diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 8a740af..9f3458c 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -24,10 +24,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox + python -m pip install poetry + poetry install - name: Check linting, formatting run: | - tox -e check + poetry run tox -e check check-docs: runs-on: ubuntu-latest @@ -43,10 +44,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox + python -m pip install poetry + poetry install - name: Check documentation build run: | - tox -e docs + poetry run tox -e docs test: runs-on: ubuntu-latest @@ -67,10 +69,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox + python -m pip install poetry + poetry install - name: Run tests run: | - tox -e tests + poetry run tox -e tests - name: Keycloak logs run: | cat keycloak_test_logs.txt @@ -89,7 +92,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox + python -m pip install poetry + poetry install - name: Run build run: | - tox -e build + poetry run tox -e build diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index c073b31..a84cced 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -19,23 +19,24 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox wheel twine + python -m pip install poetry + poetry install - name: Apply the tag version run: | version=${{ github.ref_name }} sed -Ei '/^version = /s|= "[0-9.]+"$|= "'${version:-1}'"|' pyproject.toml - name: Run build run: | - tox -e build + poetry run tox -e build - name: Publish to PyPi env: TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} run: | - twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* + poetry run twine upload -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* - name: Run changelog run: | - tox -e changelog + poetry run tox -e changelog - uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: "docs: changelog update" From aca13dec9a7fadaea39f686304120cb648e84ad0 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Tue, 13 Dec 2022 22:20:46 +0000 Subject: [PATCH 139/222] docs: changelog update --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8674555..f247290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v2.6.1 (2022-12-13) + +### Fix + +- use version from the package +- default scope to openid + ## v2.6.0 (2022-10-03) ### Feat From e9b173024bdcfc30c824d0e03f0636c3fd877ebe Mon Sep 17 00:00:00 2001 From: Igli Manaj Date: Tue, 20 Dec 2022 02:17:21 +0100 Subject: [PATCH 140/222] refactor: remove print statements --- src/keycloak/keycloak_admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 55bb6d6..0e0cca0 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -790,7 +790,6 @@ class KeycloakAdmin: users = self.get_users() for user in users: user_id = user["id"] - print(f"Disabling user with id: {user_id}") self.disable_user(user_id=user_id) def enable_all_users(self): @@ -799,7 +798,6 @@ class KeycloakAdmin: users = self.get_users() for user in users: user_id = user["id"] - print(f"Enabling user with id: {user_id}") self.enable_user(user_id=user_id) def delete_user(self, user_id): From 4fe06af67745d0322ba9214fd9fc91b4bcde3f23 Mon Sep 17 00:00:00 2001 From: iglimanaj Date: Sat, 24 Dec 2022 00:47:53 +0100 Subject: [PATCH 141/222] refactor: code formatting after tox checks --- src/keycloak/keycloak_admin.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 0e0cca0..ac1f46e 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -772,7 +772,7 @@ class KeycloakAdmin: :rtype: bytes """ return self.update_user(user_id=user_id, payload={"enabled": False}) - + def enable_user(self, user_id): """Enable the user from the realm. @@ -783,18 +783,16 @@ class KeycloakAdmin: :rtype: bytes """ return self.update_user(user_id=user_id, payload={"enabled": True}) - + def disable_all_users(self): - """Disable all existing users. - """ + """Disable all existing users.""" users = self.get_users() for user in users: user_id = user["id"] self.disable_user(user_id=user_id) - + def enable_all_users(self): - """Disable all existing users. - """ + """Disable all existing users.""" users = self.get_users() for user in users: user_id = user["id"] From f9ecd865230398228cbf0b99a84340e86f4f4ac1 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Sat, 24 Dec 2022 08:19:24 +0000 Subject: [PATCH 142/222] docs: changelog update --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f247290..8be645d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ +## v2.7.0 (2022-12-24) + +### Refactor + +- code formatting after tox checks +- remove print statements + ## v2.6.1 (2022-12-13) +### Feat + +- option for enabling users +- helping functions for disabling users + ### Fix - use version from the package From 35af02af95a56799bcea03b9f2a67dd0156877a1 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Tue, 27 Dec 2022 21:39:00 +0100 Subject: [PATCH 143/222] ci: added python3.11 to daily check --- .github/workflows/daily.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml index 3b3fb0f..6f4168a 100644 --- a/.github/workflows/daily.yaml +++ b/.github/workflows/daily.yaml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} From eeb2fbb6281b626db5c9741bc4d6856f0ab19b38 Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 21:15:16 +0200 Subject: [PATCH 144/222] feat(api): add tests for create_authz_scopes --- src/keycloak/keycloak_admin.py | 19 +++++++++++++++++++ tests/test_keycloak_admin.py | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ac1f46e..6d74a8c 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1475,6 +1475,25 @@ class KeycloakAdmin: data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + def create_client_authz_scopes(self, client_id, payload): + """Create scopes for client. + + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :param payload: ScopeRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_ScopeRepresentation + :type payload: dict + :type client_id: str + :return: Keycloak server response + :rtype: bytes + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + def get_client_authz_permissions(self, client_id): """Get permissions from client. diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1420c56..29d9e13 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -890,6 +890,24 @@ def test_clients(admin: KeycloakAdmin, realm: str): admin.get_client_authz_scopes(client_id=client_id) assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') + res = admin.create_client_authz_scopes( + client_id=auth_client_id, payload={"name": "test-authz-scope"} + ) + assert res["name"] == "test-authz-scope", res + + with pytest.raises(KeycloakPostError) as err: + admin.create_client_authz_scopes( + client_id=auth_client_id, payload={"name": "test-authz-scope"} + ) + assert err.match('409: b\'{"error":"invalid_request"') + assert admin.create_client_authz_scopes( + client_id=auth_client_id, payload={"name": "test-authz-scope"}, skip_exists=True + ) == {"msg": "Already exists"} + + res = admin.get_client_authz_scopes(client_id=auth_client_id) + assert len(res) == 2 + assert {x["name"] for x in res} == {"Default Scope", "test-authz-scope"} + # Test service account user res = admin.get_client_service_account_user(client_id=auth_client_id) assert res["username"] == "service-account-authz-client", res From 5855ec867ec92757dc4d9012e4e49235ef21f14a Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 21:27:55 +0200 Subject: [PATCH 145/222] fix: create authz clients test case --- tests/test_keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 29d9e13..5be7dfb 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -887,7 +887,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): assert len(res) == 0, res with pytest.raises(KeycloakGetError) as err: - admin.get_client_authz_scopes(client_id=client_id) + admin.get_client_authz_scopes(client_id=auth_client_id) assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') res = admin.create_client_authz_scopes( From 1da9de39b6266d4e577a08b31127845c67ed4bc9 Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 21:47:01 +0200 Subject: [PATCH 146/222] fix: create authz clients test case --- tests/test_keycloak_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 5be7dfb..1afeda9 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -887,7 +887,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): assert len(res) == 0, res with pytest.raises(KeycloakGetError) as err: - admin.get_client_authz_scopes(client_id=auth_client_id) + admin.get_client_authz_scopes(client_id=client_id) assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'') res = admin.create_client_authz_scopes( @@ -905,8 +905,8 @@ def test_clients(admin: KeycloakAdmin, realm: str): ) == {"msg": "Already exists"} res = admin.get_client_authz_scopes(client_id=auth_client_id) - assert len(res) == 2 - assert {x["name"] for x in res} == {"Default Scope", "test-authz-scope"} + assert len(res) == 1 + assert {x["name"] for x in res} == {"test-authz-scope"} # Test service account user res = admin.get_client_service_account_user(client_id=auth_client_id) From 936ed549779b66bcda482c9a329db9a47d297b6c Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 22:14:22 +0200 Subject: [PATCH 147/222] fix: add testcase for invalid client id --- tests/test_keycloak_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1afeda9..128699a 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -897,9 +897,9 @@ def test_clients(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakPostError) as err: admin.create_client_authz_scopes( - client_id=auth_client_id, payload={"name": "test-authz-scope"} + client_id='invalid_client_id', payload={"name": "test-authz-scope"} ) - assert err.match('409: b\'{"error":"invalid_request"') + assert err.match('404: b\'{"error":"Could not find client"') assert admin.create_client_authz_scopes( client_id=auth_client_id, payload={"name": "test-authz-scope"}, skip_exists=True ) == {"msg": "Already exists"} From 15a325382fe2f969d997008e371e233154590682 Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 22:27:01 +0200 Subject: [PATCH 148/222] fix: fix linting --- tests/test_keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 128699a..1dcf635 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -897,7 +897,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakPostError) as err: admin.create_client_authz_scopes( - client_id='invalid_client_id', payload={"name": "test-authz-scope"} + client_id="invalid_client_id", payload={"name": "test-authz-scope"} ) assert err.match('404: b\'{"error":"Could not find client"') assert admin.create_client_authz_scopes( From 1d864703a3de98b0fdaa6ad46c1db0f9083b0f65 Mon Sep 17 00:00:00 2001 From: hadeer_e Date: Wed, 28 Dec 2022 22:39:08 +0200 Subject: [PATCH 149/222] fix: fix testing create_client_authz_scopes parameters --- tests/test_keycloak_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1dcf635..9dd6b51 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -901,8 +901,8 @@ def test_clients(admin: KeycloakAdmin, realm: str): ) assert err.match('404: b\'{"error":"Could not find client"') assert admin.create_client_authz_scopes( - client_id=auth_client_id, payload={"name": "test-authz-scope"}, skip_exists=True - ) == {"msg": "Already exists"} + client_id=auth_client_id, payload={"name": "test-authz-scope"} + ) res = admin.get_client_authz_scopes(client_id=auth_client_id) assert len(res) == 1 From f53f59cc0b659ca286d69de987c9cfd3b6a847a4 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Thu, 29 Dec 2022 06:39:09 +0000 Subject: [PATCH 150/222] docs: changelog update --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be645d..1b52112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## v2.8.0 (2022-12-29) + +### Feat + +- **api**: add tests for create_authz_scopes + +### Fix + +- fix testing create_client_authz_scopes parameters +- fix linting +- add testcase for invalid client id +- create authz clients test case +- create authz clients test case + ## v2.7.0 (2022-12-24) ### Refactor From aa207286f05372e2d59aafe72db4b4b6aee57c70 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Wed, 11 Jan 2023 10:56:44 +0000 Subject: [PATCH 151/222] feat: added default realm roles handlers --- src/keycloak/keycloak_admin.py | 62 +++++++++++++++++++++++++++-- src/keycloak/urls_patterns.py | 6 +-- tests/test_keycloak_admin.py | 73 ++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 6d74a8c..26792f5 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -988,7 +988,7 @@ class KeycloakAdmin: data_raw = self.raw_put( urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), data=json.dumps(payload), - **params_query + **params_query, ) return raise_error_from_response(data_raw, KeycloakPutError) @@ -1012,7 +1012,7 @@ class KeycloakAdmin: data_raw = self.raw_put( urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), data={}, - **params_query + **params_query, ) return raise_error_from_response(data_raw, KeycloakPutError) @@ -1657,6 +1657,62 @@ class KeycloakAdmin: urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query ) + def get_default_realm_role_id(self): + """Get the ID of the default realm role. + + :return: Realm role ID + :rtype: str + """ + all_realm_roles = self.get_realm_roles() + default_realm_roles = [ + realm_role + for realm_role in all_realm_roles + if realm_role["name"] == f"default-roles-{self.realm_name}" + ] + return default_realm_roles[0]["id"] + + def get_realm_default_roles(self): + """Get all the default realm roles. + + :return: Keycloak Server Response (UserRepresentation) + :rtype: list + """ + params_path = {"realm-name": self.realm_name, "role-id": self.get_default_realm_role_id()} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES_REALM.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def remove_realm_default_roles(self, payload): + """Remove a set of default realm roles. + + :param payload: List of RoleRepresentations + :type payload: list + :return: Keycloak Server Response + :rtype: dict + """ + params_path = {"realm-name": self.realm_name, "role-id": self.get_default_realm_role_id()} + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakDeleteError) + + def add_realm_default_roles(self, payload): + """Add a set of default realm roles. + + :param payload: List of RoleRepresentations + :type payload: list + :return: Keycloak Server Response + :rtype: dict + """ + params_path = {"realm-name": self.realm_name, "role-id": self.get_default_realm_role_id()} + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError) + def get_client_roles(self, client_id, brief_representation=True): """Get all roles for the client. @@ -2664,7 +2720,7 @@ class KeycloakAdmin: data_raw = self.raw_post( urls_patterns.URL_ADMIN_USER_STORAGE.format(**params_path), data=json.dumps(data), - **params_query + **params_query, ) return raise_error_from_response(data_raw, KeycloakPostError) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index b5f3277..4ea0449 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -189,9 +189,9 @@ URL_ADMIN_EVENTS_CONFIG = URL_ADMIN_EVENTS + "/config" URL_ADMIN_CLIENT_SESSION_STATS = "admin/realms/{realm-name}/client-session-stats" URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE = URL_ADMIN_GROUPS_CLIENT_ROLES + "/composite" -URL_ADMIN_CLIENT_ROLE_CHILDREN = ( - "admin/realms/{realm-name}/roles-by-id/{role-id}/composites/clients/{client-id}" -) +URL_ADMIN_REALM_ROLE_COMPOSITES = "admin/realms/{realm-name}/roles-by-id/{role-id}/composites" +URL_ADMIN_REALM_ROLE_COMPOSITES_REALM = URL_ADMIN_REALM_ROLE_COMPOSITES + "/realm" +URL_ADMIN_CLIENT_ROLE_CHILDREN = URL_ADMIN_REALM_ROLE_COMPOSITES + "/clients/{client-id}" URL_ADMIN_CLIENT_CERT_UPLOAD = URL_ADMIN_CLIENT_CERTS + "/upload-certificate" URL_ADMIN_REQUIRED_ACTIONS = URL_ADMIN_REALM + "/authentication/required-actions" URL_ADMIN_REQUIRED_ACTIONS_ALIAS = URL_ADMIN_REQUIRED_ACTIONS + "/{action-alias}" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 9dd6b51..f6d34d0 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -2421,3 +2421,76 @@ def test_clear_bruteforce_attempts_for_all_users( res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False}) res = admin.get_realm(realm_name=realm) assert res["bruteForceProtected"] is False + + +def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None: + """Test that the default realm role is present in a brand new realm. + + :param realm: Realm name + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + """ + admin.realm_name = realm + assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()] + assert ( + len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"]) + == 1 + ) + + +def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None: + """Test getter for the ID of the default realm role. + + :param realm: Realm name + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + """ + admin.realm_name = realm + assert ( + admin.get_default_realm_role_id() + == [x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"][0] + ) + + +def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None: + """Test getting, adding and deleting default realm roles. + + :param realm: Realm name + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + """ + admin.realm_name = realm + + # Test listing all default realm roles + roles = admin.get_realm_default_roles() + assert len(roles) == 2 + assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"} + + with pytest.raises(KeycloakGetError) as err: + admin.realm_name = "doesnotexist" + admin.get_realm_default_roles() + assert err.match('404: b\'{"error":"Realm not found."}\'') + admin.realm_name = realm + + # Test removing a default realm role + res = admin.remove_realm_default_roles(payload=[roles[0]]) + assert res == {} + assert roles[0] not in admin.get_realm_default_roles() + assert len(admin.get_realm_default_roles()) == 1 + + with pytest.raises(KeycloakDeleteError) as err: + admin.remove_realm_default_roles(payload=[{"id": "bad id"}]) + assert err.match('404: b\'{"error":"Could not find composite role"}\'') + + # Test adding a default realm role + res = admin.add_realm_default_roles(payload=[roles[0]]) + assert res == {} + assert roles[0] in admin.get_realm_default_roles() + assert len(admin.get_realm_default_roles()) == 2 + + with pytest.raises(KeycloakPostError) as err: + admin.add_realm_default_roles(payload=[{"id": "bad id"}]) + assert err.match('404: b\'{"error":"Could not find composite role"}\'') From 503df44452784d7bbfc30e043d20b89952a80cec Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Wed, 11 Jan 2023 11:18:26 +0000 Subject: [PATCH 152/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b52112..3c18674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v2.9.0 (2023-01-11) + +### Feat + +- added default realm roles handlers + ## v2.8.0 (2022-12-29) ### Feat From fb0445c0c7f24d116e440a31ed5f924908038940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Mendes?= Date: Thu, 2 Feb 2023 16:27:16 +0000 Subject: [PATCH 153/222] feat: init KeycloakAdmin with token --- src/keycloak/keycloak_admin.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 26792f5..d985c1d 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -54,6 +54,8 @@ class KeycloakAdmin: :type username: str :param password: admin password :type password: str + :param token: access and refresh tokens + :type token: dict :param totp: Time based OTP :type totp: str :param realm_name: realm name @@ -88,7 +90,6 @@ class KeycloakAdmin: _client_secret_key = None _auto_refresh_token = None _connection = None - _token = None _custom_headers = None _user_realm_name = None @@ -97,6 +98,7 @@ class KeycloakAdmin: server_url, username=None, password=None, + token=None, totp=None, realm_name="master", client_id="admin-cli", @@ -115,6 +117,8 @@ class KeycloakAdmin: :type username: str :param password: admin password :type password: str + :param token: access and refresh tokens + :type token: dict :param totp: Time based OTP :type totp: str :param realm_name: realm name @@ -139,6 +143,7 @@ class KeycloakAdmin: self.server_url = server_url self.username = username self.password = password + self.token = token self.totp = totp self.realm_name = realm_name self.client_id = client_id @@ -150,7 +155,8 @@ class KeycloakAdmin: self.timeout = timeout # Get token Admin - self.get_token() + if not self.token: + self.get_token() @property def server_url(self): From 9c51d02a63e367cacef5e463f47ca696be3ea2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Mendes?= Date: Fri, 3 Feb 2023 22:07:47 +0000 Subject: [PATCH 154/222] feat: update header if token is given --- src/keycloak/keycloak_admin.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index d985c1d..2a17da5 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -154,10 +154,22 @@ class KeycloakAdmin: self.custom_headers = custom_headers self.timeout = timeout - # Get token Admin - if not self.token: + if self.token is None: self.get_token() + headers = { + "Authorization": "Bearer " + self.token.get("access_token"), + "Content-Type": "application/json", + } if self.token is not None else {} + + if self.custom_headers is not None: + # merge custom headers to main headers + headers.update(self.custom_headers) + + self.connection = ConnectionManager( + base_url=self.server_url, headers=headers, timeout=60, verify=self.verify + ) + @property def server_url(self): """Get server url. @@ -3378,22 +3390,8 @@ class KeycloakAdmin: self.token = self.keycloak_openid.token( self.username, self.password, grant_type=grant_type, totp=self.totp ) - - headers = { - "Authorization": "Bearer " + self.token.get("access_token"), - "Content-Type": "application/json", - } else: self.token = None - headers = {} - - if self.custom_headers is not None: - # merge custom headers to main headers - headers.update(self.custom_headers) - - self.connection = ConnectionManager( - base_url=self.server_url, headers=headers, timeout=60, verify=self.verify - ) def refresh_token(self): """Refresh the token. From 23ecba1e6fcfb5f96bb27c6298396d190c8b3659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Mendes?= Date: Sat, 4 Feb 2023 13:30:37 +0000 Subject: [PATCH 155/222] style: fix formatting --- src/keycloak/keycloak_admin.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 2a17da5..9a60e07 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -157,10 +157,14 @@ class KeycloakAdmin: if self.token is None: self.get_token() - headers = { - "Authorization": "Bearer " + self.token.get("access_token"), - "Content-Type": "application/json", - } if self.token is not None else {} + headers = ( + { + "Authorization": "Bearer " + self.token.get("access_token"), + "Content-Type": "application/json", + } + if self.token is not None + else {} + ) if self.custom_headers is not None: # merge custom headers to main headers From f39fc53858b8a7e2a43f14c29f9d591b55b4e071 Mon Sep 17 00:00:00 2001 From: Philippe Moll Date: Mon, 5 Dec 2022 16:33:44 +0100 Subject: [PATCH 156/222] feat: Add Client Scopes of Client --- src/keycloak/keycloak_admin.py | 136 +++++++++++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 8 ++ tests/test_keycloak_admin.py | 92 ++++++++++++++++++++++ 3 files changed, 236 insertions(+) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 26792f5..d9742cf 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1539,6 +1539,142 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) + def get_client_default_client_scopes(self, client_id): + """Get all default client scopes from client. + + :param client_id: id of the client in which the new default client scope should be added + :type client_id: str + + :return: list of client scopes with id and name + :rtype: list + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def add_client_default_client_scope(self, client_id, client_scope_id, payload): + """Add a client scope to the default client scopes from client. + + Payload example:: + + payload={ + "realm":"testrealm", + "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + + :param client_id: id of the client in which the new default client scope should be added + :type client_id: str + :param client_scope_id: id of the new client scope that should be added + :type client_scope_id: str + :param payload: dictionary with realm, client and clientScopeId + :type payload: dict + + :return: Http response + :rtype: bytes + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client_scope_id": client_scope_id, + } + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError) + + def delete_client_default_client_scope(self, client_id, client_scope_id): + """Delete a client scope from the default client scopes of the client. + + :param client_id: id of the client in which the default client scope should be deleted + :type client_id: str + :param client_scope_id: id of the client scope that should be deleted + :type client_scope_id: str + + :return: list of client scopes with id and name + :rtype: list + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client_scope_id": client_scope_id, + } + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakDeleteError) + + def get_client_optional_client_scopes(self, client_id): + """Get all optional client scopes from client. + + :param client_id: id of the client in which the new optional client scope should be added + :type client_id: str + + :return: list of client scopes with id and name + :rtype: list + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + + def add_client_optional_client_scope(self, client_id, client_scope_id, payload): + """Add a client scope to the optional client scopes from client. + + Payload example:: + + payload={ + "realm":"testrealm", + "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + + :param client_id: id of the client in which the new optional client scope should be added + :type client_id: str + :param client_scope_id: id of the new client scope that should be added + :type client_scope_id: str + :param payload: dictionary with realm, client and clientScopeId + :type payload: dict + + :return: Http response + :rtype: bytes + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client_scope_id": client_scope_id, + } + data_raw = self.raw_put( + urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError) + + def delete_client_optional_client_scope(self, client_id, client_scope_id): + """Delete a client scope from the optional client scopes of the client. + + :param client_id: id of the client in which the optional client scope should be deleted + :type client_id: str + :param client_scope_id: id of the client scope that should be deleted + :type client_scope_id: str + + :return: list of client scopes with id and name + :rtype: list + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client_scope_id": client_scope_id, + } + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakDeleteError) + def create_client(self, payload, skip_exists=False): """Create a client. diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 4ea0449..d8c5f0f 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -95,6 +95,14 @@ URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES = URL_ADMIN_CLIENT + "/scope-mapping URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES = ( URL_ADMIN_CLIENT + "/scope-mappings/clients/{client}" ) +URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES = URL_ADMIN_CLIENT + "/optional-client-scopes" +URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE = ( + URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES + "/{client_scope_id}" +) +URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES = URL_ADMIN_CLIENT + "/default-client-scopes" +URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE = ( + URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES + "/{client_scope_id}" +) URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index f6d34d0..c59bb8c 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1318,6 +1318,98 @@ def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str assert len(roles) == 0 +def test_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str): + """Test client assignment of default client scopes. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + """ + admin.realm_name = realm + + client_id = admin.create_client( + payload={"name": "role-testing-client", "clientId": "role-testing-client"} + ) + # Test get client default scopes + # keycloak default roles: web-origins, acr, profile, roles, email + default_client_scopes = admin.get_client_default_client_scopes(client_id) + assert len(default_client_scopes) == 5, default_client_scopes + + # Test add a client scope to client default scopes + default_client_scope = "test-client-default-scope" + new_client_scope = { + "name": default_client_scope, + "description": f"Test Client Scope: {default_client_scope}", + "protocol": "openid-connect", + "attributes": {}, + } + new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False) + new_default_client_scope_data = { + "realm": realm, + "client": client_id, + "clientScopeId": new_client_scope_id, + } + admin.add_client_default_client_scope( + client_id, new_client_scope_id, new_default_client_scope_data + ) + default_client_scopes = admin.get_client_default_client_scopes(client_id) + assert len(default_client_scopes) == 6, default_client_scopes + + # Test remove a client default scope + admin.delete_client_default_client_scope(client_id, new_client_scope_id) + default_client_scopes = admin.get_client_default_client_scopes(client_id) + assert len(default_client_scopes) == 5, default_client_scopes + + +def test_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str): + """Test client assignment of optional client scopes. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + """ + admin.realm_name = realm + + client_id = admin.create_client( + payload={"name": "role-testing-client", "clientId": "role-testing-client"} + ) + # Test get client optional scopes + # keycloak optional roles: microprofile-jwt, offline_access, address, phone + optional_client_scopes = admin.get_client_optional_client_scopes(client_id) + assert len(optional_client_scopes) == 4, optional_client_scopes + + # Test add a client scope to client optional scopes + optional_client_scope = "test-client-optional-scope" + new_client_scope = { + "name": optional_client_scope, + "description": f"Test Client Scope: {optional_client_scope}", + "protocol": "openid-connect", + "attributes": {}, + } + new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False) + new_optional_client_scope_data = { + "realm": realm, + "client": client_id, + "clientScopeId": new_client_scope_id, + } + admin.add_client_optional_client_scope( + client_id, new_client_scope_id, new_optional_client_scope_data + ) + optional_client_scopes = admin.get_client_optional_client_scopes(client_id) + assert len(optional_client_scopes) == 5, optional_client_scopes + + # Test remove a client optional scope + admin.delete_client_optional_client_scope(client_id, new_client_scope_id) + optional_client_scopes = admin.get_client_optional_client_scopes(client_id) + assert len(optional_client_scopes) == 4, optional_client_scopes + + def test_client_roles(admin: KeycloakAdmin, client: str): """Test client roles. From ea624540c6f95bab12aa5680f600f859934dd189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Mendes?= Date: Wed, 8 Feb 2023 15:41:05 +0000 Subject: [PATCH 157/222] test: test admin init with token --- tests/test_keycloak_admin.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index f6d34d0..2ec7f8b 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -90,6 +90,15 @@ def test_keycloak_admin_init(env): ) assert admin.token + token = admin.token + admin = KeycloakAdmin( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + token=token, + realm_name=None, + user_realm_name=None, + ) + assert admin.token == token + admin.create_realm(payload={"realm": "authz", "enabled": True}) admin.realm_name = "authz" admin.create_client( From 811bcfef819f92e992f9c7ed29b3698af57546c3 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Wed, 8 Feb 2023 20:02:05 +0000 Subject: [PATCH 158/222] docs: changelog update --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c18674..e23cfb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v2.10.0 (2023-02-08) + +### Feat + +- update header if token is given +- init KeycloakAdmin with token + ## v2.9.0 (2023-01-11) ### Feat From b693d6e396860a233728d8c5794aede01999dbec Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Wed, 8 Feb 2023 20:10:36 +0000 Subject: [PATCH 159/222] docs: changelog update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23cfb6..080c8bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ +## v2.11.0 (2023-02-08) + ## v2.10.0 (2023-02-08) ### Feat - update header if token is given - init KeycloakAdmin with token +- Add Client Scopes of Client ## v2.9.0 (2023-01-11) From 12392fe9854c082904c3d1031c09cc9a95bc2354 Mon Sep 17 00:00:00 2001 From: Will Earp Date: Wed, 8 Feb 2023 21:11:14 +0100 Subject: [PATCH 160/222] fix: do not include CODEOWNERS (#407) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 685d8da..14e0285 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ packages = [ { include = "keycloak", from = "src/" }, { include = "keycloak/**/*.py", from = "src/" }, ] -include = ["LICENSE", "CHANGELOG.md", "CODEOWNERS", "CONTRIBUTING.md"] +include = ["LICENSE", "CHANGELOG.md", "CONTRIBUTING.md"] [tool.poetry.urls] Documentation = "https://python-keycloak.readthedocs.io/en/latest/" From 75f4571fa9c72a679954984dd97e7e424b4952d7 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Wed, 8 Feb 2023 20:15:06 +0000 Subject: [PATCH 161/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 080c8bb..f1695ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v2.11.1 (2023-02-08) + +### Fix + +- do not include CODEOWNERS (#407) + ## v2.11.0 (2023-02-08) ## v2.10.0 (2023-02-08) From fb84c3b67b8c97836fc9d46d9764583c24ec4e78 Mon Sep 17 00:00:00 2001 From: Nuwan Goonasekera <2070605+nuwang@users.noreply.github.com> Date: Fri, 10 Feb 2023 13:15:54 +0530 Subject: [PATCH 162/222] feat: add Keycloak UMA client (#403) --- src/keycloak/__init__.py | 2 + src/keycloak/keycloak_uma.py | 241 ++++++++++++++++++++++++++++++++ src/keycloak/uma_permissions.py | 6 +- src/keycloak/urls_patterns.py | 7 +- tests/conftest.py | 19 ++- tests/test_keycloak_uma.py | 120 ++++++++++++++++ 6 files changed, 390 insertions(+), 5 deletions(-) create mode 100644 src/keycloak/keycloak_uma.py create mode 100644 tests/test_keycloak_uma.py diff --git a/src/keycloak/__init__.py b/src/keycloak/__init__.py index 694e53d..7e49c8f 100644 --- a/src/keycloak/__init__.py +++ b/src/keycloak/__init__.py @@ -42,6 +42,7 @@ from .exceptions import ( ) from .keycloak_admin import KeycloakAdmin from .keycloak_openid import KeycloakOpenID +from .keycloak_uma import KeycloakUMA __all__ = [ "__version__", @@ -61,4 +62,5 @@ __all__ = [ "KeycloakSecretNotFound", "KeycloakAdmin", "KeycloakOpenID", + "KeycloakUMA", ] diff --git a/src/keycloak/keycloak_uma.py b/src/keycloak/keycloak_uma.py new file mode 100644 index 0000000..a066567 --- /dev/null +++ b/src/keycloak/keycloak_uma.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (C) 2017 Marcos Pereira +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Keycloak UMA module. + +The module contains a UMA compatible client for keycloak: +https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-federated-authz-2.0.html +""" +import json +from urllib.parse import quote_plus + +from .connection import ConnectionManager +from .exceptions import ( + KeycloakDeleteError, + KeycloakGetError, + KeycloakPostError, + KeycloakPutError, + raise_error_from_response, +) +from .urls_patterns import URL_UMA_WELL_KNOWN + + +class KeycloakUMA: + """Keycloak UMA client. + + :param server_url: Keycloak server url + :param client_id: client id + :param realm_name: realm name + :param client_secret_key: client secret key + :param verify: True if want check connection SSL + :param custom_headers: dict of custom header to pass to each HTML request + :param proxies: dict of proxies to sent the request by. + :param timeout: connection timeout in seconds + """ + + def __init__( + self, server_url, realm_name, verify=True, custom_headers=None, proxies=None, timeout=60 + ): + """Init method. + + :param server_url: Keycloak server url + :type server_url: str + :param realm_name: realm name + :type realm_name: str + :param verify: True if want check connection SSL + :type verify: bool + :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict + :param proxies: dict of proxies to sent the request by. + :type proxies: dict + :param timeout: connection timeout in seconds + :type timeout: int + """ + self.realm_name = realm_name + headers = custom_headers if custom_headers is not None else dict() + headers.update({"Content-Type": "application/json"}) + self.connection = ConnectionManager( + base_url=server_url, headers=headers, timeout=timeout, verify=verify, proxies=proxies + ) + self._well_known = None + + def _fetch_well_known(self): + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_UMA_WELL_KNOWN.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + @staticmethod + def format_url(url, **kwargs): + """Substitute url path parameters. + + Given a parameterized url string, returns the string after url encoding and substituting + the given params. For example, + `format_url("https://myserver/{my_resource}/{id}", my_resource="hello world", id="myid")` + would produce `https://myserver/hello+world/myid`. + + :param url: url string to format + :type url: str + :param kwargs: dict containing kwargs to substitute + :type kwargs: dict + :return: formatted string + :rtype: str + """ + return url.format(**{k: quote_plus(v) for k, v in kwargs.items()}) + + def _add_bearer_token_header(self, token): + self.connection.add_param_headers("Authorization", "Bearer " + token) + + @property + def uma_well_known(self): + """Get the well_known UMA2 config. + + :returns: It lists endpoints and other configuration options relevant + :rtype: dict + """ + # per instance cache + if not self._well_known: + self._well_known = self._fetch_well_known() + return self._well_known + + def resource_set_create(self, token, payload): + """Create a resource set. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#rfc.section.2.2.1 + + ResourceRepresentation + https://www.keycloak.org/docs-api/20.0.0/rest-api/index.html#_resourcerepresentation + + :param token: client access token + :type token: str + :param payload: ResourceRepresentation + :type payload: dict + :return: ResourceRepresentation with the _id property assigned + :rtype: dict + """ + self._add_bearer_token_header(token) + data_raw = self.connection.raw_post( + self.uma_well_known["resource_registration_endpoint"], data=json.dumps(payload) + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) + + def resource_set_update(self, token, resource_id, payload): + """Update a resource set. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#update-resource-set + + ResourceRepresentation + https://www.keycloak.org/docs-api/20.0.0/rest-api/index.html#_resourcerepresentation + + :param token: client access token + :type token: str + :param resource_id: id of the resource + :type resource_id: str + :param payload: ResourceRepresentation + :type payload: dict + :return: Response dict (empty) + :rtype: dict + """ + self._add_bearer_token_header(token) + url = self.format_url( + self.uma_well_known["resource_registration_endpoint"] + "/{id}", id=resource_id + ) + data_raw = self.connection.raw_put(url, data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) + + def resource_set_read(self, token, resource_id): + """Read a resource set. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#read-resource-set + + ResourceRepresentation + https://www.keycloak.org/docs-api/20.0.0/rest-api/index.html#_resourcerepresentation + + :param token: client access token + :type token: str + :param resource_id: id of the resource + :type resource_id: str + :return: ResourceRepresentation + :rtype: dict + """ + self._add_bearer_token_header(token) + url = self.format_url( + self.uma_well_known["resource_registration_endpoint"] + "/{id}", id=resource_id + ) + data_raw = self.connection.raw_get(url) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) + + def resource_set_delete(self, token, resource_id): + """Delete a resource set. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#delete-resource-set + + :param token: client access token + :type token: str + :param resource_id: id of the resource + :type resource_id: str + :return: Response dict (empty) + :rtype: dict + """ + self._add_bearer_token_header(token) + url = self.format_url( + self.uma_well_known["resource_registration_endpoint"] + "/{id}", id=resource_id + ) + data_raw = self.connection.raw_delete(url) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) + + def resource_set_list_ids(self, token): + """List all resource set ids. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#list-resource-sets + + :param token: client access token + :type token: str + :return: List of ids + :rtype: List[str] + """ + self._add_bearer_token_header(token) + data_raw = self.connection.raw_get(self.uma_well_known["resource_registration_endpoint"]) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) + + def resource_set_list(self, token): + """List all resource sets. + + Spec + https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#list-resource-sets + + ResourceRepresentation + https://www.keycloak.org/docs-api/20.0.0/rest-api/index.html#_resourcerepresentation + + :param token: client access token + :type token: str + :yields: Iterator over a list of ResourceRepresentations + :rtype: Iterator[dict] + """ + for resource_id in self.resource_set_list_ids(token): + resource = self.resource_set_read(token, resource_id) + yield resource diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index eadcd72..1560dd5 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -27,7 +27,7 @@ from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinit class UMAPermission: - """A class to conveniently assembly permissions. + """A class to conveniently assemble permissions. The class itself is callable, and will return the assembled permission. @@ -143,7 +143,7 @@ class UMAPermission: class Resource(UMAPermission): - """An UMAPermission Resource class to conveniently assembly permissions. + """A UMAPermission Resource class to conveniently assemble permissions. The class itself is callable, and will return the assembled permission. @@ -161,7 +161,7 @@ class Resource(UMAPermission): class Scope(UMAPermission): - """An UMAPermission Scope class to conveniently assembly permissions. + """A UMAPermission Scope class to conveniently assemble permissions. The class itself is callable, and will return the assembled permission. diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index d8c5f0f..4fedee1 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -25,7 +25,8 @@ # OPENID URLS URL_REALM = "realms/{realm-name}" -URL_WELL_KNOWN = "realms/{realm-name}/.well-known/openid-configuration" +URL_WELL_KNOWN_BASE = "realms/{realm-name}/.well-known" +URL_WELL_KNOWN = URL_WELL_KNOWN_BASE + "/openid-configuration" URL_TOKEN = "realms/{realm-name}/protocol/openid-connect/token" URL_USERINFO = "realms/{realm-name}/protocol/openid-connect/userinfo" URL_LOGOUT = "realms/{realm-name}/protocol/openid-connect/logout" @@ -208,3 +209,7 @@ URL_ADMIN_ATTACK_DETECTION = "admin/realms/{realm-name}/attack-detection/brute-f URL_ADMIN_ATTACK_DETECTION_USER = ( "admin/realms/{realm-name}/attack-detection/brute-force/users/{id}" ) + + +# UMA URLS +URL_UMA_WELL_KNOWN = URL_WELL_KNOWN_BASE + "/uma2-configuration" diff --git a/tests/conftest.py b/tests/conftest.py index e0d93ea..18cb6a3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID -from keycloak import KeycloakAdmin, KeycloakOpenID +from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakUMA class KeycloakTestEnv(object): @@ -475,3 +475,20 @@ def selfsigned_cert(): ) return cert_pem, key_pem + + +@pytest.fixture +def uma(env: KeycloakTestEnv, realm: str): + """Fixture for initialized KeycloakUMA class. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak OpenID client + :rtype: KeycloakOpenID + """ + # Return UMA + yield KeycloakUMA( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name=realm + ) diff --git a/tests/test_keycloak_uma.py b/tests/test_keycloak_uma.py new file mode 100644 index 0000000..2a1dde7 --- /dev/null +++ b/tests/test_keycloak_uma.py @@ -0,0 +1,120 @@ +"""Test module for KeycloakUMA.""" +import re +from typing import Tuple + +import pytest + +from keycloak import KeycloakOpenID +from keycloak.connection import ConnectionManager +from keycloak.exceptions import ( + KeycloakDeleteError, + KeycloakGetError, + KeycloakPostError, + KeycloakPutError, +) +from keycloak.keycloak_uma import KeycloakUMA + + +def test_keycloak_uma_init(env): + """Test KeycloakUMA's init method. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ + uma = KeycloakUMA( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name="master" + ) + + assert uma.realm_name == "master" + assert isinstance(uma.connection, ConnectionManager) + # should initially be empty + assert uma._well_known is None + assert uma.uma_well_known + # should be cached after first reference + assert uma._well_known is not None + + +def test_uma_well_known(uma: KeycloakUMA): + """Test the well_known method. + + :param uma: Keycloak UMA client + :type uma: KeycloakUMA + """ + res = uma.uma_well_known + assert res is not None + assert res != dict() + for key in ["resource_registration_endpoint"]: + assert key in res + + +def test_uma_resource_sets( + uma: KeycloakUMA, oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] +): + """Test resource sets. + + :param uma: Keycloak UMA client + :type uma: KeycloakUMA + :param oid_with_credentials_authz: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ + oid, _, _ = oid_with_credentials_authz + + token = oid.token(grant_type="client_credentials") + access_token = token["access_token"] + + # Check that only the default resource is present + resource_sets = uma.resource_set_list(access_token) + resource_set_list = list(resource_sets) + assert len(resource_set_list) == 1, resource_set_list + assert resource_set_list[0]["name"] == "Default Resource", resource_set_list[0]["name"] + + # Test create resource set + resource_to_create = { + "name": "mytest", + "scopes": ["test:read", "test:write"], + "type": "urn:test", + } + created_resource = uma.resource_set_create(access_token, resource_to_create) + assert created_resource + assert created_resource["_id"], created_resource + assert set(resource_to_create).issubset(set(created_resource)), created_resource + + # Test create the same resource set + with pytest.raises(KeycloakPostError) as err: + uma.resource_set_create(access_token, resource_to_create) + assert err.match( + re.escape( + '409: b\'{"error":"invalid_request","error_description":' + '"Resource with name [mytest] already exists."}\'' + ) + ) + + # Test get resource set + latest_resource = uma.resource_set_read(access_token, created_resource["_id"]) + assert latest_resource["name"] == created_resource["name"] + + # Test update resource set + latest_resource["name"] = "New Resource Name" + res = uma.resource_set_update(access_token, created_resource["_id"], latest_resource) + assert res == dict(), res + updated_resource = uma.resource_set_read(access_token, created_resource["_id"]) + assert updated_resource["name"] == "New Resource Name" + + # Test update resource set fail + with pytest.raises(KeycloakPutError) as err: + uma.resource_set_update( + token=access_token, resource_id=created_resource["_id"], payload={"wrong": "payload"} + ) + assert err.match('400: b\'{"error":"Unrecognized field') + + # Test delete resource set + res = uma.resource_set_delete(token=access_token, resource_id=created_resource["_id"]) + assert res == dict(), res + with pytest.raises(KeycloakGetError) as err: + uma.resource_set_read(access_token, created_resource["_id"]) + err.match("404: b''") + + # Test delete fail + with pytest.raises(KeycloakDeleteError) as err: + uma.resource_set_delete(token=access_token, resource_id=created_resource["_id"]) + assert err.match("404: b''") From c86400d6860878c08b3a2d5544b35e05a984b166 Mon Sep 17 00:00:00 2001 From: ryshoooo Date: Fri, 10 Feb 2023 07:59:05 +0000 Subject: [PATCH 163/222] docs: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1695ce..98a9894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v2.12.0 (2023-02-10) + +### Feat + +- add Keycloak UMA client (#403) + ## v2.11.1 (2023-02-08) ### Fix From 33f768fd746da2b4ba903d92988ab7e917523ee3 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 10 Feb 2023 08:52:16 +0000 Subject: [PATCH 164/222] chore: upgrade dependencies --- poetry.lock | 1644 +++++++++++++++++++++++++----------------------- pyproject.toml | 6 +- tox.ini | 15 +- 3 files changed, 878 insertions(+), 787 deletions(-) diff --git a/poetry.lock b/poetry.lock index 72a5065..94c356d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,16 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "alabaster" -version = "0.7.12" +version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" category = "main" optional = true -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] [[package]] name = "argcomplete" @@ -13,6 +19,10 @@ description = "Bash tab completion for argparse" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, + {file = "argcomplete-2.0.0.tar.gz", hash = "sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20"}, +] [package.dependencies] importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} @@ -27,6 +37,10 @@ description = "An abstract syntax tree for Python with inference support." category = "main" optional = true python-versions = ">=3.6.2" +files = [ + {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, + {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, +] [package.dependencies] lazy-object-proxy = ">=1.4.0" @@ -37,17 +51,22 @@ wrapt = ">=1.11,<2" [[package]] name = "attrs" -version = "22.1.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] name = "babel" @@ -56,6 +75,10 @@ description = "Internationalization utilities" category = "main" optional = true python-versions = ">=3.6" +files = [ + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, +] [package.dependencies] pytz = ">=2015.7" @@ -67,6 +90,20 @@ description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] [package.dependencies] click = ">=8.0.0" @@ -85,11 +122,15 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bleach" -version = "5.0.1" +version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, + {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, +] [package.dependencies] six = ">=1.9.0" @@ -97,7 +138,6 @@ webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.2)"] -dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] [[package]] name = "certifi" @@ -106,6 +146,10 @@ description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] [[package]] name = "cffi" @@ -114,6 +158,72 @@ description = "Foreign Function Interface for Python calling C code." category = "dev" optional = false python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] [package.dependencies] pycparser = "*" @@ -125,6 +235,10 @@ description = "Validate configuration and produce human readable error messages. category = "dev" optional = false python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] [[package]] name = "charset-normalizer" @@ -133,6 +247,10 @@ description = "The Real First Universal Charset Detector. Open, modern and activ category = "main" optional = false python-versions = ">=3.6.0" +files = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] [package.extras] unicode-backport = ["unicodedata2"] @@ -144,6 +262,10 @@ description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -156,6 +278,10 @@ description = "Codespell" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "codespell-2.2.2-py3-none-any.whl", hash = "sha256:87dfcd9bdc9b3cb8b067b37f0af22044d7a84e28174adfc8eaa203056b7f9ecc"}, + {file = "codespell-2.2.2.tar.gz", hash = "sha256:c4d00c02b5a2a55661f00d5b4b3b5a710fa803ced9a9d7e45438268b099c319c"}, +] [package.extras] dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency", "tomli"] @@ -169,14 +295,22 @@ description = "Cross-platform colored terminal text." category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "commitizen" -version = "2.38.0" +version = "2.41.0" description = "Python commitizen client tool" category = "dev" optional = false python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "commitizen-2.41.0-py3-none-any.whl", hash = "sha256:2044f6d1cf002f363280e0dcefc8b557095dc42530060bfe5ad917ea4d5c48a1"}, + {file = "commitizen-2.41.0.tar.gz", hash = "sha256:5d50093fe546cc3b5217780a8cdfc5a9de6a27ce8aa7db6443e2e47dc2b10b6a"}, +] [package.dependencies] argcomplete = ">=1.12.1,<2.1" @@ -196,19 +330,76 @@ name = "commonmark" version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" category = "main" -optional = false +optional = true python-versions = "*" +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] [package.extras] test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "6.5.0" +version = "7.1.0" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"}, + {file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"}, + {file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"}, + {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"}, + {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"}, + {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"}, + {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"}, + {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"}, + {file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"}, + {file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"}, + {file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"}, + {file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"}, + {file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"}, + {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"}, + {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"}, + {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"}, + {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"}, + {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"}, + {file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"}, + {file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"}, + {file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"}, + {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"}, + {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"}, + {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"}, + {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"}, + {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"}, + {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"}, + {file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"}, + {file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"}, + {file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"}, + {file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"}, + {file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"}, + {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"}, + {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"}, + {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"}, + {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"}, + {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"}, + {file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"}, + {file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"}, + {file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"}, + {file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"}, + {file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"}, + {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"}, + {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"}, + {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"}, + {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"}, + {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"}, + {file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"}, + {file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"}, + {file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"}, + {file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"}, +] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} @@ -223,6 +414,30 @@ description = "cryptography is a package which provides cryptographic recipes an category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, + {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, + {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, + {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, +] [package.dependencies] cffi = ">=1.12" @@ -242,6 +457,10 @@ description = "A utility for ensuring Google-style docstrings stay up to date wi category = "dev" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] [[package]] name = "decli" @@ -250,6 +469,10 @@ description = "Minimal, easy-to-use, declarative cli tool" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, + {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, +] [[package]] name = "distlib" @@ -258,14 +481,22 @@ description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] [[package]] name = "docutils" -version = "0.17.1" +version = "0.18.1" description = "Docutils -- Python Documentation Utilities" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, + {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, +] [[package]] name = "ecdsa" @@ -274,6 +505,10 @@ description = "ECDSA cryptographic signature library (pure python)" category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] [package.dependencies] six = ">=1.9.0" @@ -284,26 +519,34 @@ gmpy2 = ["gmpy2"] [[package]] name = "exceptiongroup" -version = "1.0.4" +version = "1.1.0" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, + {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, +] [package.extras] test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.8.2" +version = "3.9.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] [package.extras] -docs = ["furo (>=2022.9.29)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -testing = ["covdefaults (>=2.2.2)", "coverage (>=6.5)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" @@ -312,6 +555,10 @@ description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} @@ -321,11 +568,15 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "flake8-docstrings" -version = "1.6.0" +version = "1.7.0" description = "Extension for flake8 which uses pydocstyle to check docstrings" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] [package.dependencies] flake8 = ">=3" @@ -333,11 +584,15 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.9" +version = "2.5.17" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "identify-2.5.17-py2.py3-none-any.whl", hash = "sha256:7d526dd1283555aafcc91539acc061d8f6f59adb0a7bba462735b0a318bff7ed"}, + {file = "identify-2.5.17.tar.gz", hash = "sha256:93cc61a861052de9d4c541a7acb7e3dcc9c11b398a2144f6e52ae5285f5f4f06"}, +] [package.extras] license = ["ukkonen"] @@ -349,6 +604,10 @@ description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] [[package]] name = "imagesize" @@ -357,6 +616,10 @@ description = "Getting image size from png/jpeg/jpeg2000/gif file" category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] [[package]] name = "importlib-metadata" @@ -365,6 +628,10 @@ description = "Read metadata from Python packages" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, +] [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} @@ -375,25 +642,52 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +[[package]] +name = "importlib-resources" +version = "5.10.2" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"}, + {file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] name = "isort" -version = "5.11.2" +version = "5.11.5" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=3.7.0" +files = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] @@ -404,6 +698,10 @@ description = "Utility functions for Python class constructs" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, + {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, +] [package.dependencies] more-itertools = "*" @@ -419,6 +717,10 @@ description = "Low-level, pure Python DBus protocol wrapper." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] @@ -431,6 +733,10 @@ description = "A very fast and expressive template engine." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] [package.dependencies] MarkupSafe = ">=2.0" @@ -440,30 +746,74 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" -version = "23.11.0" +version = "23.13.1" description = "Store and access your passwords safely." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"}, + {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"}, +] [package.dependencies] importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} "jaraco.classes" = "*" jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} -pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] +completion = ["shtab"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "lazy-object-proxy" -version = "1.8.0" +version = "1.9.0" description = "A fast and thorough lazy object proxy." category = "main" optional = true python-versions = ">=3.7" +files = [ + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, +] [[package]] name = "m2r2" @@ -472,18 +822,100 @@ description = "Markdown and reStructuredText in a single file." category = "main" optional = true python-versions = "*" +files = [ + {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, + {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, +] [package.dependencies] docutils = "*" mistune = "0.8.4" +[[package]] +name = "markdown-it-py" +version = "2.1.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"}, + {file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"] +code-style = ["pre-commit (==2.6)"] +compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"] +linkify = ["linkify-it-py (>=1.0,<2.0)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" -version = "2.1.1" +version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] [[package]] name = "mccabe" @@ -492,6 +924,22 @@ description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = "*" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] [[package]] name = "mistune" @@ -500,6 +948,10 @@ description = "The fastest markdown parser in pure Python" category = "main" optional = true python-versions = "*" +files = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] [[package]] name = "mock" @@ -508,6 +960,10 @@ description = "Rolling backport of unittest.mock for all Pythons" category = "main" optional = true python-versions = ">=3.6" +files = [ + {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, + {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, +] [package.extras] build = ["blurb", "twine", "wheel"] @@ -521,14 +977,22 @@ description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"}, + {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"}, +] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] [[package]] name = "nodeenv" @@ -537,48 +1001,71 @@ description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] [package.dependencies] setuptools = "*" [[package]] name = "packaging" -version = "22.0" +version = "23.0" description = "Core utilities for Python packages" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, +] [[package]] name = "pkginfo" -version = "1.9.2" -description = "Query metadatdata from sdists / bdists / installed packages." +version = "1.9.6" +description = "Query metadata from sdists / bdists / installed packages." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, + {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, +] [package.extras] testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" -version = "2.6.0" +version = "3.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"}, + {file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] -test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -587,6 +1074,10 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -597,11 +1088,15 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.20.0" +version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] [package.dependencies] cfgv = ">=2.0.0" @@ -609,8 +1104,7 @@ identify = ">=1.0.0" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" +virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" @@ -619,6 +1113,10 @@ description = "Library for building powerful interactive command lines in Python category = "dev" optional = false python-versions = ">=3.6.2" +files = [ + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, +] [package.dependencies] wcwidth = "*" @@ -630,6 +1128,10 @@ description = "library with cross-python path, ini-parsing, io, code, log facili category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] [[package]] name = "pyasn1" @@ -638,6 +1140,21 @@ description = "ASN.1 types and codecs" category = "main" optional = false python-versions = "*" +files = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] [[package]] name = "pycodestyle" @@ -646,6 +1163,10 @@ description = "Python style guide checker" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] [[package]] name = "pycparser" @@ -654,20 +1175,29 @@ description = "C parser in Python" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] [[package]] name = "pydocstyle" -version = "6.1.1" +version = "6.3.0" description = "Python docstring style checker" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] [package.dependencies] -snowballstemmer = "*" +importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""} +snowballstemmer = ">=2.2.0" [package.extras] -toml = ["toml"] +toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" @@ -676,25 +1206,37 @@ description = "passive checker of Python programs" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] [[package]] name = "pygments" -version = "2.13.0" +version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, +] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pytest" -version = "7.2.0" +version = "7.2.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, +] [package.dependencies] attrs = ">=19.2.0" @@ -716,6 +1258,10 @@ description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} @@ -731,6 +1277,10 @@ description = "JOSE implementation in Python" category = "main" optional = false python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] [package.dependencies] ecdsa = "!=0.15" @@ -744,11 +1294,15 @@ pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] [[package]] name = "pytz" -version = "2022.6" +version = "2022.7.1" description = "World timezone definitions, modern and historical" category = "main" optional = true python-versions = "*" +files = [ + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, +] [[package]] name = "pywin32-ctypes" @@ -757,6 +1311,10 @@ description = "" category = "dev" optional = false python-versions = "*" +files = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] [[package]] name = "pyyaml" @@ -765,6 +1323,41 @@ description = "YAML parser and emitter for Python" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] [[package]] name = "questionary" @@ -773,6 +1366,10 @@ description = "Python library to build pretty command line user prompts ⭐️" category = "dev" optional = false python-versions = ">=3.6,<4.0" +files = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] [package.dependencies] prompt_toolkit = ">=2.0,<4.0" @@ -787,6 +1384,10 @@ description = "readme_renderer is a library for rendering \"readme\" description category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, + {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, +] [package.dependencies] bleach = ">=2.1.0" @@ -803,6 +1404,10 @@ description = "Sphinx extension for Read the Docs overrides" category = "main" optional = true python-versions = "*" +files = [ + {file = "readthedocs-sphinx-ext-2.2.0.tar.gz", hash = "sha256:e5effcd825816111a377ab7a897b819215138f8e5e8acc86f99218328f957240"}, + {file = "readthedocs_sphinx_ext-2.2.0-py2.py3-none-any.whl", hash = "sha256:d801f0bfb125d2837f18f40451462528d4a97eefd8de8a12ad526b4f1ce14205"}, +] [package.dependencies] Jinja2 = ">=2.9" @@ -816,6 +1421,10 @@ description = "A docutils-compatibility bridge to CommonMark, enabling you to wr category = "main" optional = true python-versions = "*" +files = [ + {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, + {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, +] [package.dependencies] commonmark = ">=0.8.1" @@ -824,15 +1433,19 @@ sphinx = ">=1.3.1" [[package]] name = "requests" -version = "2.28.1" +version = "2.28.2" description = "Python HTTP for Humans." category = "main" optional = false python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" @@ -842,11 +1455,15 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" -version = "0.9.1" +version = "0.10.1" description = "A utility belt for advanced users of python-requests" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] [package.dependencies] requests = ">=2.0.1,<3.0.0" @@ -858,25 +1475,33 @@ description = "Validating URI References per RFC 3986" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] [package.extras] idna2008 = ["idna"] [[package]] name = "rich" -version = "12.6.0" +version = "13.3.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "dev" optional = false -python-versions = ">=3.6.3,<4.0.0" +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.3.1-py3-none-any.whl", hash = "sha256:8aa57747f3fc3e977684f0176a88e789be314a99f99b43b75d1e9cb5dc6db9e9"}, + {file = "rich-13.3.1.tar.gz", hash = "sha256:125d96d20c92b946b983d0d392b84ff945461e5a06d3867e9f9e575f8697b67f"}, +] [package.dependencies] -commonmark = ">=0.9.0,<0.10.0" -pygments = ">=2.6.0,<3.0.0" +markdown-it-py = ">=2.1.0,<3.0.0" +pygments = ">=2.14.0,<3.0.0" typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] -jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] +jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rsa" @@ -885,6 +1510,10 @@ description = "Pure-Python RSA implementation" category = "main" optional = false python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] [package.dependencies] pyasn1 = ">=0.1.3" @@ -896,6 +1525,10 @@ description = "Python bindings to FreeDesktop.org Secret Service API" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] [package.dependencies] cryptography = ">=2.0" @@ -903,14 +1536,18 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "65.6.3" +version = "67.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "setuptools-67.2.0-py3-none-any.whl", hash = "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c"}, + {file = "setuptools-67.2.0.tar.gz", hash = "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48"}, +] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -921,6 +1558,10 @@ description = "Python 2 and 3 compatibility utilities" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] [[package]] name = "snowballstemmer" @@ -929,6 +1570,10 @@ description = "This package provides 29 stemmers for 28 languages generated from category = "main" optional = false python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] [[package]] name = "sphinx" @@ -937,6 +1582,10 @@ description = "Python documentation generator" category = "main" optional = true python-versions = ">=3.6" +files = [ + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, +] [package.dependencies] alabaster = ">=0.7,<0.8" @@ -964,11 +1613,15 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] [[package]] name = "sphinx-autoapi" -version = "2.0.0" +version = "2.0.1" description = "Sphinx API documentation generator" category = "main" optional = true python-versions = ">=3.7" +files = [ + {file = "sphinx-autoapi-2.0.1.tar.gz", hash = "sha256:cdf47968c20852f4feb0ccefd09e414bb820af8af8f82fab15a24b09a3d1baba"}, + {file = "sphinx_autoapi-2.0.1-py2.py3-none-any.whl", hash = "sha256:8ed197a0c9108770aa442a5445744c1405b356ea64df848e8553411b9b9e129b"}, +] [package.dependencies] astroid = ">=2.7" @@ -984,15 +1637,20 @@ go = ["sphinxcontrib-golangdomain"] [[package]] name = "sphinx-rtd-theme" -version = "1.1.1" +version = "1.2.0" description = "Read the Docs theme for Sphinx" category = "main" optional = true -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "sphinx_rtd_theme-1.2.0-py2.py3-none-any.whl", hash = "sha256:f823f7e71890abe0ac6aaa6013361ea2696fc8d3e1fa798f463e82bdb77eeff2"}, + {file = "sphinx_rtd_theme-1.2.0.tar.gz", hash = "sha256:a0d8bd1a2ed52e0b338cbe19c4b2eef3c5e7a048769753dac6a9f059c7b641b8"}, +] [package.dependencies] -docutils = "<0.18" -sphinx = ">=1.6,<6" +docutils = "<0.19" +sphinx = ">=1.6,<7" +sphinxcontrib-jquery = {version = ">=2.0.0,<3.0.0 || >3.0.0", markers = "python_version > \"3\""} [package.extras] dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] @@ -1004,6 +1662,10 @@ description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] @@ -1016,6 +1678,10 @@ description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] @@ -1028,11 +1694,30 @@ description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML h category = "main" optional = true python-versions = ">=3.6" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] +[[package]] +name = "sphinxcontrib-jquery" +version = "2.0.0" +description = "Extension to include jQuery on newer Sphinx releases" +category = "main" +optional = true +python-versions = ">=2.7" +files = [ + {file = "sphinxcontrib-jquery-2.0.0.tar.gz", hash = "sha256:8fb65f6dba84bf7bcd1aea1f02ab3955ac34611d838bcc95d4983b805b234daa"}, + {file = "sphinxcontrib_jquery-2.0.0-py3-none-any.whl", hash = "sha256:ed47fa425c338ffebe3c37e1cdb56e30eb806116b85f01055b158c7057fdb995"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -1040,6 +1725,10 @@ description = "A sphinx extension which renders display math in HTML via JavaScr category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] [package.extras] test = ["flake8", "mypy", "pytest"] @@ -1051,6 +1740,10 @@ description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp d category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] @@ -1063,6 +1756,10 @@ description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] @@ -1070,23 +1767,19 @@ test = ["pytest"] [[package]] name = "termcolor" -version = "2.1.1" +version = "2.2.0" description = "ANSI color formatting for output in terminal" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "termcolor-2.2.0-py3-none-any.whl", hash = "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7"}, + {file = "termcolor-2.2.0.tar.gz", hash = "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a"}, +] [package.extras] tests = ["pytest", "pytest-cov"] -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - [[package]] name = "tomli" version = "2.0.1" @@ -1094,6 +1787,10 @@ description = "A lil' TOML parser" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] [[package]] name = "tomlkit" @@ -1102,14 +1799,22 @@ description = "Style preserving TOML library" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, + {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, +] [[package]] name = "tox" -version = "3.27.1" +version = "3.28.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, + {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, +] [package.dependencies] colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} @@ -1133,6 +1838,10 @@ description = "Collection of utilities for publishing packages on PyPI" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, + {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, +] [package.dependencies] importlib-metadata = ">=3.6" @@ -1152,6 +1861,32 @@ description = "a fork of Python 2 and 3 ast modules with type comment support" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] [[package]] name = "typing-extensions" @@ -1160,6 +1895,10 @@ description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] [[package]] name = "unidecode" @@ -1168,14 +1907,22 @@ description = "ASCII transliterations of Unicode text" category = "main" optional = true python-versions = ">=3.5" +files = [ + {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, + {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, +] [[package]] name = "urllib3" -version = "1.26.13" +version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, +] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] @@ -1184,29 +1931,37 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.17.1" +version = "20.19.0" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.19.0-py3-none-any.whl", hash = "sha256:54eb59e7352b573aa04d53f80fc9736ed0ad5143af445a1e539aada6eb947dd1"}, + {file = "virtualenv-20.19.0.tar.gz", hash = "sha256:37a640ba82ed40b226599c522d411e4be5edb339a0c0de030c0dc7b646d61590"}, +] [package.dependencies] distlib = ">=0.3.6,<1" filelock = ">=3.4.1,<4" importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} -platformdirs = ">=2.4,<3" +platformdirs = ">=2.4,<4" [package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] [[package]] name = "wcwidth" -version = "0.2.5" +version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" category = "dev" optional = false python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] [[package]] name = "webencodings" @@ -1215,6 +1970,10 @@ description = "Character encoding aliases for legacy web content" category = "dev" optional = false python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] [[package]] name = "wheel" @@ -1223,6 +1982,10 @@ description = "A built-package format for Python" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, + {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, +] [package.extras] test = ["pytest (>=3.0.0)", "pytest-cov"] @@ -1234,696 +1997,7 @@ description = "Module for decorators, wrappers and monkey patching." category = "main" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "zipp" -version = "3.11.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[extras] -docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.7" -content-hash = "48a656650bc98b40759fa591d4878a407f5af1fe65d1035ab7bb6430bf9fae29" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -argcomplete = [ - {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, - {file = "argcomplete-2.0.0.tar.gz", hash = "sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20"}, -] -astroid = [ - {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, - {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, -] -attrs = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] -babel = [ - {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, - {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, -] -black = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, -] -bleach = [ - {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, - {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -codespell = [ - {file = "codespell-2.2.2-py3-none-any.whl", hash = "sha256:87dfcd9bdc9b3cb8b067b37f0af22044d7a84e28174adfc8eaa203056b7f9ecc"}, - {file = "codespell-2.2.2.tar.gz", hash = "sha256:c4d00c02b5a2a55661f00d5b4b3b5a710fa803ced9a9d7e45438268b099c319c"}, -] -colorama = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -commitizen = [ - {file = "commitizen-2.38.0-py3-none-any.whl", hash = "sha256:401e1d6907d752dbb00fd5a8b0d0201ff36bc110870168776d49de20bf5b8b61"}, - {file = "commitizen-2.38.0.tar.gz", hash = "sha256:7daa217f703f330c18548304400d133a834840fd01bc79ef2966426c74bdbf1f"}, -] -commonmark = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] -coverage = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, - {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, - {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, -] -cryptography = [ - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, - {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, - {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, - {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, - {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, - {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, - {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, - {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, - {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, - {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, -] -darglint = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] -decli = [ - {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, - {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, -] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] -ecdsa = [ - {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, - {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, -] -exceptiongroup = [ - {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, - {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, -] -filelock = [ - {file = "filelock-3.8.2-py3-none-any.whl", hash = "sha256:8df285554452285f79c035efb0c861eb33a4bcfa5b7a137016e32e6a90f9792c"}, - {file = "filelock-3.8.2.tar.gz", hash = "sha256:7565f628ea56bfcd8e54e42bdc55da899c85c1abfe1b5bcfd147e9188cebb3b2"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -flake8-docstrings = [ - {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, - {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, -] -identify = [ - {file = "identify-2.5.9-py2.py3-none-any.whl", hash = "sha256:a390fb696e164dbddb047a0db26e57972ae52fbd037ae68797e5ae2f4492485d"}, - {file = "identify-2.5.9.tar.gz", hash = "sha256:906036344ca769539610436e40a684e170c3648b552194980bb7b617a8daeb9f"}, -] -idna = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] -imagesize = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, - {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.11.2-py3-none-any.whl", hash = "sha256:e486966fba83f25b8045f8dd7455b0a0d1e4de481e1d7ce4669902d9fb85e622"}, - {file = "isort-5.11.2.tar.gz", hash = "sha256:dd8bbc5c0990f2a095d754e50360915f73b4c26fc82733eb5bfc6b48396af4d2"}, -] -jaraco-classes = [ - {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, - {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, -] -jeepney = [ - {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, - {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -keyring = [ - {file = "keyring-23.11.0-py3-none-any.whl", hash = "sha256:3dd30011d555f1345dec2c262f0153f2f0ca6bca041fb1dc4588349bb4c0ac1e"}, - {file = "keyring-23.11.0.tar.gz", hash = "sha256:ad192263e2cdd5f12875dedc2da13534359a7e760e77f8d04b50968a821c2361"}, -] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"}, - {file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"}, - {file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"}, - {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"}, - {file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"}, - {file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"}, - {file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"}, - {file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"}, - {file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"}, -] -m2r2 = [ - {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, - {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mistune = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] -mock = [ - {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, - {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, -] -more-itertools = [ - {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"}, - {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"}, - {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"}, -] -pathspec = [ - {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, - {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, -] -pkginfo = [ - {file = "pkginfo-1.9.2-py3-none-any.whl", hash = "sha256:d580059503f2f4549ad6e4c106d7437356dbd430e2c7df99ee1efe03d75f691e"}, - {file = "pkginfo-1.9.2.tar.gz", hash = "sha256:ac03e37e4d601aaee40f8087f63fc4a2a6c9814dda2c8fa6aab1b1829653bdfa"}, -] -platformdirs = [ - {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, - {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pre-commit = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, -] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, - {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, -] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pygments = [ - {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, - {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, -] -pytest = [ - {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, - {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -python-jose = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, -] -pytz = [ - {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, - {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, -] -pywin32-ctypes = [ - {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, - {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -questionary = [ - {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, - {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, -] -readme-renderer = [ - {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, - {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, -] -readthedocs-sphinx-ext = [ - {file = "readthedocs-sphinx-ext-2.2.0.tar.gz", hash = "sha256:e5effcd825816111a377ab7a897b819215138f8e5e8acc86f99218328f957240"}, - {file = "readthedocs_sphinx_ext-2.2.0-py2.py3-none-any.whl", hash = "sha256:d801f0bfb125d2837f18f40451462528d4a97eefd8de8a12ad526b4f1ce14205"}, -] -recommonmark = [ - {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, - {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, -] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] -requests-toolbelt = [ - {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, - {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, -] -rfc3986 = [ - {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, - {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, -] -rich = [ - {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"}, - {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"}, -] -rsa = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, -] -secretstorage = [ - {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, - {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, -] -setuptools = [ - {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, - {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -sphinx = [ - {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, - {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, -] -sphinx-autoapi = [ - {file = "sphinx-autoapi-2.0.0.tar.gz", hash = "sha256:97dcf1b5b54cd0d8efef867594e4a4f3e2d3a2c0ec1e5a891e0a61bc77046006"}, - {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, -] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.1.1-py2.py3-none-any.whl", hash = "sha256:31faa07d3e97c8955637fc3f1423a5ab2c44b74b8cc558a51498c202ce5cbda7"}, - {file = "sphinx_rtd_theme-1.1.1.tar.gz", hash = "sha256:6146c845f1e1947b3c3dd4432c28998a1693ccc742b4f9ad7c63129f0757c103"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -termcolor = [ - {file = "termcolor-2.1.1-py3-none-any.whl", hash = "sha256:fa852e957f97252205e105dd55bbc23b419a70fec0085708fc0515e399f304fd"}, - {file = "termcolor-2.1.1.tar.gz", hash = "sha256:67cee2009adc6449c650f6bcf3bdeed00c8ba53a8cda5362733c53e0a39fb70b"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -tomlkit = [ - {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, - {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, -] -tox = [ - {file = "tox-3.27.1-py2.py3-none-any.whl", hash = "sha256:f52ca66eae115fcfef0e77ef81fd107133d295c97c52df337adedb8dfac6ab84"}, - {file = "tox-3.27.1.tar.gz", hash = "sha256:b2a920e35a668cc06942ffd1cf3a4fb221a4d909ca72191fb6d84b0b18a7be04"}, -] -twine = [ - {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, - {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, -] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] -typing-extensions = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, -] -unidecode = [ - {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, - {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, -] -urllib3 = [ - {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, - {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, -] -virtualenv = [ - {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, - {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -webencodings = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] -wheel = [ - {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, - {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, -] -wrapt = [ +files = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, @@ -1989,7 +2063,27 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [ - {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, - {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, + +[[package]] +name = "zipp" +version = "3.13.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"}, + {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"}, ] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[extras] +docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "5956d67888e27fa37f4283a03ab3564658926480b09ee5b9496e3d603a5ecb2a" diff --git a/pyproject.toml b/pyproject.toml index 14e0285..89e8450 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,9 @@ Documentation = "https://python-keycloak.readthedocs.io/en/latest/" [tool.poetry.dependencies] python = "^3.7" -requests = "^2.20.0" +requests = "^2.28.2" python-jose = "^3.3.0" -urllib3 = "^1.26.0" +urllib3 = "^1.26.14" mock = {version = "^4.0.3", optional = true} alabaster = {version = "^0.7.12", optional = true} commonmark = {version = "^0.9.1", optional = true} @@ -42,7 +42,7 @@ sphinx-rtd-theme = {version = "^1.0.0", optional = true} readthedocs-sphinx-ext = {version = "^2.1.9", optional = true} m2r2 = {version = "^0.3.2", optional = true} sphinx-autoapi = {version = "^2.0.0", optional = true} -requests-toolbelt = "^0.9.1" +requests-toolbelt = "^0.10.1" [tool.poetry.extras] docs = [ diff --git a/tox.ini b/tox.ini index 174d074..2f15705 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,13 @@ [tox] -requires = - tox-poetry - poetry - tox<4.0.0 +isolated_build = true +skipsdist = true envlist = check, apply-check, docs, tests, build, changelog [testenv] whitelist_externals = bash +commands_pre = + poetry install --no-root --sync [testenv:check] commands = @@ -23,7 +23,8 @@ commands = isort src/keycloak tests docs [testenv:docs] -extras = docs +commands_pre = + poetry install --no-root --sync -E docs commands = sphinx-build -T -E -W -b html -d _build/doctrees -D language=en ./docs/source _build/html @@ -34,10 +35,6 @@ commands = ./test_keycloak_init.sh "pytest -vv --cov=keycloak --cov-report term-missing {posargs}" [testenv:build] -deps = - poetry -setenv = - POETRY_VIRTUALENVS_CREATE = false commands = poetry build --format sdist poetry build --format wheel From 9764ec6bd3895344e1f9772a1a3883efd9ea4151 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 10 Feb 2023 08:57:59 +0000 Subject: [PATCH 165/222] chore: upgrade wheel --- poetry.lock | 12 ++++++------ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 94c356d..077e8cd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1977,18 +1977,18 @@ files = [ [[package]] name = "wheel" -version = "0.37.1" +version = "0.38.4" description = "A built-package format for Python" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.7" files = [ - {file = "wheel-0.37.1-py2.py3-none-any.whl", hash = "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a"}, - {file = "wheel-0.37.1.tar.gz", hash = "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"}, + {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"}, + {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"}, ] [package.extras] -test = ["pytest (>=3.0.0)", "pytest-cov"] +test = ["pytest (>=3.0.0)"] [[package]] name = "wrapt" @@ -2086,4 +2086,4 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "5956d67888e27fa37f4283a03ab3564658926480b09ee5b9496e3d603a5ecb2a" +content-hash = "8d76b155adddd2eacd0304397b33465d5c67f09165d8de641c71f5ce7b979be2" diff --git a/pyproject.toml b/pyproject.toml index 89e8450..d855cbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ docs = [ tox = "^3.25.0" pytest = "^7.1.2" pytest-cov = "^3.0.0" -wheel = "^0.37.1" +wheel = "^0.38.4" pre-commit = "^2.19.0" isort = "^5.10.1" black = "^22.3.0" From d7bcca10db99bc902e6d9a92af2822e5259f9665 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Fri, 10 Feb 2023 08:58:55 +0000 Subject: [PATCH 166/222] ci: dont skip --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 2f15705..82df028 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,5 @@ [tox] isolated_build = true -skipsdist = true envlist = check, apply-check, docs, tests, build, changelog [testenv] From 4f1dea8d2c6ae9cac2a68858e25aa3d800e79a22 Mon Sep 17 00:00:00 2001 From: Richard Nemeth Date: Sun, 5 Mar 2023 13:30:04 +0100 Subject: [PATCH 167/222] fix: tests and upgraded deps (#419) * fix: tests and upgraded deps * test: use absolute path --- .pre-commit-config.yaml | 5 +- poetry.lock | 386 +++++++++++++------------- test_keycloak_init.sh | 3 +- tests/providers/asm-7.3.1.jar | Bin 0 -> 121836 bytes tests/providers/asm-commons-7.3.1.jar | Bin 0 -> 71548 bytes tests/providers/asm-tree-7.3.1.jar | Bin 0 -> 52826 bytes tests/providers/asm-util-7.3.1.jar | Bin 0 -> 84817 bytes tests/providers/nashorn-core-15.4.jar | Bin 0 -> 2167292 bytes 8 files changed, 202 insertions(+), 192 deletions(-) create mode 100644 tests/providers/asm-7.3.1.jar create mode 100644 tests/providers/asm-commons-7.3.1.jar create mode 100644 tests/providers/asm-tree-7.3.1.jar create mode 100644 tests/providers/asm-util-7.3.1.jar create mode 100644 tests/providers/nashorn-core-15.4.jar diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 806a12c..7e03ac0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,9 +8,10 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files + args: ["--maxkb=10000"] - repo: https://github.com/compilerla/conventional-pre-commit rev: v1.2.0 hooks: - id: conventional-pre-commit - stages: [ commit-msg ] - args: [ ] # optional: list of Conventional Commits types to allow + stages: [commit-msg] + args: [] # optional: list of Conventional Commits types to allow diff --git a/poetry.lock b/poetry.lock index 077e8cd..83555c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. [[package]] name = "alabaster" @@ -14,21 +14,22 @@ files = [ [[package]] name = "argcomplete" -version = "2.0.0" +version = "2.0.5" description = "Bash tab completion for argparse" category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, - {file = "argcomplete-2.0.0.tar.gz", hash = "sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20"}, + {file = "argcomplete-2.0.5-py3-none-any.whl", hash = "sha256:e2a2cdb8ee9634ff2fa368ba6b996b46205341e0cf106be8075fd9bdbc5cf2e7"}, + {file = "argcomplete-2.0.5.tar.gz", hash = "sha256:1cfd12928d62e41901783e4dc7d7ca03eccd589840face4c020693b13f754312"}, ] [package.dependencies] -importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} +importlib-metadata = {version = ">=0.23,<6", markers = "python_version == \"3.7\""} [package.extras] -test = ["coverage", "flake8", "pexpect", "wheel"] +lint = ["flake8", "mypy"] +test = ["coverage", "flake8", "mypy", "pexpect", "wheel"] [[package]] name = "astroid" @@ -70,18 +71,18 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy [[package]] name = "babel" -version = "2.11.0" +version = "2.12.1" description = "Internationalization utilities" category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, - {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, ] [package.dependencies] -pytz = ">=2015.7" +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [[package]] name = "black" @@ -302,14 +303,14 @@ files = [ [[package]] name = "commitizen" -version = "2.41.0" +version = "2.42.1" description = "Python commitizen client tool" category = "dev" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ - {file = "commitizen-2.41.0-py3-none-any.whl", hash = "sha256:2044f6d1cf002f363280e0dcefc8b557095dc42530060bfe5ad917ea4d5c48a1"}, - {file = "commitizen-2.41.0.tar.gz", hash = "sha256:5d50093fe546cc3b5217780a8cdfc5a9de6a27ce8aa7db6443e2e47dc2b10b6a"}, + {file = "commitizen-2.42.1-py3-none-any.whl", hash = "sha256:fad7d37cfae361a859b713d4ac591859d5ca03137dd52de4e1bd208f7f45d5dc"}, + {file = "commitizen-2.42.1.tar.gz", hash = "sha256:eac18c7c65587061aac6829534907aeb208405b8230bfd35ec08503c228a7f17"}, ] [package.dependencies] @@ -342,63 +343,63 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "7.1.0" +version = "7.2.1" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"}, - {file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"}, - {file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"}, - {file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"}, - {file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"}, - {file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"}, - {file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"}, - {file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"}, - {file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"}, - {file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"}, - {file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"}, - {file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"}, - {file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"}, - {file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"}, - {file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"}, - {file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"}, - {file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"}, - {file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"}, - {file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"}, - {file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"}, - {file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"}, - {file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"}, - {file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"}, - {file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"}, - {file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"}, - {file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"}, - {file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"}, - {file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"}, - {file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"}, - {file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"}, - {file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"}, + {file = "coverage-7.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49567ec91fc5e0b15356da07a2feabb421d62f52a9fff4b1ec40e9e19772f5f8"}, + {file = "coverage-7.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03"}, + {file = "coverage-7.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3004765bca3acd9e015794e5c2f0c9a05587f5e698127ff95e9cfba0d3f29339"}, + {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cca7c0b7f5881dfe0291ef09ba7bb1582cb92ab0aeffd8afb00c700bf692415a"}, + {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2167d116309f564af56f9aa5e75ef710ef871c5f9b313a83050035097b56820"}, + {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cb5f152fb14857cbe7f3e8c9a5d98979c4c66319a33cad6e617f0067c9accdc4"}, + {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:87dc37f16fb5e3a28429e094145bf7c1753e32bb50f662722e378c5851f7fdc6"}, + {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e191a63a05851f8bce77bc875e75457f9b01d42843f8bd7feed2fc26bbe60833"}, + {file = "coverage-7.2.1-cp310-cp310-win32.whl", hash = "sha256:e3ea04b23b114572b98a88c85379e9e9ae031272ba1fb9b532aa934c621626d4"}, + {file = "coverage-7.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:0cf557827be7eca1c38a2480484d706693e7bb1929e129785fe59ec155a59de6"}, + {file = "coverage-7.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:570c21a29493b350f591a4b04c158ce1601e8d18bdcd21db136fbb135d75efa6"}, + {file = "coverage-7.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e872b082b32065ac2834149dc0adc2a2e6d8203080501e1e3c3c77851b466f9"}, + {file = "coverage-7.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac6343bae03b176e9b58104a9810df3cdccd5cfed19f99adfa807ffbf43cf9b"}, + {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abacd0a738e71b20e224861bc87e819ef46fedba2fb01bc1af83dfd122e9c319"}, + {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9256d4c60c4bbfec92721b51579c50f9e5062c21c12bec56b55292464873508"}, + {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80559eaf6c15ce3da10edb7977a1548b393db36cbc6cf417633eca05d84dd1ed"}, + {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bd7e628f6c3ec4e7d2d24ec0e50aae4e5ae95ea644e849d92ae4805650b4c4e"}, + {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09643fb0df8e29f7417adc3f40aaf379d071ee8f0350ab290517c7004f05360b"}, + {file = "coverage-7.2.1-cp311-cp311-win32.whl", hash = "sha256:1b7fb13850ecb29b62a447ac3516c777b0e7a09ecb0f4bb6718a8654c87dfc80"}, + {file = "coverage-7.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:617a94ada56bbfe547aa8d1b1a2b8299e2ec1ba14aac1d4b26a9f7d6158e1273"}, + {file = "coverage-7.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8649371570551d2fd7dee22cfbf0b61f1747cdfb2b7587bb551e4beaaa44cb97"}, + {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2b9b5e70a21474c105a133ba227c61bc95f2ac3b66861143ce39a5ea4b3f84"}, + {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82c988954722fa07ec5045c57b6d55bc1a0890defb57cf4a712ced65b26ddd"}, + {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:861cc85dfbf55a7a768443d90a07e0ac5207704a9f97a8eb753292a7fcbdfcfc"}, + {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0339dc3237c0d31c3b574f19c57985fcbe494280153bbcad33f2cdf469f4ac3e"}, + {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5928b85416a388dd557ddc006425b0c37e8468bd1c3dc118c1a3de42f59e2a54"}, + {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d3843ca645f62c426c3d272902b9de90558e9886f15ddf5efe757b12dd376f5"}, + {file = "coverage-7.2.1-cp37-cp37m-win32.whl", hash = "sha256:6a034480e9ebd4e83d1aa0453fd78986414b5d237aea89a8fdc35d330aa13bae"}, + {file = "coverage-7.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6fce673f79a0e017a4dc35e18dc7bb90bf6d307c67a11ad5e61ca8d42b87cbff"}, + {file = "coverage-7.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f099da6958ddfa2ed84bddea7515cb248583292e16bb9231d151cd528eab657"}, + {file = "coverage-7.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97a3189e019d27e914ecf5c5247ea9f13261d22c3bb0cfcfd2a9b179bb36f8b1"}, + {file = "coverage-7.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a81dbcf6c6c877986083d00b834ac1e84b375220207a059ad45d12f6e518a4e3"}, + {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d2c3dde4c0b9be4b02067185136b7ee4681978228ad5ec1278fa74f5ca3e99"}, + {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a209d512d157379cc9ab697cbdbb4cfd18daa3e7eebaa84c3d20b6af0037384"}, + {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f3d07edb912a978915576a776756069dede66d012baa503022d3a0adba1b6afa"}, + {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8dca3c1706670297851bca1acff9618455122246bdae623be31eca744ade05ec"}, + {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b1991a6d64231a3e5bbe3099fb0dd7c9aeaa4275ad0e0aeff4cb9ef885c62ba2"}, + {file = "coverage-7.2.1-cp38-cp38-win32.whl", hash = "sha256:22c308bc508372576ffa3d2dbc4824bb70d28eeb4fcd79d4d1aed663a06630d0"}, + {file = "coverage-7.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0c0d46de5dd97f6c2d1b560bf0fcf0215658097b604f1840365296302a9d1fb"}, + {file = "coverage-7.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4dd34a935de268a133e4741827ae951283a28c0125ddcdbcbba41c4b98f2dfef"}, + {file = "coverage-7.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f8318ed0f3c376cfad8d3520f496946977abde080439d6689d7799791457454"}, + {file = "coverage-7.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:834c2172edff5a08d78e2f53cf5e7164aacabeb66b369f76e7bb367ca4e2d993"}, + {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4d70c853f0546855f027890b77854508bdb4d6a81242a9d804482e667fff6e6"}, + {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a6450da4c7afc4534305b2b7d8650131e130610cea448ff240b6ab73d7eab63"}, + {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99f4dd81b2bb8fc67c3da68b1f5ee1650aca06faa585cbc6818dbf67893c6d58"}, + {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bdd3f2f285ddcf2e75174248b2406189261a79e7fedee2ceeadc76219b6faa0e"}, + {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f29351393eb05e6326f044a7b45ed8e38cb4dcc38570d12791f271399dc41431"}, + {file = "coverage-7.2.1-cp39-cp39-win32.whl", hash = "sha256:e2b50ebc2b6121edf352336d503357321b9d8738bb7a72d06fc56153fd3f4cd8"}, + {file = "coverage-7.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a"}, + {file = "coverage-7.2.1-pp37.pp38.pp39-none-any.whl", hash = "sha256:436313d129db7cf5b4ac355dd2bd3f7c7e5294af077b090b85de75f8458b8616"}, + {file = "coverage-7.2.1.tar.gz", hash = "sha256:c77f2a9093ccf329dd523a9b2b3c854c20d2a3d968b6def3b820272ca6732242"}, ] [package.dependencies] @@ -443,10 +444,10 @@ files = [ cffi = ">=1.12" [package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools_rust (>=0.11.4)"] +sdist = ["setuptools-rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] @@ -584,14 +585,14 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.17" +version = "2.5.18" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.17-py2.py3-none-any.whl", hash = "sha256:7d526dd1283555aafcc91539acc061d8f6f59adb0a7bba462735b0a318bff7ed"}, - {file = "identify-2.5.17.tar.gz", hash = "sha256:93cc61a861052de9d4c541a7acb7e3dcc9c11b398a2144f6e52ae5285f5f4f06"}, + {file = "identify-2.5.18-py2.py3-none-any.whl", hash = "sha256:93aac7ecf2f6abf879b8f29a8002d3c6de7086b8c28d88e1ad15045a15ab63f9"}, + {file = "identify-2.5.18.tar.gz", hash = "sha256:89e144fa560cc4cffb6ef2ab5e9fb18ed9f9b3cb054384bab4b95c12f6c309fe"}, ] [package.extras] @@ -644,14 +645,14 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag [[package]] name = "importlib-resources" -version = "5.10.2" +version = "5.12.0" description = "Read resources from Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"}, - {file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"}, + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, ] [package.dependencies] @@ -833,14 +834,14 @@ mistune = "0.8.4" [[package]] name = "markdown-it-py" -version = "2.1.0" +version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"}, - {file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"}, + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, ] [package.dependencies] @@ -848,10 +849,10 @@ mdurl = ">=0.1,<1.0" typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"] -code-style = ["pre-commit (==2.6)"] -compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] @@ -972,14 +973,14 @@ test = ["pytest (<5.4)", "pytest-cov"] [[package]] name = "more-itertools" -version = "9.0.0" +version = "9.1.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"}, - {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"}, + {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, + {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, ] [[package]] @@ -1050,14 +1051,14 @@ testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" -version = "3.0.0" +version = "3.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"}, - {file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"}, + {file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"}, + {file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"}, ] [package.dependencies] @@ -1108,14 +1109,14 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.36" +version = "3.0.38" description = "Library for building powerful interactive command lines in Python" category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, - {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, + {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, + {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, ] [package.dependencies] @@ -1141,18 +1142,7 @@ category = "main" optional = false python-versions = "*" files = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] @@ -1228,14 +1218,14 @@ plugins = ["importlib-metadata"] [[package]] name = "pytest" -version = "7.2.1" +version = "7.2.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, - {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, + {file = "pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"}, + {file = "pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"}, ] [package.dependencies] @@ -1331,6 +1321,13 @@ files = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -1485,19 +1482,19 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.3.1" +version = "13.3.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "dev" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.3.1-py3-none-any.whl", hash = "sha256:8aa57747f3fc3e977684f0176a88e789be314a99f99b43b75d1e9cb5dc6db9e9"}, - {file = "rich-13.3.1.tar.gz", hash = "sha256:125d96d20c92b946b983d0d392b84ff945461e5a06d3867e9f9e575f8697b67f"}, + {file = "rich-13.3.2-py3-none-any.whl", hash = "sha256:a104f37270bf677148d8acb07d33be1569eeee87e2d1beb286a4e9113caf6f2f"}, + {file = "rich-13.3.2.tar.gz", hash = "sha256:91954fe80cfb7985727a467ca98a7618e5dd15178cc2da10f553b36a93859001"}, ] [package.dependencies] -markdown-it-py = ">=2.1.0,<3.0.0" -pygments = ">=2.14.0,<3.0.0" +markdown-it-py = ">=2.2.0,<3.0.0" +pygments = ">=2.13.0,<3.0.0" typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] @@ -1536,14 +1533,14 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "67.2.0" +version = "67.4.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.2.0-py3-none-any.whl", hash = "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c"}, - {file = "setuptools-67.2.0.tar.gz", hash = "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48"}, + {file = "setuptools-67.4.0-py3-none-any.whl", hash = "sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251"}, + {file = "setuptools-67.4.0.tar.gz", hash = "sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330"}, ] [package.extras] @@ -1890,14 +1887,14 @@ files = [ [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] [[package]] @@ -1931,14 +1928,14 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.19.0" +version = "20.20.0" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.19.0-py3-none-any.whl", hash = "sha256:54eb59e7352b573aa04d53f80fc9736ed0ad5143af445a1e539aada6eb947dd1"}, - {file = "virtualenv-20.19.0.tar.gz", hash = "sha256:37a640ba82ed40b226599c522d411e4be5edb339a0c0de030c0dc7b646d61590"}, + {file = "virtualenv-20.20.0-py3-none-any.whl", hash = "sha256:3c22fa5a7c7aa106ced59934d2c20a2ecb7f49b4130b8bf444178a16b880fa45"}, + {file = "virtualenv-20.20.0.tar.gz", hash = "sha256:a8a4b8ca1e28f864b7514a253f98c1d62b64e31e77325ba279248c65fb4fcef4"}, ] [package.dependencies] @@ -1992,96 +1989,107 @@ test = ["pytest (>=3.0.0)"] [[package]] name = "wrapt" -version = "1.14.1" +version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." category = "main" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ - {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, - {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, - {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, - {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, - {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, - {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, - {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, - {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, - {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, - {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, - {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, - {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, - {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, - {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, - {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, - {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] [[package]] name = "zipp" -version = "3.13.0" +version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"}, - {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"}, + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [extras] -docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd-theme", "readthedocs-sphinx-ext", "m2r2", "sphinx-autoapi"] +docs = ["Sphinx", "alabaster", "commonmark", "m2r2", "mock", "readthedocs-sphinx-ext", "recommonmark", "sphinx-autoapi", "sphinx-rtd-theme"] [metadata] lock-version = "2.0" diff --git a/test_keycloak_init.sh b/test_keycloak_init.sh index 07df97d..7f4abc5 100755 --- a/test_keycloak_init.sh +++ b/test_keycloak_init.sh @@ -13,7 +13,8 @@ function keycloak_stop() { function keycloak_start() { echo "Starting keycloak docker container" - docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -e KC_FEATURES="token-exchange,admin-fine-grained-authz" -p "${KEYCLOAK_PORT}:8080" "${KEYCLOAK_DOCKER_IMAGE}" start-dev + PWD=$(pwd) + docker run -d --name unittest_keycloak -e KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN}" -e KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD}" -e KC_FEATURES="token-exchange,admin-fine-grained-authz" -p "${KEYCLOAK_PORT}:8080" -v $PWD/tests/providers:/opt/keycloak/providers "${KEYCLOAK_DOCKER_IMAGE}" start-dev SECONDS=0 until curl --silent --output /dev/null localhost:$KEYCLOAK_PORT; do sleep 5; diff --git a/tests/providers/asm-7.3.1.jar b/tests/providers/asm-7.3.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..8a502662ad9b513c68ca792e9f9eb58d38b47c0f GIT binary patch literal 121836 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

a9^RCI<oKuM?M>wTU^3VmMj>Vo|PLwN{ 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ͨ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 literal 0 HcmV?d00001 diff --git a/tests/providers/asm-tree-7.3.1.jar b/tests/providers/asm-tree-7.3.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..28858f4e2ec280215966c42db1f2c75d400f1f84 GIT binary patch literal 52826 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%0A-El^zIWkGW#c;VcGm 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 z

Wa>O}1x^SEtwT!bUXfM-lh?7YIJMA2-t?X%Dlktsp(I$2uaaj5t5xO`Q1Vg6ua zkYaC|7*F<@7RpW!`$N&54T3qFpfd!@xSP8W-S24$%hYouT2a$Dz|ts&&=*YFL>kue zEp2o{q5rFFoU?^_f~KlzV21J)^>)30I>YNUiwiVA<#H15y3 zy1Y?6!uSVja6WK;)MRAk*W^a0wFGUncIL{;vgo`tTBMk2Pbg$kHe7;Pv1{Qy+TEHh za7#1KN;wiWZCp8oZ~Bboh3fY(RW& zbVg(Z{1&O;)CY>5md+C-26GKThu%y)iG-lcO(9p1HUiv|F%T0zOZJ?IUOC@sAY^EC zVE-vhteEyF*Fh`ITfiOt#plZU2rGdCVR~7Zk`RYD%SGgEtpxyrj6eOu(~>N8^z6*P z3)F|I6yQFBF(G{0V4~6}BNP32lA3Z-`nDFMsi{}&t?!=1mknOw=ML-X-H^3jqx=VX zhG!e4S5LYfw4n``LwX~tB>1s0#}B6r0eLOwg*XUAH)6Yv56sYGxW3D(Hcl=9*Pufr zNN?tf&MGUyl6I=2JCVSNvx_lxd*CJnr;3Q%x_jl6KT}s`k4vO!v z{7+5xD1955hIhLxWQU`1+SwRMS%n_KEna-)YLVSee0A;JuX+a|=4qob$dIUAxfP)V z<#7Tf7A9I8b;bK$K$y_v+AMv2zL?ekZJE@_seqRJaN;ASJj-rja%Ms%W}@R+1XhLI zhHhUb5X(qv01kc1eC3S^sqo)*OxfJR21fkjDx->zDNY+dv40y&IIf1eg0V56$H|9B z@}V{+kIDLA*1zko7~h2T(d%Rde$EFnT)9_b(ax}^W^Zf${ep(0#Q2Hf?pLej&Bt_f z+9AL{!{3V_?lB*rLD77bWL-q7z;Lm9aN@|mL%&z!?{f&?9Rq!O7JXgaW$e6zHx}00 zI<^4&1fDG?BDIOTs`NeSS`NMxqKXONnhRk7+<=-D3&tK&dplQULM^H1djZMBD2!vY z=}G$gM=ot>huOd5yt2blIXEZm7Xzi;ezYF4ify0@{!bOF@O}dQe-g>+re@*_G(1^O zEQ8u=l56LLHTBnxiI*Z=@gu^7Z*6FEf;p_vFid|WClwYJIBCI9xWqRIG&~J$-kN=P z1|fSIgArVcHzcrDlv^k=dh3$tR4$ol;fPDe;}d#I`1cOA9=IACd(nrT=8W}ha(xqR zz3;n6TI6g%J`koz%9|}LXma$@d!M2Xm?2EXa*@Ck8dFmz*NiS917V-H!W6_L%dM@g zZR~6+Y+JcnN`uk>@(|{5xTcI~7D+yi-XcxIk9!f}VJpwsWIJ`+Io1iDDwuPA0^t^e z`1c*LIqRr_tDbYg$NgyCd9!T+m}BJDMha0;TQBRGa5-sJIEts6wB&uIJf{#BeO6zbOsRr$IM1W)Yina-A(7T1dfZLma zZgck)Lf-!9JX^1#WBvWZK?cHjBbwwOZf$r3XL84yzl~6O?V{h9f>EPAE8tQ1R{-6ZpsdQePNy z*btSA8&~9F-jI?5Jmpd+XGmNf$c3OiG;Tgpkr6G$H0V^024LuMGFY0UnHpyhf(X|w zNDw7&J(jYrsrb5!o-r0?nekKY{H^!p17 z%Go%}*gSIM48p;aoY~%bfuF{E-#tUu-vz!E;;E`ihSx*?ol8BBvA!8&`a}f+z`wH+ zlFC08{$z&^i-mell8xxF7bZ4}bqovmbrG<&aXIXO5Um>_DofH}&`&W4UE;5JZ$~{8 z`mmiAnWYi+c@3kt;+~_#b%C9P@%bbc z`a4cBtaJxn7bjR)Hl3>j0^sg{NzWy!t7mKb?dOhwB(PtvmrW^@yFg^vTcgtN8zBdIaQEnzgawZ9@Nn%6J|cDh_j`E0NG)s? z0`Di5b2BHJz&G`nJ`}}+3$1h$=G*(Jdg2#2Kn+Dnt15T(Vq}5AP=qD` z!%tj%8r&X`O%neUHR`liEk9!;Vu&4hHiH;_HY(H6HU-G&(d@$H9hya#McN%CP(H1k)umX&6Li+6A$7gNZ1Ses4t-{c*RwJ+iFYs* z_XlXxGrB>_6PbE5{RYJOsADfv-=tm`?M0S+%?%D)4NwSB8MVcOyOC9{qeC(KrdeuQf3*%C% zEN)+j)vAf!2Dn-zy%?p5Idrr9&a2@1YjbLST>Wt=4P*K+H@hDy=yVM{yPSfvr26!f zfkA3%HHU&>a~t6v+qiI@0*zs4KQD=)(FN`UQNmIAe#@H8h@G0xDormPB7Bq!wYOxb zNVVDAxyZhVK55mlbiSx1w7g$4Xl1)1+By;$=@CuEXjcx7Of=uS{6!6qa)Qdg-}S{m zr{nk|X};j&B>kh$vR|yPUC(DHPM-R zI;VDh(rqjM@cVHrC<{89?gB2nzR=4xd5dW2wU1B{`$IS2SuXVgs`dE55O49wPBG2W zK86UmY`d)Vkz27Me&YUcZrHtxg~IwAIBCcjY5Dak@I{J_+Edd`(GHoD@EX{o(@0~U zq`VC+x+k|V&=nilK1W$B+vWP8Qr~KmrpWGV^t8h0ynqYvJs)+(w~z72J$ zpa07Hy-Oqa6p2`~K$iEmMkDtUsZg^ZByiuMmU)2sBW_5ScY$g!AdwbFc#67P4npxR zj~?(1sRs0trwTmw1A5$nO;c{Gj`;njmTmCD@4o6y&N&#++2&QIV&4Otinym+{}J1A zy1OHD#)nnYbG=vf*sf1LU@;!%mbJv$-ons=V00idgXC%=TYIUe{G$j3hJric^_#D+ zR^ZoQJu_=WIUzh5qGu%bT6sYY<^?m_<iz& zi(kkV!fO`P*;XrDF)FDr#cOI6NN~<-G{RXx{L%eT8muQmJs2Wg<+kVa&0A|i;X1?8 zT3b@=1LG!WnbG}Y!^8kEB661QC~R@^qa=g(MO3t4)dJ0^+m>i9QPr&6h9vcHlOEve zPZ1-zhCTY~%I2^Oc3OBUr&U=1PQ^G%?r&_?VIV6a7sTNqLhk9V91M$*nCpDEB;QB@ zKkVm;WIJxFw)qh~S2n73G=E#joOo&!Uwl{Rt+n=Qgb!Gp1BJmj1fENT4_?S9(L5(n zNIfKfrB8Bv*I~9gJVO7J<%)^b_KDTbiPf%&AIB62*Axfm6bGji2bUCH-Q%3vhkvFw z&^W!xzeh4o`@VjCA`!TIOCxwjBX~t4ct9g~LL<1JoQI76)cpzYM`oq#4b_+5k#g7( zx^>x*t6B7s>J2aOc%pna{c(Soly-*nEfyd3C(&7i82-z7n?I{ZpMn3BQk7{MvOFE{ zm3V4IBm1!B0<8|P{S@bj8t*~zF1UxmA;(4a*8A1_cH5d{b$Cbik_p77~rBSMx-cpC^26<&}|J@OrNG5tUnHaPG+wqHk#RM3N=T*wFFB zLR+k{#^RMyQ@pB0+B2}Sn01Bb6`dpR&;)y~=Z@YpYhR403F$lLz9?};{E^NtmsgK@ zuJXd+J0eg#yRPvB{x|e{8vY~kwWRJ1>Lb%4pD(cYxar(GS3>XsboB4_wjBc14!G0{8#fIJ*d9&nAN+L}4@UqoA81=&*wn`!E^;c$p*cUkhQy(& z;v5&-FkFxfcexPcRh&eWW5t5hV3hB|b%qDbBM~$MYN%NB&#YKvO1N#|@Fx~@=eyPl zy3*lSAL!K+Pb%#~5*d@aP^V=Wpv@9@nxBO5>DbF{(U-JRC#`!Boa=;#RIT820nOUB zzHAfdiY-wSR^!+ik@Zy#`)t}W+HYjM*7@JQ#Pd> zbz0j>EEWb-T0qhmL5X~d&P-I?V97@k1!OuZENRD<(mSi)lt|K2H#=CJYI1A`hj_0` z_~)og;7bKWJy^wQ0Z+>D`VgCp;n$2|*NlPJj3s=cO(?C|)@v|nLfp(DAhB`K_3vKr zU_>aB`PJoaENuUo zxYLNaBl)yipJo|~aAi<@r4>|FffC)XL!=$b2JOsg^?5#ZiBMa1ya$|0!{?xF(*|?$W-W}t*tu5f1}3#e{80`Ibo<3Xk0SSZiXja+bgctg;%8X>v(EAZ+P^Lb zunN1KNTS3VGSGKT9E zIhEjW;nWJV%>jgqAm8Mb0f(9+u+rMTT{fPv>DId0R&naIROQwy<+-rCgQl1U) zdcF_No-rlr`nbHlhUYICub>od7cLok)Oo@|LM-i%aim(E9QexCV-??2uP+Bnov=aw3;hsWAViZs7d=k zw@@XJo=EUc^Y&1vN;%_))XsyfVGLNoM847&R{ml+^97fBu5#q7Q^NM%PB}~AZBA># zz(1O}jzN)DC-KFzF|*6e8?J|c$Bs^EGxEZfTH&pgGR~1QZV52W;+LZ1 zJCp^=N;;XH-cFJ<)g8kBQg@^;(jmjO}*w{)X9A`pFqN!cnU9F*RM@6kOD9Nox zZMCuoXK5K?i-T914;YZ<2f6T6sj!gb9I_Zc)Ur>E^QH>)wgP$$eA7>!F|ovmE746x z-0!IXycVQ%*O?SU&QXHnea{iT9{eb^AR4DZ(IPNku44>Iq|Z zZ{$iuh|mGZClOd^f!95fG)zb`U{uzMPVQfyVR(1yk*ST4f9>#YNH{B?JgzV_fm@RV z#XUD91+_I@kj)iydDo=bJ3Mm~*g5v6!E^tQsREr=Hj6rBNDwdAf6f8{l1OK-vqOjx z=Re0m4IJC2U$78nF>j)47YabOFr%MJ>Y=0dLH!AUqm!5_Ulf`PMz zVJcHF2I(3eeJW!4G2;)&ln16QD0Kr`ADmZ(xt0^gE3_%#wNadjbx+!ZQZ~>5(XB`a z5*{Y&7ZYKOkUg~dgM*fIf?LOIBi!8E$1@Exm>Z+5ZmE8sKsG1ADLwk@>v)iE^){VI zt!EwZm7pT}kewQX5UX>3-MNXLpzwz=u0RfjY&&p}E@Ver!qJ|z;J8^W-0FewNOU4*7X=j(*6JcGsrQk5FUxXj}FcNuhefZw@iWzSN}; z+Kkq>ZxX#bNna_~RlGW7TDBfZF5a;>krq7~F}F5}Y!I87&uU1KSVaj8@4AGh_?d#d zt+;mOus;QGW^cr)Z;rv!wHzp)ZNP`R2Fh%aV!6EhC)n8JICJkt*)PxM}LXZDPrI$ln0=uSX6&y6b1 z_ihGFXX{p$+!Ora{rUp6d|{hD5X_(Xr!FAVm*X@W1&PzXusg2&ErEYRQ|}Ba5`V;} zKl_>^e{L2I%_ShR*2A?SvT}Ufn-aqU1flgCe3w|`$cb2uTn`%r-1$~Wu+#3j`RL#B z`x7q95`2T%b<{|2F~>gwN~?pWp`pFW@~p6%SckyX25 zxY6ni_P)Jdhq%PS0ev1yQXre-n$M$%{<2EN`o&EWnHpiLO%Ji?#T*igEOL&QAdDGh zds>?KJ>kR#30Dy z6SAt{#jez(!w?ShOr}97tvEnhP0T!@tFYn|nxB00gmK^iud7?tOXjE2so0)p|7M;QCkL#+OiXJ$WwR5e6_4VB2!qEKNVM%2hy zM!bk+?&q;!)SQw}GsP1<{;IkdnX9a;@az2wi!N}@UJ<-cb8uWseTo~KUUivnLm&b& zZiOzMSj&N>Mz!|wUCYG2O^R20zgnH&O>isn1y1NaTW+n%?D|HER9eiv%H`C_jgn4y(QfN+K=AGihw?JJ9_f&%OsjY;S zqaNa=W)CVw` z)U`u@+M}e0mYaojMQDfJt;Kz7+H+%3Ip@|72xIpUB7N*{@bq&Ii$zcG&V9n%>}M<< zu*}Sx-fVk{YZq+#=8$#8A!nfz^zF%X4q?5`>8yFl>_KKZRUDjP0f*|pD>p5}+H+t; z!zZE9cy!Tt!W73I-v%b2@%}Wi50{;jF>Dra@n;`^)ZF%vKFA|=z>4mG4{ynl4)e3A zZ^^g}*=qnzx<^c@7mov}Oa81B+szkGM_7hNd%135-tZEFsUZ&N5K z;!c|WX}b8k9-ZF;mfs~4I760WxhO(ZWnxS6NjCjNPUb;6!#jD3x2E%APHhx4H6%t< zMBgb!6@)_=EIYx{bi8av2hPxE8%OODFfmjPN&*PNK!q$ep@k@(AZ%)Dw?=Lv$#DyWG?`#l&E#fpC5CVz+aKRTc289+57Gv8olZuXTA!oWykSA-XCF!c_W@LyaJD?oy< z)(BIkF32$Z%VpxEaSh=x;q9>#=i8KOBxvaP^&|ztT5CrR;%tl7ko6n1pr7FK&WZ0qeGBTGSqp5SBrBoPs z(QszbXofXEBt!8J9hDJBG83fqSSg4J13icH8IuV?3s#3~5cBS{k6kor8om5M^RQHQ zYx$_0GJ{TXlxm0u4U3vpk{8|Z8}p~8uXWQ1i~-$I79sM?_L~zAAN|al$QNCoN03`D zbAM$HRgVeeZf3uN8;Z-f#w7a(im%Y>+2s+A?*#jY?dsWYRX7*1&O4{OyMaKSJbr$z zIRFcBP{XYAzVlYqN;@k(fS_#$wTGWe{%M?G=y_~3vNu1HknyL#(1~ivK-M<~Wn-4A z#Iy(yhJEZB(12B1AHot-!nUm`9}O6^>~P`&2f%C?;t7ow_%tesS>P_aHYka3;sSS~ z9&d}&^V?S!rU7_9F!BU(ZPJ-8v>1y5;hAyPHH#wUm^0Tw4kgvCo2ghgB>i}FySxd% zUV`4;E>0~-BvAJghYebx&Hd+AJ9O`xCjXrezaM%viq;`F)tM*N893FM7}Xg$)tMR98M-v5eo4cK zjb>@xqRQK*`bEhZ?@Oln+2--RH7H`ia;0!*9i(j!b#<&>Or8J&a zm6%+oNdA}TM4a50 z6RPPu@&b@oG5r=Q&*pdia2-JW^O<?#! z!WVSii&&$WZ00x*sEG-8Ry$VZcTC3k2=it&BY@)0M)h}YhAl)^?XujC;o@^AH3Nbj zDx_Y%{#C&jVIx1cz;`;VZBg$$i}kjD68}uNlgQwR*07FkJX*k!oA5Q{g%UCAmiaW( z7<(|QL3oUeUFBV>oOZ75zmCcO}1^DQ=UAk7p{01Hi~Lwa6i>>zZ!CE(_w%Kr;o4 zFcpm)Ge@{|F-h#@vy8)KndNqb!zs@LXF=W6Q~@DlEqGu~@;>#QY%uk7DKS@Y0^B@=Cq_H1%CjI_q|O@5k(uEN$Pg<_KRnZQ z;mffgki^VNI&*em`5Md?Zp&0Tmv_P8H=fB)n_7RW&5*mz;(p4=kTJ-7JQq9>7B?q& z%!F(bWzCCWiq=f^q{6jGt>-q!&ay~tB5{G%F=sHNojSIS=?G%a2ycin2j`vV3(B!o zZ|KWnzY=EcuVuDbpFW{+ujZMdCsf9dLoePvmF z)N~^X~kJrf-xxp^@c( ze0^f&lj8{?xWbo|XP7&zz&X3WIpy*mdgA#h{dnusrriXVRWu&SDV^5>|oO13}Vzjn^q zVaKIL2mkQz6MyPrW3OD(_8Zfefc3VymiULg_4c^_nmu2VAECcXR!R!_U7H=L%7G&`*<&>Q|kSvnEjlB_urKSNE&-`R4!t#zgM#0VOJQAl&PBeU~ zP6M2&l(C>EwGkXbUm{d)DKt7|*#Z4Y#LM_pjRUxb_J(g;`(?CpWh$Mdc19)AJV=PGmNZLO|fqdq1o zuny>lt(OF|w^{NHWvBq=k}P~8ZA-HbVg&`(2Kw8o+HBiIuex3y0BC{IOw;UU91x6g z(|YZ;5KAWi0`{Se2LjoJ`CNQM@Yu&i#ok>*P{K}QOrR=e>`Kr_EMCS~u>M?>1l=aZ zUnN!_t^oj=;UqO2iiC;uV*GKed|xF;Jt9imGbJU18?L;xV>I1)vCSc$Sv}@}Tqup# z@RO}}q*SK#n)|z>C#JfrLOl_eGhdZ^IaZLj!sJ89zWHOw2#z|9U>#~p7tw7HpkDjeVK7nc(jE(RoGPX_c zEL}rBp?Y?`6E`ddBA=c){N}v_x$L{9b(szg9#d`WqbIx({I|}FK5>W~yQienh;ML@ zBDp4hA#V@Nbu5BHKJRGv$E>~MpV@hFvocfuk2cec$O^p&9+?J@;ZqK8NhfWes6KvH zesNe2!FwlhBqN_vWSN4|11J7NbUrhz{^+464#tu122LUGU79A}#M=C1GsCCg1Prwg zP) z_an{< z3=*Cjg$(tqhyqXJnlTBT;6-<^jg?5_W`4ssvHT43mB~#o(mK9NOrLw za9oNl#JXnrT075G#5gOut!$CztgH0XR55X`R`NxL+#$bmMyfL0nq+nudaO@@8PSQQP)`AhYcv z*$mYNzmkcLoNU7$E-hsI2757&sX<(#sdiN0zLMu86{yX;C(8R(U~=A_My zk2N!#Teq)be|xz?FkE7T)BW>NJRQ*E8vuP;SE6+w@x1AVeD6zEkj7DGr{j4ef7kpn+$VV_D(5g%X31{ql) zpN0o@|H!zq;F9_muO&IZWXls>jj+0!L>?*XcgD&bq3Z1!QPvHzKh^_DKjuOZ%oA}Q zoD<6r;(xS-UM;nd2%JDbBeDNGZ6V$NSzxE)=IH3)?D{`-b~%b&2!EL1_#XY};Cw)X zLL)N5!9Z1zMaW*7Luk}2tKtLR*dYW0;)FwBprO<6Tw8B#^Bi6$(fgt7gc(_xC;y0w zzmSPgAb6itFWAm#SeF&=Gt9D@ToMUWB1;T3OPaJYpt&VOZzEpZjgu^WQnm1+mVWKk z9IlkJ>2sB|Edcs(>ZF|pq>l~1sR%GL5OK>sr{M}NuI`v29f`M*_Nvi{dIBVuW0Vk2a4@8D|W zYUN-rWNPH-`hN=%W7Kz5P&LrL?NRh1gqR*gs)p5Qg@KBeKZ>Z(KvdBha56FKFDN=F zqO-PLn<9;?7BhHWI^wQW2tT$eSQlI`Wc~TvO8r&KPIu_Ceo-Z*@lS5M^xbyta-Syp ze}7$j0G-|i87(!sIMMb@u+E_7#@dt!cVC@}^Cg^Sj3$*@ac36koS3T9@@%R1W65(k zR8W>aCEjQzW0`U_vW{h_)f6(jCd>E;(calgvrc3O(z9OGMO?F*a};1h%(7hdRAzOu z&M>BV~B zNLA8eEqIvERP^Q!qt|nCiW+OX(SN0$ZG+I_tdddw zUtA3z9;% zKqnK;R%aS?EZoO4{VmT*ajo0YFK2OL6^1GHjP5h#XgO1D8J4Sd*;A@6+Oo++nHdDv z7n=nq9m^}lB83s#yY)vH3)6`=Xz;fL+YG+7tBN|~hA!VccZ(K)qYZj_2Y1FfaDU>a z=DCm7S->N!UT14uN9(6ks}N)&HS44VP=k5w`aIvyMm*`HKhVRJdJmpsxX3K;#o6P9 zG+^BOytEa#CoO%~wHIyuK53$cU6H`Zxuj)Lkb%Cz4>PLR5Re>#*UB1kbriu-P6{W-3 zBuj<-SePoSt6q5~&H8c>T|p&(Axvdf&{DTLiI4q+Zm!*65))0Kg$OjQ4E-v1U6xOQ zuB3~%Qc)zDoTZYzbd%eCTN{?Vwi)lBg4UhihP~91J>g1`_Be;*8Lr)9al>{ZxZl`0wFR(oUL^>%$p(y+YOV{?O z+_ic5r)A>T$+$GjOEXbNu|Jh9@B+5sh?{{T$=LZGnX@>+mrqa}#%fQa5eGwK2ES*A zWne%Va#gTgl?sqtjFhmP63QG$LdEy1qs?G$yCjv{_l$G@h+(p#F?3;--4#U3pw)JA z$rx=7BZbE4O0gXuK!sXiZljs&xpogfmz>4WoctjDy5!<}pk(+CQs{j7ntNmB;8Pyn z?nI_6N9$*&nd9o<47_!O+)eTpG)5LA4&`Kva&zJEap9n{P|9oyRG?N!7ZJJa_=&m` z9f$OB>70oH^B?}2@8f4iy@@sG?L6|CNUD zAqP!vpz<~eV53horl8nZOdcih4Ew&$+##tavl~|{__?x)l+MNDe=IgjY38$cEZjZ4 zeSQDS??)lNeLEPhx^&vKa#l$NRxy6*+7e3_g&S~o6()ZBSn)BDkW*b<`>F~alAyfLu+TU@`R74re=VI@o_fPd$QOOfpiJg~27FWz0SBphLeHt- zf2g_KDCnLC90-UK_J7y3nEnscEb^b<0Q@gjCS(4ejVmU8QyP>RDeT9}M(5I+pAflQ zv0O~Irxr>X9p(}^1!kvY^M8=`PQjT)-@12f+kC?g-WVO*PCB;Lv2EK%$F`G>ZQHh; ze7*O-&c%0e&Z%9e*1TDFvudvSjIri?eq*c@SL2TsN5mc`?*t?72!|(DVFyG|8Z*D9 ze`f-IUo+N#G}nogTF&1(S5OJRlW28I*nW1a#_L^=-4qjr16HjffBu*}a{(18{b=d- zqceEQ>|8~E4v6bX71QBIlcVi*HMHbRETEVDcMipmW^YCU&7E8W)js!CrdZd-8nN5- zSRBd&H?gf)SKkrTB(>iHC7VoLfqSUkPn{gs4?}9!K}%}=q5pnqW*Jo3stjR?(s=`) zReSf#v(Nk_?!7OfUxFWa81Y+i*+!BYnl@$lb3KI>y8yF}8TUj%MAjhbr*DKVc}$UH zUBb04Us%5c73~#)Ttrdk>`lE(Ywzawp* zQL&W$zbG5^uMWrmzP!0ZB{;`{uPrj1T{vu4#&dkaHV z@;29V_w$h~Gwkg2_x+SrV7{c##FBc|wR(-$Gd^0`&ZD2LO z-J;iRys=rN)m>9nxztBm)e%TkV-3?xDZ~U#(wDYXc$ntA>&A&%#Dv!urmHMHgZS)BZ# z0?%MZ4NG3NwvB5+V0=VRx)9N3GJm0aakg2#YcZAlJ*5TJVKjI(m;OC9crSc#wzbJp zE{HoNMDt9j4zu@-9^h{>0N#hf3Pg8Wv(`46c!aRfPnDp<;xI-WJf z>6G*(P0eXlJDzUwL~+KM1la;kM6&A4$Hbi-Agfd$O(D&+eA9TWxKcRHZ9);hxYDuT z9OoPi)CYoh7K3ufpLKpN6T`q^c^ymShB)#M6cR{~Wbh(5#IL_QaFTtJ>2#@y1SllJ zmIxFFbaVQ<7?~T!X6g9FLUr&Fc@PGbvZa3-hxG5yM1ZK8tyO#r86*`Bf9+v3^9#89 zA?FP!$8XX&7lT$AW(<&4wUq`V18*sn8o*;*dtrg|h;4DHq7x|P`8&HyJY+c5i1Wos zPeHYnwe9WC-2@WTLt8osbXuqx@^WGcH!Y2W=wIz|J-T?+{CN3Q_|XfYS+#x>UBFM; zLIc$CePf8!jT9w2+fq{|SoH$`7i(@CiPy^doXXsVc)`k>jiTBpXnJv+ZE4Wtx}oM2 z1%SbPuM|Y??ZTA=9QlE9F{eQjyc!ddYsFg3;O@v#Df&{;N~qA4pYkZ5P<+VCn_WQ} z@)3Kh0l2l3AiY;8j(Pl5zxut?(UQ7*{Tw?x*Z*(9ql{pk}!SV3)8Bd){in!vo1R#Lr-xc%rG$zjgg#}|WY7Kc zbkU?z&hwRrGZ$9dFHTQ=^ZRe4+YVCwn1~9m2#)TGCtUO2voo;gI3zJI zA$#JP<|7!lrf&8jjJ2||l}6pRfjYUjErATc`^O&L6rgI4mQRBCeTv8`&Xrm*D!Zme zvWzBqY#^0oSA}04kPAfbfaN%bIH1B4a<+-4gu(oaid6y{Z@Modxu3O&R_yB1Zx`%P z6RyO24Sgt^LIp&4F*DYg^TW0;ZkUsCQt!5H4R$T_%e4aO_!xFTbx<7_{pdraqP_f@ zL-gD!p-cOTLRk{cRc4Lbd&_{m0R>%2T{%b2vC&_#1@)|y@@qSiK}fL&l+OgHF}#yB z5f;T^$eKNK7!ohl{)~Q+WdAwx{L2;xQiJDQFbG}A43Zqtr$U@U_e-BnB$Hpzu!K!$ z{Va2RFDY$K2)QFaQ6~&3R;hi_2v5U@+1;^kAI3!Lv)<^{lqg+F4)i2)1>G34>WRSa z&hG;PcSrfgsyXhPf{lsHH_~t%cADhiZC`e_`3MS2m6MFOQ7|aydkHZV)3-+$hswSh zW>B*)T%^>%Sxd*z^M?sz<7_{HaNy*DmzF1wOW(ciOPA(8a&c6`C}X&=P`8xOCxQCbWC%;lv5OsmwR^X`m*sw3RsG;FLMyI1x>o z&F#PJjlw0eYsF^)CN2WO@&PsqC&Ea~oIBLmM|rWV)XC(%q}H#^mJ*51#h`$)m4(}j za}i$_S3GVUqm(DrSr`p9z^u5K^xoq>C)V|g5kZMWLdpzfI}`G~hI!(9`IZupRuQw^ z4oz;Cn{H(TONy6(2}J`p!bt{(ep@Z>l(xy~R(Gy0++kXgiWJh!-MfJY%8qPVo8BRf zbR)fNi{%u0gHuc6g}@QM6qVhGR~PpjHi;^9!lBYEC(6W@ZU`-fA-TE%vow$e3|9CLMYeI`K^>MSahdx-3ehKm~4`yv!-z zSeSc|k}CbC?C1np!Xd!qu%xvPFSV8^#X*vbgjR}nTt_|d#|vF_`#I;L8kL?EwN`CI z17FpO`$B|%0ZnuY^u+nf!+15E4bq@c5`fdLDk2{3+5@{FHFiC zA9YAY+`K0Q|0C<{#pnp^i!=J3xMecDB>kA_sgM;qNAR5zRfk~B6w83?6po|*kz}dO z4t>cnU@`+}*Ulu;YTlq=#a3;=rqJmMSWsrTMqpBuT**ZVmkqHbqEe#_vn7O7k0`NF zwdDI%;z@Zg*s!igEp42@d8~W(7eZ?jrf9k+O>weY)^s z+VEFO*Vp7^?~of!8=$-++j&<+wUOTNKyA^s6UMc6uR9W0(o|K=rOm&%jTcE7rYa@Q zb^1FxxCKhm_;lV|_u23cIkO*CO4;43mLX?npfVRWHGxR0(7vr=Lbw(+;tekG2~ISH z<>yoBI0yc*F~lqMi?9^*t@uF9XgNBgc$$7`vI^eAGmoX&H&1Z5UT?Z?&y<_2GM$G@ zTrFG>xiIW!wA%`5io1Vkm*sFUxPup1Z@;+jxhArzn zz~q}LK+ZDunJr%API+S-{W<-xMP|*tncn*Hmr<$6vc!w=p5-4A*81A=*ks?BOP+Hz z2MGM~qnc{nMq|OrAvU?r=*QZ?N4uZ93DWfWkRCIg%CrA27C0jFoqN^OimdQmTJT(u zg><{d?h&9DjoJb&H%M8eibjT`v(N0Y%cP3;exyw*o)Ii*4-lPpEWYT)ivZ-2*Ou9w zab5TOEjP%IxQagO@QIb>krP$p-ur3ioFrF=EDkY?lG8nBB!_}YdxgfdS}R53Ks8;ct4rb4S1w$r9UC%yke&3%innA`;T>M z3H9=lib*ateT{!6CpH#}gR;Jj@QX|OjGFw|zg38u^dQv}=DJbE?j9Nr?&d3ihfd*7 zEU|zcYKbEmpZmD&_6ffk1*33B^Mh_Lzo=( zicsrUaIVQN|E(kQy^s|Q=4+MB5souluwMHO`k%7~YQn7y);MF2Z$6(vtS3D~4jd~$ zrc2{p*il$!M5}1yAAvc&@JhREAr^Tziz3n8BL(MyfL#<^hWODIu)_D&at%zlJIUHS zFguk5%yqOeD!5c><8Z-;Q0(!IG1q*IvzY69BH1df=6pt?K@_GWHw>csM7x9xodXw? z&Z>m*Ia@9$9X{(YcJ>gD^BF(C+7I;=Pu=uIk=W5WYSYnSSKx=aD)rVe8La!8)oT~puFp%wXRPPOPY{Rge+9jM! zQ3$KLoDD5ySgn2`Xdf2OB`a-8-88L3ov$KlFks!?Pk6#1eAO$`Ef|j@buM~=jUXU` zgDR5WoFGlMPO*&rEnF#`niwDjnQXVUHBGoD0w%?^Qa{ri#51_IgOzOfMPi>&y0;Fs zn(bj@kThajr$D!7Pj%mADjcY#(w{D40o&Kghfz^ZSG0;3lwg!Td1fbV;IR* zu|GB$?N{rr_|D-HZ4l%so#0vF^cNAFVz+FiefXiF%^G>w!_x)IIckiwIG=N8Czf?| zaFn;`A4{D*l{)_gJdUfzMO~TpZQYKf=+~cntRZgPH1wq;Bw6tcm^{T`W5w1ylxLz@ zDRBiH;UtAsg;K4mkkKS2C`qKDO4`=)$Fn3t3`}AQYnk*V=|akK5Tpww8`i*B*g5T{ z=@s$}k+kfUbPNX$WvJ}~tVxwsg%Q$io|Sf>vRNhzw9Ir4$-SP=nqOp7cNx9}h&X)`z2pD0&j;^y(P_R|H_ zq35R3!xwjvVV>8!Mrd)GK*BvZK1JAEbce`_9q-guB?a#a z#harzoooi7@z-QAo9T0jrnW)0J#T$6wi?Y*es{A zk2Td@j+-9OLB6LR4pJGs8H~`E^a3^XDrENI%{?rvnaRE5GfdjmAB>T2NeN9k%t9@# zsviNrTInx;gn8rE2ya}Wx&Ea{@<3V5c?Rw=xU144#jSl5`IO|XtxiJw66mN&4-%=g zBGYW#`D;1RQtNz-4M0Eqp=h0So14V#RT3Pu!*b4;;4k$co&#ZAJr2pfOOxjrD^e#+ zHVi1=Lcq=4*bAc-&B?<{yWv^fe?&Q_{F6(o`oY+XCVzkK+z7w9$C1@@iPCd8a6;8J zMO3A7hIdO4`@(w&9=`Hau3yk?;p5oo1K;cG|4W_rYsOWreuz&v{Wy~0Hesc|DH*}3 z21OX-3u^e%uI%J85gjR+2^VDN%-~R!^fW|o_eWn7?0W{?^3J^wiJsrIOuuBqyEk6(8~-GZ@GE!2 z5$3&Co(?DZ?bM4(J@?aKW0Ob13qOv&`j9}5NKObH>!g& zlr6m8@N;!g^r4c0b^6Q7$kivf3G8Y{H|0&z-u^LY#{s)bWO$)9%#)Fjdggk>m=UZ|hm_l{I|1`K22K_`)f#lvz^7<`{ zT}kZ|KwMq^SbZAQgpBFA?E2SYS=goS8DZ7#5YsbIXXeC)_R8;QbLY{7z~7LIw1D!4 zvyfl>sl~xJhp`{O(4FrShSS>vyYUbi-R{d4=hWuTWJYNj-+20HwtIpcsFS%rV*kQS zyn&o}@0HFKgS%Oq9poJIyn)mT9G(T;zVwr}7|lkq=6^FJxkk~(8X0GNI_*Z#Go|_< zp_zce#rS&<2*8#|^92}mORTwvJ3V8;UfNLmyGp2B_l@2VRr>k<)pR4C@{7%bcgia^7vXPy zOJKEg-)2#}{4s9mPM$o!em>I764b=5D-q12gwQ4VLgOS587vLi(GG#Gx2_iNGa*s~ zE_rZ}n?^HbjNB`H3C#4BOZKGy3qJYzfWIa$`f$r9 z?dyi%J-d`ZfXXsWkxoO}gUp$jmM%ImL1}t~!a^0tAe9d@UK&On3YP^<$^$z!AeE_V zQl&| z!rq9kT}AX&v8J~CY7^YWRJN|kp!F&mdGN21PIU zm|kXS#=(<(5$h}a*e%`zW6{P=MNSpybszdpTEZF1b6%ozb7(uVmM*A!mz@>0xx=38 z3HZf@+xMQ!z?o5qyw07%PScQ9=t@SJo&_ElJ~|&ZIxo3l=7-H8$hQ=~Um6O#0y1nA zwy*`mwgS=A3K`(WUgn!=+!~-SpC?NzVS&qw(7TbQrNE-T9niiD-7+#Mg`c?X#vEkl z=S5qi*ef(LE(YCia@D|80l%3*SOw2(8rOSaJE2HK^_vzlM2=uJyiIbs;OWSHVEOe= zC%is{uEdcSYWh7=TEI^O23>@EyOPN&74?%J0KR{pls#t1UTaY&=Us(3)EoIiKdrW5 z9&xq^KRd5SOun3+7`EKWkR&^w7jCxtOt^nembP5h1)1Cv3Kc(v+!GqrdsyP9LI8i^ zq5GfHwF%GJAj)=EPT znX?cN8oUGr85T(h-`R{w+ho5op3uKVVdx zdVvRurJbx~P?OWzVz;&^xIYC8N$bB~fDFajB!Ogs>*|oNgT(zq0S`gL#~QW) z^yTBf#Q$b&s-nZJAWz1EMa?&;Ls(ZKr0$YY02>F(eepxQVjUMIu| z6VHd7o7}V2*ZsE*h#T>XkB!|zE`ve5-Jkj(o=Q{1|GcU(@s=(1g=Uta&1LmBaen=FDRBef{-NC%yyF zdqS);IHdCEWnSqd_+<3)Luq_6he(IW zZCa+(HShSwn^d}mAkcs$wnDV#)X7s_9GA;LwOq1$P95hEMZq9*LKIbz;d>c!%3zRN z4^ZL`WPLDpAQ(>LS&S`h@YDs?ch=FYQ6=l8RW!3iPVJ|qX8QVIUoHH;V!BCK@NesP zl3%mmmL^OU;m@fc+J!E`5ig|@@K1x89|Wk=!sV;U__He-H@sIlpx^mw-7;HHK-LoU zFgA!$eRm)Qb}UO!5+JdgV~XY^%)S*Peb`?T=v)Nvwuu%xIwjqzY^%)rJRqLs9h?bY zS%wyv(A2u3THnQCo9-uzA&DVixb7_ufJ!$*6f2V&INll}eRAhK*nu5{E2WW)<%?vA z5KIQ*76fpXH(Ki|T=fe6A~(9(XPeVE6#&(ZT$a@3z;WhonGom%b(LOD9Q;NZ-$|}_ zw5YCVx&yhK;LW-7j>F8G^ibyd?$Y?$+%H>&wtejQJP~(Sk??KB9$HUBvikHS9BlgNT&5v8w0!?)=$YkCN?-u-clRaQ>tW3U(|%?JJ< z+a>4;_xg@~xcVX;-U)t9(j|ET4fF1qLvz+Y+zJ*F_9I{YV?eB9 zATL`oy|&%jD1a$8ElO`**E&E{lyguR8ncM5yZL;!*ykeeQ783#Ca57>o1a+ktPv-Xl8Q+$da zyhH-mI^}a14+{5Oi}i`i9Vp(I^nN(^ap2kvy8H!H;{iz=5C+Qf$VhoDgw$>HsoNUR zH35klfs|3TEYf%zjE7M)N@XBy%o5<%5Pb0ZEPr@H^9fsZ)(p`ym6q;zuzmeT_e32C z7}t~7#`~DZm6rbH)bk-pXA&B0k|wPYww^MmHU_lCDu@ck7iFEv=yuMQPAK0UJENS%Je&~a#cJ(hDl^Eaoh__9$ z&CXc-&MDR$8ui{U>ZUJl$ShtwUAUIEC~Nw57Q%lE5refS!O>Z-{U&di;D?HaxUBq{ z`*|@7dHub2^dky+MU8z6c?aJ~97_Z&!X$BwIKP)D(V`@G7AX5ssz5q5Nuy?q|8iE1 zs5w1B4@z=fp^jOyfjPH8mQTe^QYTE}bu4+FO0A{1faOzD7$n65$83afOcaU!4FlL+ zxy)o(E}u}+p!L#CvD{Z?3D7K)_r}%kb&rSV=hNR48gy4gcdA+Rzt;8l ztwRS_ks=%5>8IN1r)*j0@+sW!@4)L{rlyBVz+MSoSl?5pAZ3pNatE zpy?zZ42#gm&#VKJLA9t}n&`MY=?bMb{)3v4h^J`q|1}KZPp4`6X#O1IN*rU=K{+7e zH}=vb*!eJ`GkgfMcS}@#0D$l*l?;uSaYfnSflZD!3P)1P``<@W3e@j1)c?-Ypprx% zGIbI;z9;VymhnuOJ#!x?{(*IUvHUC-rIXK=V^KNZtX3kFKe&hZm<3rFmLyU zTlojJQhdFN%YkKL=DY7(8ux3x6%VC|9%DI0P+`f!j*+aIzmRK-t?C_6^0QC74wsbL zMNRwMmui)ZR|E5n9d?N>9PAd)h7~R-uI8aP4pl4`u&vT+9*qoNINJ)O(cgzv20G`_ zdECr9=}Z2}Zj1fSiruf2(+-_Qc)D7Es4e6er!A2S_Bjxm07*|D?Dm#i= zF37svsz1)q`aKAYEo+2}pUn2|7#mHt$$sCl%Peg98(!*g%Dw+gvbYv5dh&NBVvnbl zXMkm+hLT0V=29ZN$8@n(Oy^8^p()%`9c{KGZ+kq%})Vk`0MS8ia z$2RW4p*cZOagy<4+A|ch7r<0`bBUptw$ zC?7aue}y3eO&FvHH!j@?!^j-8U=ANf@i`Hv?z2!)4&vjRo%y08Np)<{k4Htjz#Mp; zHyk$^VtDxzA2+dF5XSSwT~%+g&T9F9mIpAmUyMPqk02k)i~)=fWlEAiB~KoK>LM0K z#-QmKy3$QinG;VC_E@Sc3~o^g)0~Sefg=i2C|ZN74)ERN5vO+y(rh;|LUZ>))VvwoU4ZS_K8B@?s0v`)*x$X0y-Q-dI!f^1btqcnGJ`_Nmb}(n^ne=RSE!I3bX4V zmx?B+EsPr2dKXSaT`XTLFLrLi&AS;6aQPCvFWK8)?%fv*?fep{$10p7 z+H*a|jG63{qz6Be%&E>{z1iIJ*>;we`%t`jjn7N^)Vr{T&hhCPvk(ghI)tYzZR`>`JXkp?07zxw>8yj%@6LGXXR(c%sEHFtX|BM;rRo$pCIrGcH2EpvU}7l|3UJ{Vk}f!c`&Wz@PxSY_B;nL(i)0m~faV$9IhD%42VhpqwfI%?*O;3g)t*J6Uf5`Nv#=>=y(JjO7qX`3# zOb8cI262QXF0_KOPkNhL256wXYYO3@_A9yBf{gtI8w}%fa6?Eb?gI8(MdH$!He#KV zQZQA=X^LF-c>UHhGfoyJ0sw7?9JZU7y4OxJqs-u~y(s)uooozGo6W&n6ajGd;mdHU z6$KFlg)UF}5}MtyX(7)jW@)_il9_HlW*d>fmy5U?4sLaq&Z?Krb(Q)NY+1|)ks{)4 z0}u>ooVSjQi7DQWo-wy*K3wE8$4X0zSL8oqtF_M&J9?kpl7B)H4e8YD(e10U*6b2p zq3hjwE>*!DE=0vZzlc=NtY)6oKMOUfFgl7maelCzEvzSDpQq>~c`#Z^NKflca)uV$ zU?K{7MOh}sDHe7PT^54U&Bt^6z`UAy&Ar^6a9d8w4V>CPb_2}EjWfESxIi#U!s!9C z(67jS4s49V5m0f$5t#Ghk3*DOE?+=eW56O3$VS=Co0YY>*uXJoJHMSD$ay4M zLaX+JJH>_a_L+0pix#87Hi01)rTT@HSIGSvsH9h=>be91qPlG=Le!U7 z;iEIm>~al#>d`r6P%91){>UW2i~Q68eG^Ex2xV);n#am3-AGjJb4 zPM(qS#L&|KFL57AuNKTnl+=k-l%{yCb5M&5M<#pC1%DA3D;o12V@0)<_LlKsq>&nDc&5@NvT!}cFF~c)BQi$(QWu<+27zDbXAP+%>?#SG)aqm zoCvf$i3IkbJ0&fFbu?=d`s4*6!NK(dcAb@r5bpJ3CpR`s6H*Q3h64uAe4dnJwt^Kc%R->(Ds}E&=d@bK zI30}>lhdLk&81kcD*Q~|2dtIMbjY?EPqCy3EwcNS#x{<`ERv~nB+h8xko*kOvarzz zT;x(^aF@I5(CbAN!Hp5c2X@%IYf`C67w4H3aoR(M&JNFHxJC>ff}kPzTnQSs;@Xtw z65%iBuSC=PYXjXC5g_9Sm|*?j)^!i0amk0-euSZ)q+CiAA;hs;-EUly$a#((<036B z$jpoQTlJgv(!gz2?|zAfL>{+Ik~?P9;f#)Mk-ebyvx&>t4=c(Vh?}#7uSuLu_@+1RR##oa22l1#WBlreUocpm#v{T)h(kr z8udgO1efCE_eUN7@y~Bj+;|h@k4@5c_os|F^W>Ztc~;(|${5n_pj9{}AA8fB2c5D$ zJ&gugCSY=s!zyy;!0>mR$cC%Q0cPh5xk_D z5iOJBa?-x6rVLl_R<%1??-LbcxlzAl+f~shFF900=_;xoBc?;nepqOlmD^~*LAl?& z`LCy4YY4v6)B$ThnqVhsY!%(spUBVKbUb?%dPakuBEK5P#&|7$2UZP8gIM_SJxEg6 z$i-MQ6fj4)JL-StrsN==S9HXqRv#Nks9*(qX|lG-T1M`->lo3~QswLQ5Ho}V0I1Af zOyh>Z%4V%HPJo)MFVM*h?-B9UhOx@A1M8A$?6jJ&XpAX{xtxJAYp)Vmaxe<0k#ldC zbyQC7`7w4CJ$9(HIFq9a&)q_pc%ZMj9DK^2l4r$}G-pBso3Tu*5?#5Q#$_+K2tnpF z@^ZpRdVUh;%OJ5PwhfX4*3T^Ix?7_6x7uyx8&&8w$2+{1aRbj*OYm5Dt|bn7Mr=zz zch@Fi*c|e;FtXdOX!9h+CE+P zzH0Agf;MKNlB7A{OGfW_)(v;KhEKqd=ZjQsbJ*6S%Z~V`8WZQdd$BJhqc`ex;qnPM z^?r^BQT}x;FT=J;=Up5y7-n6Uz;4^jKI|#MJ)4r~Z?{O8cQEgT_=SmHVW@W?&U*?& z*EN$wd&32)4iWzj&pYhDOUaRXJ#C`Bpw`fvSW?;BX+=Jx))P~H)m_RU3-~vpjXe0Yy`U)kS~sQ)^4p%b!S=8l@5PV(;t4m*EN#>G z@ia!^^ROfXz!*@Hq2FQDPMR+9JVkJ|2Bvmy55=LDlEGSms2R%R%lil3Y4@$?tf%E`#%)j;!(f8~ zCHg_y4rq_MFWv2EizbIkMUDw|OFhPJo#pfQFYXMM!%u(AmA@;9g}{ zWsu@-6q^CnA?&hGZOsHDbkumh%_J4>=Yofar?X?5^WNIjTgo_0|EsYrRTZ;LPiMsH z-+WW{2fTi3fCnTfF5!uFAoL~}H|~dMgW)NHPW(wa@Ag4N(t4;5Y*(vb^J{(PZye@$ zmUPQ5OZ6hR2ioL`989Tn%_^)OiMB_S_%gwbgq>j@_W9;_Wn_SGJT@}nKDG`MWYH03V`!N$^VHcZKMPbr zTtR3aG}HuChrj5k5eS2z@ML|=eMoLnKZ;I!i}D(!c^C#Xem;CghV!j+3Mjf5b>IiuBD5*m z8?D4h^>~X(^oFuK?Q-KJ1cgwqr7nm}Qz3i^T#E}KOq(Ozxc*4u56iUuLU^s!DC=i= z!*%ufYjo_Xd8f=eCWm#peRyplY%X)hpO$JXfJ2DSDR^)~JP#>ibFO~G^-OA*BsM%E zPdm`f=M`Ks!*s;k8)y{eh;nZa9PRy=LE|Esf)h;G->C2C=LEKn^pZhKM)I(3S?A;!S(u84UD!~e(JX#D`87R?TxYqUX)o^e!(k=kubO=O`>3)aZ4r5L>{pVX(hwl7H2L z;{>95hmH%f`8Nvyf&y8ggx}?eeCvK^$Mz1)Z`mYuItqXVLadtZ@P3{q62y(@>klRr zMC>J~MoO1st6}_NN~^EG$SeS6`WH5K7S~obrU`1|5*iCNFs^Xs)1o0k;NR9Yu(nPu z6PVv{8lWI42L8fKRl!tEVJNFEH3qH_%6*j#Ry&tM1=}_TSmkc0Vfm9hw_cDX{rVJgl!FWBbt_6P{XpP5r+Hg zYbjL4lL6@mW9IaQLG1fYD2{_G>7JU%){h6CvIxcN$IR1L+0$td#Xh3HZZ6t5`3Ltb z0&$kD5@~%QpbrP19(rJ_(v~i)BI!sRs24u*asEK9Ye}&;zTH#{^7MPp)zwfh%;AZZ zi+g6va+c!a2DYv?)5ABSQf}$$l!#I6UI+kD(#Q2xY_sf{*o~fA@dlXNUxf6u(C6ks zTcc8bRu4h47|5YzA-1k1)#z1W=N^2xOa9G|^Cz-^Yn;`PTZbb-1J@K8=57NGjMs|< z2jAupeZY^HZpA|+06{1|O7Z!bkc1{VC9Ec%T7zQ63G%qOdJ>C~&lz8P5Yv!P!fn=( zM?jmP&qODftKW~TS~k0^jd?5jL4kkvS%tb_53NEeh-oj6T0aCZ-sBki#8(dQJ(*t- zQ8Ly*JE`4(RMuA^n==iDhtub2VmKksEsa2GrG{1O)y9?yl4OPZ)bemrG2pydm+;v0 zdjpu~+0Pq5MQa5bURogn)nn2g?WycNx|uw2wcmZ~A6oK!Y-~@zXGz12tRzD+6{`Zx zdvO4wT|WYCzBzvS!s7+WVJGtO{^)^hPhd_32%-h)05!)P2AtedS_VeQTdvQFSl?y1 zpvfT~rUTDM0St(`b20G)8Gb$a+Hu^^4-io)9T*Jc>^jw;8yNvC@+Ka^&UIi@KbT5U z*0IZ9{t8IVCH(4VleELMw(sRum)G>HI(;@#M?uXWtmK5&W@11BElezc@(NpN6@!F5 z%nyq~*S6#3B1hubi7}L@lRXL)6N1nI5RkfycqU!ZIG1x(4E(^N|C)Ok>%O-U4E6uA>?6RJ9AsBU|teQ7K^E_Od`o7JMsc^!^%hYxI z0MW78Rr?03Q`Ykf>iI#mWrXVxiE~M+JZ@Rhk`A%CsinEV`~{y@^V7iIC6m&}ug<|h zM+7?7U3pZ~7_-n#_ZXOl&z^AZ8jKa*dhHxY*o(N7|XYR?-t^2=zvLAx}Nh4{e zDErwr91*&njgwUJIU?|uXqwez4vthG=!eWX1?!VTJ(8jP;tdAXeq8Ffhlh??H+l&D zyjH-r7>!5Yxcnnka<<*vgHk{MTlbNR zFXIoiB5b3m!dSzMCLjDu9A9t$s6^ds2n6x~rkQ%rSV|X=fi)BtTU{_X_Bcv)j!6NYI5gdpjd^4UIGt9^sGwpMO%-jGuBxW4>XiN+M-5Bc z3e1rt1T=oQ7xueqV**{qV$G@j=EE|bloxMeG#Q3?#|qE^nj1gzixIFS7MeejN6j?D z6T}l1=}sZ{N-a@L7b3A3h$zRtl~i3WU^KH0^d(PEAicey$Y+X|C1a&5tX!v5jn{8m zQF*n)qipPIRt!O)fTWnFI#4tx6q4Nz{6W%AcgiJv9p9v z_F+UFD(;jvG|a-6AIicly!0_)l+skHV5CveD2$NE4))o&Ohf{X1{qvpw7o!w=+C{( zN1)YbQ?%XVv^6l^4(5(Wi?F#gGDaQYDEb8TSb|p>)EErS)KC+Pr8Br!UPp%BDD_%_ zWPzn**cFD0l9dVcgN-en#_1MT@h+EtyyN3a_%4PwwCC!gPnstNrA{HZE7)|V1N?g0 z1Pgf1S?@y=RDB>K7TB6LbXLm(!U#F?7NOw68t6 zdCB0+6R<3zp`iRC*?{~uK~LnW1(fG~a4k(}9E}+)@wGluZfkE$DL=I71~kLxQg_1M zN}rP1_35N6lvW1U0M}%7h&dPGFcq9 zJnhO`1EK?CeB6Uk*wPY;WO%qpsXf`U661&=y@)bwu9{T`rNxAVg1MUY!FfESJYk`D zF67v2uJO@ofiH;)MI7r=xTer^#q~sRo1!BE7CsS3q^d{k-vfVm6c|_4390OOt2SKn zsU@dk&UY2}-!pggwcPKyqC`-a;F$mp`U?1Y$O)J)rhIi#-(xTa1++_)=} zx73Ib$x9lu(TkDO~ zMrc5ly9vS1zDD|GBad6LU4IpT!}l8 zv4IJs;lyxNHov2bf8>STjvjzG6v>hu~~ zPUx=5z8=y*^)w+wEeD~XDS#hrOWK`tGDNWpg1Sw+gl z>>h|P9X_9+o=_yYJ&Uvp*-Ms1e#U=>cOK^ACOvf&M5q|5i7H`e?K{}Kgl~TB8TOz* z@;{-D$xNs>EQzv)9UU?}l~D)Q_T=}b+!1Z59|)UH4L^V;1}uI$-fQuM;bS5y{9ct= z9on#`HT2)YnY~#+C9+A8Ni!{-ie1e|i5FNw3-Rq(;a@Rx%KFwn!FqR@>g|>={U&Fz z3R|wHzAxA~ROrY=Gs@W(BS#7l%SvZO!&Qs+aG%I~eeilcb$hNM)u);Lp>tG&k*GAZ z6WJL0$zZE6cut$+S4KEQM^@)-PlX9be4OSt#Z)BIok)pUuPad4sra)RBXV~?i)VF^ ztr{H@h%0N&@+@izb)dEAZ}{hutpa|zantmR&Z;@*Nql;d^3-Jjk0S9N(Vlxyxh&}|mQmIvq-@z)?&Y4@4GYuE zPN71?JD=+flMwdW*eAYmwtH~o@tyss7u|H>vt+@~W+JOM)0}B|a{|*I;dg_OK>2}H z>3BD*8w{x{k+jQ$#Ig*$m7qOt%%_5wI}7jff!V)7(|M?7OgAFfkpp<}^8_~T0ARC z_Nu6xI9BNq^BP{(akP~viLOWY!LZAQmaKxJ+W9KE?gSVqsv+{$PUo9&sU9*f;V``L zW8@*<7{)$6BokgL*`k1J7@-gp zCGSlaQ03bqN=4Vv-aSjH<9P*$_mE zqp5YjU=#g3I(g$F(T(b@o?Z<%n~r2wL?IuRpNb9}6=uKc=QE-c!Kd+#%(%!8iNrG% za2i8*xTThkqvDzr>=y;^Wh#ME!m$)9yxvpWd@Rwe7T?qd;v(sZaJ#iidS&y>O)1|o z^q7c@sv0M=VxZ1m*^-w7TS51sNc5D$C_MRt(dZoQl=X#S@391~k|S|g%J~KhL#Cxf z8Z$jR_MXc=IBq9ss`X$j`0k=)o3Ij|L|CwDS>@-Mk=Wgkt{8LZ&4U#J`nB#mmDY6`hFMMpNxC};nU^SGixihi)5_DlCF|;?uf$>s6 ztoe57Yl(c)>edi!f!dF8G+enm{DEVr9TUYr9fx#1D=$k11c}ZsTAU^MjicFp*Qh3I ze~*5a1<*twC5Z8YdNAKht_b~&1f}%H6qoM1;gUPncQI9aFN2tG3S-pE)Js+If>U5E z2%iXD$ZDmD(1RTc?+sHqDQ?H|jWN+3RD^s^RHfmpztVV0 zW*r_3jsLV){Gj$;&ro{KYYz$M^q_VU(2fDagxzkTEc{)oFO$&T7r9wB*iuT+Sd>va z3ASK<-6{JM7p(B3W(n^rQfRD2{em6lWFss#MejOhdQ{b?(Hgm4He|-@hf4 z@UDnWyG3G~0p(la8K5M07NQIeUCmi-4jq#Hu6I7;JGNf7o1SA=4;dU}ua-P9ejY&PLBR8T6Uqgwk)aBU(ZaFTp}_oWujWaD1><_c1rE|? zM6fcvcCmmniAY0HjC2a<8EAkhe>4aj7XVCvbRGgDGpXVW*KA|5*S zzF6;rM}_hs+{*l7|EvtZSPY>$LH^Rflvlwrsdui8606lcj|UeU;#(qOww(;MOkM~$ zmI3EPQO^V`hD>I+!9B%IPYOy~KD-*JXQSZvN>5YDh6*TaDgR;V2>g;?{poE{otgyn41*o>yp_O_Ga1SMwpq0bZzhRm)g zp$)BsS>414Iuz)22-EfE>BRCU1*pp5ClJXl^F+z8zYE!#;uIno8D*k+trc#CS_6KM zt4T5S#U%MfsBS*1r5wCwOm=e%SnHu{eY=j9>P^TT_!;l)(v2bB2Owx2pCIXLo-A7f z?LY%af=m<1L3H=9+{ULZ5hHgMmOav@8L#b$VoR0!xvLQS+IL|?b>W=8?Mg499{;IP zXKdpduL-efTXwCu5Ny+`sYsf)&Rd#aF+s2a4>c57_!h+cFXuy;ZkKdfZ) zcd8{e1GZOwbgG5pP0C&7>I+~|1h2&k%O~?_g--m(3486bwLU$e3_QKPI z9)@71xLZJ)2<&(hBY6?lZlunP!vY^)@R)t$wNij(Uo|&&!i7n&Aac&AprY_TF{S&Z`^~KS(`VB=T4gYc?o<%gTvu zuxdN3-|Lop-+CvE)3Q{vo>^mBXfCH>5S81aad)-kSFQd1sGZ}B!6%IKz2P+o89zl% zWJM-KmJ2larksTlT{pBx{Hks{$SFgmYLNBhfVK-v z>CqgPuB7La&dEKGb0~wfO>%5NFUb;Ew0&YpN62|*v~`5K-bcahRwxI`2fogUzRn5n z$iWmx?;rB1O>qdFA4nf4sS76cW~e@s)+g!9r#(d7KWdd1Kp!O9gF|;hZ9v@%VY^qk z2dN9L(}lNl5_5}Z7bxcr@IAT8G?S%)G=PFUn#0L3YLFOlGkHS1F8jAHgqQVOxygfs_BES;RCCxSGfE_ z%2lLn&ABe@{9Pb&U~vJpIkHexVo0s?u4oNwYP+x3Qct*68nq&S$*fO(XbEpQ&Wqs# zUGi9^VDT8=9iN_^h*QaXeo0fzCy5-}pwYZ-%_gDEo`rnl+&K3N*b6ALt4-5Y%BxU~?m8(8F)uI#`~D0~u-DLj6{pI$yB5YeAwn`1brDLngoBpmB7_ zI0keQqjmm3QL>C;x-(@gO{;;TsNtU-vTWLG1E~hZ{WCm_v|5+?Oq8Z1Vnd2<9Lj8b zSW*faGwOqVx&rWTj)QNIgKv@pUjUmYf!q7iJ#Cmn^ancikxm;kb=z!%c9+oOGBDs_ zOw|VHB}};dHsrr{+=CTonBMMSh}+?N@tT-iZ}U8e$!?AB-}OFV^*(6zyrFA+VQYNw zg|E;IIlU0fuP=G=y(D$nRBo+x*>(opxz(z&EiaTY0)9) zZB^aquuPA5Ede8LR{_qg^@~lTS;2Pw-^2xeY3qI&>v8|Q%z1`Mu@~2m)rzE>wFgay z-H~VAAFGCruwUgEaoj;=-Yd;neFKm_u%ZuZg#i74OYZdYQMw>cA6oZ-zS&;ixDifn z3Sz&|YEEzZvp;=Uj`8~zKjUtFzRBG~e%4bRyDaCwRa}b;zADkK^qS3oDpDM~CTa!< zM*Ggb75b||+9G>i6+gv?$;bSpiBQU?j|34>BEpk*auVkNb?5Czz}C+%J(VFnk(vcC z-)Vq#D#F3G;M{;lyv~Ghlgv0DnEq~Gme9gE7vZ0H6wB`*#f2|2@`8V}qfGP`%6|h# zykSBe^D9vNfWCe1VjO>gL%!&#N8KWQ^REr{2Gf30Qxm)t?zX2~5zGQ<(I(n7CuUcx z!nCDqkJzG+?t*+mu{xql2vT;rp?~ooaPEfU5hHJnB*V8JE)`C zv6vy?VFBwH=`XinfN<(EX#2MJ=7Mv`Dd4mj(XR-(QI_g1Y0#-s!U(#l3q!T#R{koS zGt6EUzCh4E;~mJ^yKSIs2Uphj$xj37!E=3u;4UC+*|BwPVlSW*b_O8gLQrfL2sefz z$^dI@c1|yaZSm+6gq{}+!1L%c!%#bWOWB-`C{|o$0G)Ht91IMNJh4<@u9CBz4Iy0dF}jsxTeom3$XsGWNZ`~dpS85 zXJW>BR>>_b+e0*{Bpq=#&o?$rti__yxboFL=DF-xWfT=K@hqA{;pp6`_q;r9t;DVn z%KTR`JS5Z?Ax`;$(s2GhK^auM;%=-2lAUy;??1Y_4^NhHnp(6syAN795sN@@Q#S7bR07_1@Mc{6B!Y2wsm!k13 z9;ej5Y<7q@{U4LAY$Fd}n7Kagzbp!mFpggg=PIYZ@k zq+4_7zDn9)3r$L8!m4Goe#>f@70W1tDAibD9T+zYDg8Lt9+SI~ATFgR5`{2ow=_6ZNHeYMOU|@uQefk@a-uVYpWz z>)^~Q8IAQ}z9lvOHLEQ>qfv#B2p^m{k}BNFDom02hql;Q8?RLbMCp%sm(i_BLGO1Hs?o3G4EYJ|XDd1;nx|5SHC4j-AKBx&V~Yyx68 zZSG-RxoX9C4i_w-J*O=ErM(Qk9a!QjpPEBMEGfh+xPeN6`_hb%wec%p{@IKIICT)- z!{fP?<+-Ip3GkXX*$Lb6WdB>}vS;w}r?>?jzH^iesCi}Ik`}S7SvYS6DL_*SBvJ-3 z4<0-PLVDOJF{!h|?UKR?USU8e>mSA0KHQ^X2TB}_5c>VTY&<2w#Gg>Q;5`rbt|)RD zw{W__9g${?Fw77q4aK_&blB@)TfVwh0G`mtSGt8m2N=SfKbyBC(Q|Np85>IRwYY+f zo)}lc=*s5XXO*4=dAI@L&oJ**W}q>2@udys&dsQ7x;7y(Y7YXDqH550b0Segg9vg; z!bLiOO70aD;!6Qxr4fqYg>vAipnf4QkAgQ3?ty_}vKeAzgBgk#a=L#Vj!B6H5_rR! z%rTZ*7v}jODb9U8`ob($h%#P^5p&`Qmtvl%54eY!o9 zw#ToF)Lm^Pgu&hAay+?{6ZTx49}E;2>B5|iH{s)L_=Q`(AckK@$7*u>ysyma{&<0= znx}7=*A9wa$t*4E4Nh-p)k%3_ASXUU96^DR z^i!t`Apj@NXI7);B&L714MM^Gupv^PuDPSu>1X+gj(VY#Xd(& z$H2BS;Ln6nPlZ7%2(ydkvQQy*0=bo{=rt!R^G}H>S~~{Enabub?6%v9rS!&XfuRVgizmvk){@}V(ax{EDt$WG3-cLrQf-%aMhS6D(~i zCW^GNVNy-`zH{9xo_-S^6hiefj1huv3!WIEY6pH0Vd*AJ8N}*Y2q~~nC=dU(r;o~Q zK!3j~$1LUCn^qJfn_=(+D3|2pUso9zll@IJtfEI$xT{BHLr1rwnGcclf?1#O7fpKk zZ1BUQhJ6no_OI;AKEyYcHg4NmV#+jSlx+TIvz@RF zEjAO0zMOl$mo`;{r1YTP-kCOAKrcC)Z2)%(wk(jo^yV!$n=LmXGs@Kr7ZQ^*I^K-C zV%W1i50@F_U;5pNm(d9l# z$guaMzZ^NlcC8n4JOum1y=)m?PSp;+k8s%Xr|dQ(2%jFjwa|C@$hk?&ftebWkht~j zT_qb(skG^l*e}9WMaVVazD1v$?AqzY* z(sU)QxVfrxg7x->HM;+a}Zt&h?mm)^jbg>Vp3%-HX4o%)K`&-Xq5V z$zRUjm-zHGRn0#%GsmqF@Xe4_#XoRz9@oJ1#i&~Cm$rNn*U0q+xf0D#g|C10F3_UD zZ!FRu!~pEB&{nUt$K)Hn9b3(<&&yAPvr8U7*h%qbfL?~~!IcH`;Ld&{U{NXZ;vvi> z-ZJjl-YSv7P5!k_Qw$rmq(&Bzp&N7ha%<-0GgOQZT>AZ&_E|!Cw@_gcW*Rvp(?Pkh z^i2WvgD22Xwo%rd5ou=MOP?U~`m_<>|5MifgaF3`zCua9N1kV|zUk6jU8XV79Bl-U zb1_Dm^4-foqDAAHQsR`}a~o3U^F!woyoS!;aRQoUFU1{tLEFj_8|M)X>869u<>yKkgelI@T9j?)=8U_>udy{+sU;_oHXwSWGx&tOtt%l}64VTtCE;u$Xq{ zZykqIU~h4x8~N)dl_3{lTd@&}i+b`ijQs*WioCa0h#bX=FYX&zcrWz`MU>O&i(&F` z22-@W#S9;X^y!VK6V1rch!#=yPYe37W4OH&w;Z2WpJo}68M-JUCz(`9p^bvuSb}fr zmk$3V-#oS2@=xf_;msK@Uy#K+zrw#C(Bt>seAvyje|q|B{sNWXs9ROJ{vVG?b@=BG z#Ft*v_Ugltn00?b3e*IfQImUY>knL_P;0X!1^qmPEOxi4cq!k&hMqI+#C0-sEvyyX z6rB8asiPPb3D?DeYZ)<0Fm2PVU_uOu2TR6D(GvetP~#-;Et-UHH5F{jmQLN2$PDFY z2YyxAHNv~T@oIRvyne9_rFmIDsJxUmym-G&><=#m zBNlrjnj*~hc$4TLW?1@YcCtPaVtg`e(7LU3a}^a1&-XZxD{#Y_pL&X6*-*w$7a79%{*~hUJQjlMuF0`S)&s`1=g2>#B!t+c0^Zypm)-`3 z0ICN!J7;o_)aEVK-@T$AUO$E>)@L6uT#l}R#9De^Ym^j1ad%=n>KFI=^zTNLT@eGs ze&&1v8m|Aa6hD>!c_^8h6<*4)Hz+(OZi~7CYhEeUlPyMVb~1K<(L){GaPLQJKz>Ef zh!Y4nW0&$FFu0;=^-0ol>I5K?V?_0tB=^e~;#@PN_VJFq+2<;NZjXf6FR6phVpq-^ zzV6W$;_)$H?GYQnI$;2&8fs5DMmJy55Rai@q}3@B520aJSB*n7q7fSw56l_?eTLlL zM9rNh36s~Mw8@V_V_sgBXd6h4qi)(}`dIsr@ijUv=mjf~`F1sM%?ek?B305Cf|0qw zlPop{D&w~$6)U>8HurPbYQ+eMO!o4itK8iHigNpZ+gHK8Agh3 z?mvIA= zNzqRS!9+4%Xrt*cUL*~p8$4$Uq{HioE}F!2=1ih1JY&kFGdg2RrE7P_6iegja6eC! zO9SeNPAij**DnA`B1xy|M}aJnWRM9wM;gXN`b*j`4QVRDkhQ{}Mdx(Jltys^P zIdGMtEo9!MvZGVA-v-K7?UoC)R_zuH)K2-94Af5b77f%+#Xc;smD0U4(2c^qG|-LG zy*2QKqV0iTQ^Gzi@P*1fH}HkJO{*Uc^!KS+zYi2&-d+&Ywc0HqC^zb7Y#=L`lsuF9~(l;(^AC@AQphPS9MnFM5jCf{UM=K>CQ~9{L*kN+mqP}@E>Z6Xrt>SpuSEz)hVTCpK~x<1Q5ewz0PvXnPyFrQlpg>)M>BdmLrW7QXEzf= zdIKjLdKYI4Yx@6+s*$lXcCj}3zXK6u4Qx!5O#WA%Pl}S39I^n)cL}X^IjXruRCtZa<>TP@d3t|9x~h_u-+Tpc$WinTWohs#Q%kHzZD9;?W<4pDBx zLhe`?gGY3J(=NZQ({CDz6$F!XI5pZb;m`^GMRN?3IWTP)M!Q;SjD;+rfzVS;*Q}kZx*&WR(dVoqFp@Et9d-LC6#`p(bq$GRD|(5JPcN#?T=Q zqI5&?5Dpe&_mLs|D1pEiVd~aUFw17Te6y z;1}=)ZI6s05p6|7I%bs6J6KAQtV!0^E4Sb)UQes=8Z*w8P->QeMOgJ0T8oN$ZJE4q z6qhKs&H=K8w_X?OBdjHQatB;ePzBTQBPj=Ei;7~N8$Aj;I9rra*wD7q$0$suV+Ilf z+COUQ?90kzNEG@J9aTBNgX)8pn_CY$4Mr}bMYBUCCQ-D#i%8c_JBj^XjxcmtTg^)z zd(yURE%yHsUpy}~NU>sXuny`;LGM|{Fus@(i zs_}?)vNy#_JcGBM2}@oWANVNRvHdCL=$r77FV&21VPD3Q{-Gc8mDO^ai%W{RUQsdLyKa*o0BE!v#E9h=SP=+<;& zqbgXp*!&(k3QyX~4Zdj2Iy-l#G$kgX3si;XZp_P?p}p=?3kA5#3?i&>={IUpRJozC z*dBdOL9$p_)REu*{?j<`HoZvaqykz#O)qx*LyuPFyx=%`m3+o_w7u1qU4yfrgxM&M z(XEj=QM{4LaCSF`={JM!%9ofBL-)ys1UPqP(6g(eeQT1q&_Y) zqsYv2u>gll3Jpl*=!cbck}IOIXIn6!JsKm($_>g9~fPS$mWySfnydG0VfNCC@dh z8AFcalG{{N8wR#I**%91y7GzQePAtQ zo68kq8)&W2K%oVOiOCNeG)egrTGW&;p$iMb;E$N12zC+R*uK)rWy{wE0_enBJ$m2v z2nIFDxWJF-^C^!Dr3((ylUB$mHO#QQxgf_B%5F)qv3laYglFzlLt*;(-z_y3=4ZrB zNoWC=XowW)B9^K|t04-7A?6yB5;@yOch4Gsqw)=IMcA$he-}pw9+9~GWY+44;P|Z( z1;oqr*y8YTq;W+@0k7?F4T9^fRhmEL2ty8B2$-1}1AiCP@aoI2@?wFy7_?DXt1`?0 z9o(AIn{jFfKxO0aw}iE#T*+u3IWR{k_o1ngW;mL~u}g{!aK_UML-2@$@e$)QPE+#P zU1^-rPhsEcJtA@j7KuKYu`;!gcYAKh4fo|(hhh?iMJJqL$WJkeQxl$>*$=wA2Z28h zzs86D7}|FxI30^S67a>J;qG)Y=WO8(G&U*FmpYoO?{M)mEAhpl3s0aArX!Fs1qT(x z`^mOc)PdOi2nQWD(u-z$(iU~!LeVR)hoRBgZ1=K;4rW5_yC`sZvtu=+#G1IbBkU~Q zNp+F+bxkj=ue&*|0|jWvBgK2#-O$eB+d~a^c`|UGW)>h&Q5~=k;kgwdzOR-QvBJv7 zlzNOS=J3ka-GtoHaZQ2|oPsVraP26_`-zQ{CIf>~q0^L+vj<0U@hPL-g(c=v;Onem z^aymL`6X!~{Q1hXcMlQ)RA29MV+xjo=zlS150>KM8yF?JGpS(V)=dwQ@S3le)_76D zRSWI~VJP13*G1(QIc1a^xc0n^2KQz3u(^sbXfOie4U87z z-NyKh;g8mhU)t*~*joq#?zbvr;^#{JN@`(3uU=i6sWKXYswgqF;k1%@r0l_r_%i5{ zOreQ6l-sO!s@D$@F%(E=_`br(r4aHv;WrDbGRRg?Gt;i*R+Q=pI&FXo!6`ns@5tn3 z1V?9K z0<4D2}lx>1aRDHM6qo2HrtuPBZyM)@wycw~4op@(_*Mt!Vpw-`s7F<;Ku zhrR;v)9_A4zdGq(^3PCm5bDEf330mvYt~3avxtpW;|*pl&V2E)4HG^|MrMgNwr-D< z%p6u($U;jTB28`w$KuExIy{ifh-T{(k;sNnYGAwLPbi29>e=-p9N8lI>|D;zXm^W@I(x5#7L{8Hzz1*@pf zRD*p*1ab8USLTt;p|;Ly%CBo|s%)w_<4Q{Y7}==F)fpHmG2Ekxa41#keD3z5hBcbN zDR@ZDFibWc-QsZa$idO?!F7dy+#%texPj}MI&=|vY=vRP(kGq;0jWhO^!nixuuhiY zpWa%HpJgKz`n^Smf)X>WyP`#v-d)QdwJN#$l>{2jy5+z2<3yrQpmY{7c8xUEF2e2P zVRl6SdW}zYsxP>wradi~^3zoB&b0!&=Vu~0J=&mhZ1xM0 z|A=uCJd4O5D=o;}{NT2A2CwL}!m`2wK6UUCGm=r0qiwE=`HbM6U6P;t5jJ9}o;^{W z6a#r<6z~;{&sCg|YKos+i=I`dOtaurgSfUVzhGAykiscP-IGK~yuj^NLgLaaR24o;Mwh_u3t)Xp7v zoYG#*$dA@rxcgB>WK@jg7~EwT4YUqhdsmEI`TQ@iUE-X&Ck6ok(2DRsVHA%41K3tH zF|u7-W7trDNmvZ#nPOv zI49#M(PHg7XsT54GbHDG-#KHxSkHVvkJoMb$<8*!F z_kB<85BfVsHRdiZ%HBNzeSw`Nf6^q?aD!#epnDjoWYw+9Hr%LDwu#cZHdLtX*n$jV z9O{_GZP;P347Eyfi5K7=*#2zkDP;b;hWI?G%2=I6M$c3y6Kcc`J85snJTUi! zgJ}E(Fz8M5peI{?HtGNhSOzl)q`a_Ht$3fngF6Bl`ijJ991rfeot0jt7p=XwXgm>q-InYSiu)d# zblta0%8wFa&U11*$DO*{WciOme)*2=>Z{bXeP!QZn`A+ZN!R6n86^Z~caT_d*DSe- z4$EvI>6Jy5c4ivRQ!b0(;riS`s$4M~6?Yh*fxbvVEfgrtm{j3zvqWY}xwDi){z@{m zuMTYaQ!U)jmZ@4J9o?NB5N=%KeIf>yGae{crvE+pKm^J9ijEDG=vy z{|pONQK|P6XJXfEv+$w*v-kk9A~IHW{|Bgg|Ir>OV*QBIZPPs=v6Og$4pVUn*=tS3 zCX(UkUUFchjfmwd3+mKe3shE%6*^wXBBD||4wd^mdzIpNaADlx3MqfOj47ct8Me>Y z5bJKQ9;SNAtW3aY=2@-kuo(I1S3!|Sv5y!CNTnTICniSNp4KWx&WJREfS(J_U#4cX z60F0hDRr3#(hMkxFR{AMQ%ztNUDQL$)?7?eU9kd*)mlhJ|4={${<{EKd_|OxrO8Os zqgi{@-dHn97hAPv+?mfE`g&tWrO|nf^2$4S#35%dh|sF~C$A7yr&0M4@V|X{-huwg z3MXfPfgyWwU_yHT#@2_QU}5-u5c|ON6`KijNhSnkITLOVa9rofau?; z@sV8f@=J#ffQZ}<4Os)5ZOWp0yaF0%CCsqQRvptsJEaqm8odCfv&_4)JKDkLihMlZrSEvD+wGnI61uEm z>8F|*WT$3CgkZZ~v8O;7fi6lAatuJUgzud;yaFm;Bghwd?2=fdqw^OC!|u=u92(Y3Afz9I@y~0DQX`3oNCo)|1))FcmUI^v35!=4#5)IC zFS74_z=vXX?lF>gi6V04-&Ftn8u8R(+jD_jO6 z&=Tx?z}AqA{uI$-3@vs1k{3HQnv|Jn*tZ53GAl+*?pM&KxwqE_M);6$#}Ut4Hs9%$ zSI*+oXziCB0GWb8+q|`nN#>JH<9-k0KTIPx@?)HTr^a(m+Oi#E~L<9qIy!Nw>Q)7&uTyRJPjDk;# zBFpxU8fIzyQnk=E$zs)+w6YmT6QiqGHMd_vf_UYdW{-4eW=D^LmeJWl==sK_s&>m+!p^=+NaQz|xbJUoKY`-;S**Tq@Pt$hGZr*+!Tis^vc% zI`MGBV8R0o?j_%Y89ojr zl1M6AA;C0sP0?T)q38Y3a9hl6zNzrDg!#ik8N_ZTPoJ?!d1DGb`Efo>AHE|I5@QnY zHsS(3(O!7B@-)E*&a0|cW6l#zuTtBVm6(p{mkn!-=aTsb9yqc?KPJ=os92*G_rE(? zL>AMol?FBHE$u7<&4rJ!@u{I*w17p9dD{WcARE|aNalKZ5q~~-RT6hTgi(pZ4?$!D z^Tk;NFLV^ah43Aoxf5@~F|Ow^4qu$_a|hr0_+@@gNfn7$o-xsTiI3id!aiRtl6}xc z9U^v13k&PVdMyp!0SDHW_vJ9We&UXSYCNk8hj=N&F-qs{j>v6>I-h$9ipXn1`1jwz zrR~$B?b8n!aE}=#4d_M;at3JwgklG65(bIFNQYl&ov0{!6pot;UT0KDzf(B2`h!cv zxY8FM&A}Tp@&4)SE{4E4X41LVm7PP$&?e3Ol@v^>+yIB^{y~1k00rgA3-S^F*%w1u zHiCMGymyaeGe*wy>`2QIDg)LK6XEH-uL>&0BDd9($l0}RLC}4vFfKhM7@f0u4|@D4POZwL2g&455abl`0VOnaeAQ#KU3A+pJ4p&08)Yz-1x|0qc_sUkDruX{`L4ihF zL<$mCrk`YJt%6{)swt=YQweN>wb;b8f~xn+`d-1tH5p;D*00MF%F)7MaH}ma+Vz&3 zn^;BW<#m@?tF5%p{b%KCiDHbE@nJ$9bT_FB|7A+7sSf+HQu>+vM4exV%qWmf>qpTV zOe}G{rgSrSi&AWD{ThgSX(3ryL$e91LcJ!lKyF`FM4Ku)G)D1x5}mN|BHrA3F$m>R zGU3X8V1 zR8=F8GJ&Im6A|lH?1O-GQT)k3F_YrClJ#*{5bvfyPzXKoQV0gcGVRxrOoFC#cDHf* z=Lt(bpeLCa#fn3$1gMhFwM?$xF-8SeQPxWz00r8+)kfvg(v+rdopopQMw+-_tw zk5|x`9FkWN6iQmC(->q@mMYew=$7Ci(pU&V^5oJTpu$0Vg(3FG|h8>R2wqyY{q7Orl|OVv$xw#(Xxc+%X@c`q*SC zT3UGc@My+Z>R0|(Qs#nRd|82H(I!sYr9KF1xj)J>HB#@mL5}gAzFBf+c3c$Oz^9_| zQxrOxQZ5G>T~D@hZc(^QB_k3buYoNWPS}ni?PAiIAql*2=QO3!u1KmEmJnNfzzb87 z`($A|vTOUg$sY_$f>{f%aPZrQ_2C32_ zTx(rXxMIGf^uW_Aup+vip>^E{-gebKYXijXxm1E7WucWi`$qA?xA`ZfnK8Lbmm2WP zant?QMYit1Ovd~uJ=AM?X(t^CxF~!vb(6hqc7aDiG{|_ZqCy| zd{unmV-B8>X6}vZa2U+`KaF_bV0eyXfnjsb?nmoKobB zB2795LWU)Z>Mt02Nh|teyLJ79(@rJ>$F zy{p}`+PhWBM-!7t_wrZRq|`7qiPi+!e>%~Akkx9MCwj=~%vA{!{ieQh=$3|%A8_*V zN7vBs$x?Wh%VGc0+P5%RKOy~6!MF54WHAgK+EDE#Q2kIj-s*5d8*f{hZe#m85WO6V zApFuGZW`Ev6UlCzOiN_sj4}Nc&7v48@{PlI@Uag{=F_iiPv7#OB5MgT7m>d}NAQ*$ zz1H{U_hZN!%hMI`LBNKDyZQSUJ)R4pA4wHuZ6Y$Wma`BgU2FD=OES9^4$!s5p2(-~^Csu_Pg5e&?U ziC<4huC)iMDiw(Cn7m4c{R#Da+<9^TQD5dxK5{ke$d>j<)C61zPt?@|Ux`m!dz-xx zCc3~Sv(SOBBB$Tvt^7!oNz5U&)8oJ!(iw1nO|TY2;*5OL#g6P)D&m|N=oGG6xC|8; z&rY4SB;qWp$WJPE0fVmSRi9uDSD^1xe{@eGIb`c@(ui*MZ1CU%S8stayt^rW>59~u zG~`+s8D>PAo0wd(XRRoxyJk&AnI)+bEBPV+o72q&<{&@c8HRIB8un2aPJXV)=k%%q z*R#K;XMVUU`H73vGpQ0$JxG;cuW$h1wgH^puXM}7Ba8-b#F@)U)(n?q8#dm~mQ)Ov zbd&()P!oj^moRWxbc;)KwBHdti}R@n&Uc)HEYeBhJE9Wt5UOI>8HFUrj5o`GH_Jp0 z#|Sh#{~}bw=@1}v%%zwhp`Bakm`|M|6u@NUl?HIlE?2!|#OHhbDen!gV znSrC*FPSsiex+pB$Q43OXC!5tJGt$ode?a%F7#xPCf_Y7+Dlrvn%-E;%u(BZLUU8k z_6^usV%_jOMHij0S7q*Y)K?m3wHX45)9Vawia(U{&qwWLe?!bCH+LtB9^F2I)wK|( zFt!zjj_YqDwVE^9?GfOP{<3cpqfgZEi_O$9_t86D)TGxZn-4zhUrf?}F|YRiky(F< znjrQ1t5YLeIaBVKHlr%`yY)4?gi?mJLX(HLJxml(2FN2BWTFSG6$1Tl^AwZsv-}fj)$IrxNBH{LJ|rN@HT5pi`&O*oVsnZ`vTlOz3^W7Z+V5!4p&_{<8lWRT zlMK*d=6za=dH7bY{ZVsBDNJ<0Xf+rtQha4^%O(%Y>0FMzbmt0X~Tw z0rUj1egt|mur%Tu#3j7pH3hw5s^Q-XI4t@AcL?vBeKUPND1CUn@e1JYer-o7u5&H< z>p@-NQ>$!0P+M%x&A95aUHMr)R^44CfBAiT_xZ^STPOleB~1M?%M~n)-LZC!hW$5d z-MdO3^htA7a8Y5tNlBu`cT^SdXXUfk+v}^Y=WAcwn>(o|TV!{p#E#3ys?^SGv2_OV zJiWwf=-8+ac!-{C0=W zI5b*&jCn27dl=7(T%tGic2anyS#if>B=G|!`0Sr#PD|z4q$Q0hjOE;zDzcweT>yDl z7d+Y?a9@W~nz)^+^q~e(Gu1cze+HaJPoc`+zo*%C%>M~pll*_9Yk5ZtTW1r;|HjkB znjZQ~!%u$9>(jo-KqN3AM8bd~f^CTMfb%j9gw$Ev>DvH+EOg^oXo=fxo%^>Vo1V*B zO{zTQt*Wq8PZdvR`?by0s#?v}D><50%fGdII{ni5u}#v6aE3pA+MV&T)8+ASGVM+N zHm@efBO>f{tQH6tP0nRDx@r^NRLUl{G7U%GY4%c5pwpOzG_OvTXrF+f+(Fny$?lx9p=y~et*yQ1)i)j+!WSk1 zBhAnL)Zc!%)0omKn)y9Tr_heS-dveBnkeftnG*7<2-yg`2vUT(m>7wvd_-QLy_|CV zEfiMHQUh!Ed`+ylp!_favnqNOr(yyv<%;S~E7sqW@!8nEKMeV&Oc$f9&!MG#ZYAfJ^>{*M2j`P&301ms zYHvqz*seHd7yCCDqL@Z1knGX|Cpj!uynz?-N@5rp$C0Bewo$lhWMwQ;6iT;BD7hZE z!VzQY@gKvlMX8!mkrys{=z%&KvxM94JK3H`K_Xl0jO~817B~>@B(7I(lalvP1-T&N zjS%<3DELjeH}lu^v5T6|?E;B*zPq`|7r;-k8ni4Hlu1 z6y-MBSGEIUre3L85+l~Z4P?8%$qSl1N7$;16eVfqi>>c`cDvLrw!R61Z)KiE9=s)% zHXE2vZ|)P>X45e&QEKxsYC~ppdSY;qB-x(F;cRE0=&!;u+!umEan%}Lhz3H#FP52W z(nh;Su}ZTD^kJ1!mIK+`nRAv({E`+$$||_R@?(WV*0}1Z#Zp*h-Tl;Ibs(Y&E^qu)LRd;Hp`}9G-mIKA|`ySvGmyfCf z$k|xti%#;-_x+{H1|plnay9JSJlRarDqjkS*uC`B*s|_v=*)d_Z4LARBGqawe#>zzyx!>hpx!mM>Ea4?K&iMv-e)HSvHi5u{-iOBYhzaTA(;4H)L3RF`S+hv^EGm3IMv;` zDKuk<74+qR_SdxZyOG3t=Zvcn`eis7dPB}SSgI*gryQO_O1)ew(`v2(c~})kkS~d4 z2H9qGkcI8_N2b>F%E08&jiGi|Z-ge{rNxSa^9a`{w^EpRl5XsWv;kx8K2Vi&m`mcM zWd|P5hHDfB=^UBaJ%nJOKDKsjho8=HyVZD$gL z6=O-=V5V>{-R*arE>Jp!*@1p|E5P1~eW_>TtG%sP=mx3PG%hwhYZ)ib?(F>7jAwS7!LkM(>!6pbg9Ld?AZq?|HTfKowvCY$3>8w5*8%mBO z@t9&sW}0@RcU4?rH*p-^;9XdK!|p@3%EtjtKxUIfVs_3c{O^`8jzHX4Eeh2;Z^JjW z1z_4-)ZIL@XMpb*$w`NY^O~zGz0A?*9Y(5GhLFgv=>TGK^L%-*FFvt|Ajv(25Z zSA_E&6nEI&({?hw=Z~Id@a;#BzZb9~Ty7q@3y&EJ+>TL(?~uF0Vju3egZWGZt(H>f z@{VTz-Qt{uHM0bLP@0?0RNdriKl|%E^Jz)p-l2>%L%%0EGzjN0Kj7anJ+Ye25C(n3 zK#zRF(l*xI;Qbwsy3+HBxH|aNR~LCK^MYTs5Syp@CdN`yjAaAkR|*GE-GB1MQJjE8zk8Ad_t0-aunx-ZzC~GI;;JVAwD+-BRG={)q2m!!YFbx0o!(QZ)}z@1K|- z0&cxM7Pt385f;#Q6I9O|<|tV@zKz}edxH`3uzX%g%T|GSo;_g?rQgT+cs5qY8B8hy zCZiQV-ZI?O6wImh#od#h3yfDVnTYkUx*b-KANu9fn>)97hHwXa90d4>_z;1i!!s;H zESzCI_*q$I;k;RVQ>gcxT$`v!oUMJRqk*?3)-Tq`obOvFY(8>dt4y;7-AV0T@W!z? zVaC*>%G&GvT>GS{*Q#{i)?5-*(2o!x)?=MuW56*xV@CQU!smynAH8Ha%f4L;D&;cby4hX>88#fs=GKh8YI zgCy&tc^HidnbY`%%*=ctII=pLhi_j90vn=pPl7?d6CAFa=3&DZVrPnbq@D4Fa10zZ z?>I1oEf&_n=-7d{{A(MJ>;hc1?pi(C={;eNG^2V_(q;6FINcKFWU4 z>V&RkIGH>Zp=A+QxgoohkiM)!By}OV-!Xa?d_7y^XdB7JOL4Wv2&$efrx`6pf^IKOX?CYMDcl`KU!p*rbDr{3N zrlK>fe}7JI2QC+mduY$eI`+h-l3Z()7Y#3>X`tGHU-rO=<=TM#?{i=gYx70_t?F!`wVjSIAKZZ)Zq+ML;UL}mWNd<q9fjj4Jd513Naz`8k+q3J7T$)Y5yvL$$-mm}b^x8=k71+rK4zAQO4%y?Up}%Z@yjn6 z_9KD6#_Os3PBy6wMXr3Q-$cMg3+-_T35G25&2iYh^kKhwX^iRY-a@H+iZ&yKih&rf zzd=lDWzI8bwUV0-Wuwd4v;z6VXT7xPW8|AUC(wGrE~(2Fm4XGI14@r)kKg z$|XM}m`1IT+_0~7TA?L+v1Pi{__~ozuFv8S%mPFmQ((4jZCzXO(bam!Y#QUPYQm)R zMtkXlkM6E5&@9c7e`2`wl(+P&bY@3okSZNTb+@Uvy*KBAC8Cw|6tiqSI$9X1BStt1 z(XTg5AYiF_j{@w7_{Ew@0z}YmOp!hy1;Dj z^~(8_EJ6Zj->YkmX4)FHb#DwtbXshE&f{7;YZmn(DSf_U?JD|qw*MA{J*MfE{E~oV z0|~@6@e}J{VR%}h@1N?}WyW2a3spoiAMIC6#h%gp^h;JuIOd)S-)?SmDjTR@jI>v0 z5(l~7I=cBPn5UIsG6BO=G&>vhR%5f7VJ{UKM+1Oa1;B}Z1S$RQ`$nUBZ5 zaFdTme1#b0ws5wrH0TEEDq}Ok5=k7YSaWTrNvYS74ZH5*;2BRi_h|k~YU9;IlVajUGb*kkxxlhK zW_!-3!l5dI9IYWqEMWL9ICb#M-}tMvh@u?41wPdLbcw$ub_ws1hwVlvwMl){oaeb| zG-j_=V$~~Ja1x{FIRp0^fD)|)mP}TLBIlevVhV4G2+}xLnVc)4E<4P4hNp|9d+c9+ z<8J}}YSFZ~S9}cQr<~Q*NOEJc9)u?pGJcKmIIEj5_=H*{Y1wQ+c~HM>^bw}I5Uu`u zJH%UkZdc^8XuE3ZUlS;>7HOe{QR7Wu9#w(tK9&cIW}I%}((~Z108Co5j4%<_TDrd` zkI4bn{8CVgAuul*VWbP|R0@I*J@V5d{#?iVY@ZqH_k!&;Dt8e5*L$=7Ugy9w8e4RN zR4+2lbyMwzl9OE}vh4 zp9aEt+!*$Gz@KUzV+u@EEWgSB4SQZX`+S@8uT_5$;YNT@&p& zo{>umljs<1WAa$lw4OS)Eu8fsX%3tjsm@60d;n@mm*=@a>B2EV`se zZ3uj_Y?t4VJH&Tu#*`nPTM7;njP5_16l}}*?!w(J2E(;J%Fx{A-M&sh>&xL~KL`Ho zhV$KrPnRz0N_2%?G)r`$B)2m@EQRBCNql(7|!%T&Mh~%=fv@^vFc;?Z~Rr7I3@?b z#5Rkl45qe(#Iyu$T6?^(W&N_R-v4s%I=rAAuBxIwRo>5fr{=E8lU14tiq7i)&S9_i zG$ncOiqrl3d~e0?96ge#c$N9E4Y?Z?;ydK51&J!(A-GTJd zfT`G*`P)lH8<#~W>KDaPv@&5$2Igf?KG_I4bhJ{_&v-76SR{6kfOE@Zh70 zaMHx9dl|$Hb?#$43!{4Q#25HTs>b^vtU^Qrk{uN~wjUjJPTs}PJa|gRC;BW*iJdK9 zzqVW3ddl`B{47u*)SKfR^*c5kO>-{UrCW<#{CAw}OmZf0<~)xvGcd(qPp?B491p99 zw$1=w-MMLd`WGwN{zzpURbI*bAT*p5Eo!c{=!IsK;Yr_T!h;~Df-${J%pniAJ_i7D zWW!SDE`5w>2y;bPqBWiRM=^oJ(-&)ovaDwSiuW?jKt!k*9mu?TKriz(b zdyavzkBkrQ$eF~oP`qyFXeGYQ=y3JJs5O0F)8GtU4Qqy_R*=Prie^1{BLkR31oF&{ zapEUB%KjO~O=l75^kdqESVWSiOflwG(b68MROE!u!DFVFSffa163yhr+29pK^OAY)1hFd!1 z;Ezrb=)VnU0+XPwM`u=!0RF5%w*oT(5Tf9gh!OmxL4%G477{T_M8M{2N67bSP!#$* z0IZ=@sX<8GKO?yHyU?j1DX9~nRB1s7+=>y?{z1uq;M2>8D*UAZ*~sbDLmB=-DSx23 z#Uh-*RFb(-?90({z)EQy(DDHhQQ!;7ZJ3HRL;L>60B!K}f+02k41hLdddZNB|0=*5 zW=d&CqEDN`7UH{!9W-I-5QG0S;2*R`{t%IW2;dBguy#ntpDeiz#=aQ+5geQ58ce?) z9R)m<`kHx*nu7Qny_zA;mN^A63}Mlbt-n?T6*vcgAE{hz=hx31^jUCz06$8(`i^)X zD}^ttx#G?!SRFbOWUSgwng2eZ3*w6A8sTRVdL6_Y;0+#CaR=#V5_&HwG01yHlKnx{C%L5m$e53!fE>Q(6D>)nf zPxT&qA2vl9WD6}1hJ9pWBX}7t4~~6iVi>r*x+C}&A_WRecEw&o|1vAD%=mEh-(>#C zYio))Q+`QluHAOPGHY&GaoDvtMH-84esS8h2t^v3Zm}`hE>|)lvu^dCLv$K7o^hAz zIQGb7vi7)pdty!Wrm7>-E*5}z*1aUrCR&{a&$LT(+;zk%Iht9w%9v``1t5V}r|w9$ z>q^myXQ#+I<6e|V#pWwBPQEJ#cwqI_8lR5lqH9la2umzu-6-5Uj&7y(pxm_q*cx@| zj6308D&Fvq@TGM5JCr5ju=q-jN8w#6-zblCrgUXr6I0abcd3t8;a;lT;E%kfbj4nS zQ?#-PmhZ(xqtJL@?7F3NWn3Fj*co_>jsL|ZQM*AN5u|Wqy->bk92rgS3cGfoxHR$> z8Hd4jqkBf(eN6UFzm}#TG4TeB>qINiJ#+7}0WM6vf#Xzoy>)vF(X%wq^t&j@-m%vn z6nSRe!s9r2da5^J{bBoYGBQ)}jCu#~Grc)4ZUrCb$~R@&{rhwsfs=YiUx_6w?GJ#u z{c~vT)(zJe*s6cuS~C1z{h_Pnqpsznp!J^A@`AH)vMF0dk`5GNuknTABe~_Fz2o5( z$Vz&`>C0>gv4 z@p!x6I7I zW*MtMHH)gspsIzFv=WXw*iwc%U94)Es_Oo~X!Tff0sH+C7Bhqv2%+lV(pu>C(m6eT z$;eHwHK}MqaNNkH5GMd}%8>$j7Bqo67^?Xdf;TKDKt=6bJu4@Bq}J zr{izWAs$h;Hn zZ7oA%L!H6o1T_*=KyvIbsjLlGkLK?j(0Uy3xLOz*_|-I_|FZfz5p76ck`6sL zeGfeVNk_kw@aX_FGYK?Bd=9Kk7$QsBn{bZ;V`6;aj|`aWEI>z0(ISS<`C_^NwR!Zw zc#l@ED;GwwX%%=!7g6YKr9 zH|P2X(KP+{BR7x!(ut9i(1RC=cHSath0Gz6BUYXhM~y&Y4utNTz<9YLNB`(h2{dtM zZS^@4irSi;zJP^I@h%Xo=$%f@aVK=YVJ7Q_Nx;PSAzvcx&Q2f5#eB8hqAc(Epb@w1 zX_?l16~$1C>IQPIfVFFRe9ldU)_I%yML_Ey^!yf_aHLo}QgDgd3g;q}RS>Ji6)0yO zsWvLa{xVSzOXuAgg@`ofRJZN&z*B&xNcR|q4l#30*hX39JUlvqWk#m6IH|hwX2G_D zA@-u^HG#O##4`qDz?&f{MOLG$i(_tUp*qWe)IVbQ4Fv((`^5^9?v&s#IP}G;>QsB0 zyIFqnL<^seg;{vb&BsQxSvEM8V|hg$sz_nL8ce+%8RDtI6V6++)}>ic)jnv}FEuym zjUVO2xDpa4Un01ay0XqPVGd8}>_=t-(M$-qpTHSZ@mYwmC6+6@ZW5^(5%Gq;nBYqm zIh?PGKR8Ku$kKPLhgfY`HMQaXCHZP2cE*!60qf|y1+P04i-OBR6dL)cAh~I_)2vLs z^z^U4ahQi`q+E1#FmfdQz>{n=0ZeG9eElN04~&kc*uq&1b>#p;m3_PPV-=c)C6~mN zKeQ`9E=vwn7ed%@B}OGO+*cDj1Lz9Bva}Hzeugg zXR~hqW@0_-YNbJ-MwsCoH%o67SE z)kJ=Y$4zNQxV5%gH-Yop!0`%c zh66LVIy3=oePPL`mus)r#~4 zW)swqU0Yfhxhy$8TT<()eM|eMX_*Pa4+U!8MvlBJH5gIKQs7of<*Z=ixedxLY0!sR1PhTt|;-~yiH?4FC1>hULg}D$m=Uar{7q-%eNto@qHba z$PCLhZi;O0tbtR`s`jxSBfp*AycTgY=}IR+zIjC7$I@6lN!DI7GkNR4KwBoMC~P(6 zzN)f{7bHr>8g4NcMBX9ZkgBV*-UY$NlwgUxjo@Nw?MM%}E|NWGes(o~pMe{GMb5(V zZHa!6X5oR$8re2m!tX&}`gtd{Z^A(ukV;pVgU-M^t_~n_sn*@Gg5B?q?8rV#ysVUU=G+Fh&AKi!)8rDhPh(`3cjZUi`^=E{x zzGH4xGw6C5LvEz6IYVwr=C>l3yVd@e70FpRXNFh62vb~>+B}_!57bFUgS3#R)OU&z z?Fa{iP7%X5BdE=9uq2ZdJ#+@8;S>_}>SQct>h)G4Em5|4tL7XcMLr$1V&W0mgsHEZe$Xu*su!m!TnRlxGcsJ$ze|&Ld6E&wApYPcG0+ za>YD^ynYsXGY9JJTXvrr&^Mfvo{fz3X$pC%u8okLvCzMNpRsRF<|b^K=ssk>&D=NO?70P`b(8m+d92dicy*+8)ArtWow*ntT2B0q z$t>k$T2OQ~$Ssi<)u)+L^edrTU@l~-Q60ODM|%~(tN#q3_f9?If1k?{O3ryKxOHCT zx%`R~ay}zwJnYSm6Qc|SAMn=N<6S!p(fpafsgsC`x#vFc&j}-ZJ__pnsOtSLt+9gv z=V&l|{d46<_1l|gLdpq35Gv-qYNr0a4{ViAz{!uOonkgKY}WO+j#Nzvw$7GKP|1r$ zdfe?CD_f@Vsn0(NiKTwG!TVQ>wl$<(K?yw24N(0u9MM&*X(mBjY0uw4sXi<+WUNP0 zY&!q6@`u+wJXOa9pxO@iU%ix!a}2CH%ld#K7Gh`CUIki*HF9N4wLBoumbQxZecrnZ zr-`?{c7t&7Y6Evoc+Z2E$(WtITV@TBB2mSgP}(Jt?$zZs71W$SKPMV z6c?Ae=@#>kcyOMV8YaTM*8pu)>KEFjiRA_=(37dPdT_*Wz@l5WLYCS}RZS1*DJ+I; z;aF=FIIx2?|7{f%z1HS7>{FRO8x`t$bv_AR9^zgGBvasqKB0HOsR*b zjU*`r&sz=fr*!-1O;&eqhL{j8qYb8uct?MVdyQn#K~U$OGNy;Ad;4k69xOHSg5_c* z3n_q>55<;(*R}Xs%&Z#u*S9Y1xbpbF#!3%8`^YqTQ9~hl;U4jYMXVL@q&XPoi&De1 z8JI3ki3&*P#I7*-UoQikQzv7&?<*sTbGE@PR55PzcEfync&MqsTUKtbD=hVORn)a6 zr049w4z+tKnA_z695wY5xXI3zcZaK!Ot|AEC@L4jiFNqM!(_s|Jgz$K&hqaoqgDIY z;AfY{R0Aj1(Jzvd=SYqgv&~)dYfjw{)!;@Eau_~unB{^s$PRs@S%%|@EGtpzTA^Zn zv+Co7&8c-G%zXorsJ=bGZ-%jmy>@08UOebN2`SK*P2j;yLtM(QFHa~huJ_v+L&=$gn6rB;M{gZee=`9Kx03=m;qV_x3iY>FYDLan60_x6Pk z(mE67Q!FJ76MFKKdg!rw_*+MV5u7kwn(|sUva`TI!{-weOO)ycr5yXUAYTY*NU*$kU+34iys)9*P4|aM>NUNW2 zORRX-0swkN(cYOuSn-vCmzN|h{2vh)*?WPa80$5^i}#))SCN;vf{K%ZtV&c?F2Ib{ z&2V7UtHk6KR*K3Ce|LTFH!T_j?12~^)`jCRUa%?=+%f;t$4f(~2vR;T2 zG)kYJki)Ig9rMrie=$Wkb|Vp6BQC2!zk8?AmOlA7E_{2xK!FqfM(zG`;!nG0>AO2~ zS(g?Mud?1R?7E$OHV1IO*%JNYnb%ekycUZp(ubvKSa(aj%5~myubZSg2RStEJ{zh&Me%jo+ zXqcRoL2#szQB{l`Gos4BlOGrlsWi=v7mV(Ut6W8 z0|PCTq~%?{y3a`W2~lWiUagB|j$GJG(#6HT%oX&akF{1FD==Ak3c=z20o3)<&{A(N ztnaCPQN4fhsz`mo*C^nJ-~Svj@6JQ;i+zZ@*)yv^v`uMvZscc!2Bl0-;?KQ_+wxb} zSm~mvzIF#Dm!4EYSXu^nE+P*EZG#f)<#J{e>fpv+X>S%aAm>x1rPe@J-UD=QNvCGe zE9pLBXhjV?XjNNS_E0hH0j2qRPy0OXRY#?4(1&6K823K`8yO#cV!!DZ-C~;xUnJ^| z@aWL@VoIih!g#_Zc?SVO;2!IucZoRO55tk{JYE<0;dWc|`KZFs70E!lI3!6;P$Lc}%->FjUZlXwErW0V@ zra?pg6Fmzzw3>cx5w>UtCs@=?P^Kx(`vtT9h+|Bw;=t@N)Iq#@kvv>DArMs6U6j&Y zcCaXwhz=n>$u|R>GF-aYgz^z#$^06Auq^zzri3~ijJs(<=^=TU3^Zv<3 zv4U)XaM3qOwOCAb!mzYPIuWMEYxJ7ANp1HZIp|ednG20hw?02M$CD;a4-c`8gJ$ur zT%e65u(@*%Fq2c&$K*!`@#Ep0aW#wUo>ImjJ$k7FcL1W9c#>Ck<4VO`pT8A4OO}4cd$pZan**H6klAy5VWaU$pff~ z(iN^{XiHxBQ)fBs6KuQZsrkfqW%R5j^6X~24%=m&S@?bGDADb8s+s=q`L~9|$oYTX zIJhR_b#E9>w`OTL$(WLJ&H+k!e4FeGZ}JLGXgeapEK{A_T)oB}3iQCz_PphB zJOq@_54cWc`r3U#w6-brmz+XKK(5iDJd`yg2ai8|J4b>JRMq84^dP#6c|K0l6&lwJ zD}+iIc)lS=s_W~hbACLJ);|UBQQiTH#2r=GJ;CQV=Z0lxZ3sjr54F4_?0n%zP|ct8 z#PM$BzACvVhoT-G**ga9Ri5GNQ&;}r-uZ-AxJ5?3O9Ca#p{g!;(q{(jZMQN1>>qnH zH5Fbx+yt(E3B@fb%(CIXS~13jBxs(Tq`OCe?mkzq1A-EJwq*zuSd0O`>vC?<6{_scA3Z1;*tQ^~|W?L%o6z;;KN<=Lf1D zmf*G0QMeMR8ijX~y7!NTK1z#sd(mUi!xXYE`6qhbNTeUU@vZc>Tl+l+#}U&lXWdU* zl1*iGExiyEG8u^&KYLF}ZKe229KGR4rf)Z&w(WgOgyjf1(<8|tb^e;33Z0T|zH}=k zP9kCzmj3n6wNZ5SFVIKMs>tl&+`q(ed+3ydR6xXaEZJF}|RiOz{@ zUHR7pbk3ogFm%p!&8gbgxVvB$E7Do_o7O5d}54vqe2N(Udkd+{iliK1^ z`DdHSUYAqK@7zjA8N2~=#_(ATh>{`$gm6?(V7a?Al^i5w2d?zc( zDK#PAJIn8i4nF2HHrSbO!`O<>A{et6#yf)nS7VTn&!<5=p2M>L76J(nw-(Mr9-26oHJ?lIm?lpYu|e@ zX3UGGr8hWvseHuTgX2}4J6nV@ty&f_j4u;M zPUGexlImn6WZExTa76M_JE6#^c(?q8E2OCGW%2x{5eK8x+6w$ZD) z!=TCkWm~s`^6H9pPwt6cnuBemAI=K0jc0>a=8f+!m~L`#ags10w_?~a*ZHMcia;vD z)s?4rgx3~Z4i^Ww%br_E99Om>&U`j$HJoehQN&y9T-C{1ziGp52jQRD7Cm5=pK>%> z{rWY#5UkE@s|&n-%U^_?+m<;r1yL#yR9ENRm{Uy`>!^x6g7`b~ut7nX#bM?Y0*K-Q z5?03!Yf=hx;!R;Kb4kt^0wH+0(4l4)TmlNSa|m~|-BNyKX~$%qArA|>FA=&XoZTay zbEotf4-El#lmrrX^$~Y1tm!6-uUz>D!WwST?Edk?yT_y^*EiC&mbJOW(CQ)r0)bRA z0s@g#S^@&ljkk(FWME1h{3#pz^SO|f_4A+OOlvk^wC82hf~{$tQ0~)apZ0`X;1ss& zWW9HcO7VfSy3!L1aiB#I2LT?9)ltQvei_GyDNu>0D*P<*fTH@$%dK9G_h4BC%w z_EVb+h%W)~$D8a~AEMwdO7*D)(k>cWwW{NbNlHw)#L6coccf6o8T?6$OnT;tf5C%Q zuj_Snw``*{X-H$7OJUAkP{#7?1~?U|nB@o4^CPAlJ#i6!$RW z3+K6fr=G@DR9ID(DEf|dr8@Nq|l!Z>U(M3=sz;7j9fM5)>HI&AR3Via?hmF1jxDBsF+by9>79^ z5uF=Z=EjxINE3!Xun|JS?&=XwMp6p95E6N*6x9XnIn!C*po%8^mzx3$4$Z0`6!jvLC^&7UlCNT?EF?Gg)H@cg15dQ*O7ibRErlJ zTS;P9gQvO>;wDQp#F{as9>anqOLFp$^x_!yyk$%JawCN^xE3I-5zrB$*1p$1op8uW z!BUzl!f2J%*wdOZHfy|XzSoXrS8TTV_(|q5;e4fG(?`?s4%=*7;#;|*eHt@|YfwI7$)@H72(o5Qzv-zZwOU!8cx-WJ9G2Y~vzA=eapiF){u^$h1MEUP)>U05t~Geg{_>z^x3R*9K8&qsX*@ zr`nUzZ<}fiFxXqvI8%0}FdDWlU_Y331gs0UHt-$my5gK0JkCv8!=OKKdZH}&Ot{WJ zTI2kC@u^1ni`0E#D7q?Yjp>!X|64M-d5WCrCLtGALpNn+)_MLpgS4|hsh)rzud`o; zIwx;j;(J7zE89HHKdP(&9e}sj4}v97!D+Aoii?p#hvnZ3Q`^ujEch4~N;KTPukZ0m!&lCU?CNc!CVJs*~?kZ!bz+&XUU1YAuCq1sm)AU!Qsdx3M_g49X zT>XRG@I!wHGbS8L3vT_sPBBn2a(Itk-%TdD4U`_&T-l*(uAoA zAVt*r*8VZRG3t#H;WL2=!^;KS2XHx)SN(Dl5ZswjrGB0w$16_GgrG}F%PFD63aP|t zTjvZ7>2e0TA=#wl-tyFJ2+w$e173u+sp-j;EAS7MYBq!8`E8E zAW+Y5R~|vNSDR%;n7jy}So#Hu91x^|ky1QNBn@94Q&jQZm|y9@s5YM6 z_G$j52vShrU~~7=418b}j0fJbkEVw$ zK$Q^E+;Je+S;Ze&>{knSw2F8-0QcSRWskG=srb9sX7?mJvbhm>94z&P1?G4~&1Stt zB~1CW5=!PPFYtb$s95pwwWs8z-@{fdp#uNT$Xo0$fLp9ZrhrMQ5NNH>EH~dMfz@*L z-P>3j@$prqltH-1g6A%bFq)h7+U@ItF|qW?to_+Px3;2t#qX;^ysG4Mwc+b5Na;q} zl7n_vA%GO{vo%owcMk*K0Da9~G2_!*?FMTh(Dx$vyFNo~X|iH?EN^ZUXJ?XD@wG>s z6Y~!|SeMY>{S6WtHq1^jMhZK49Yc>0$O$;{K*U|M7H-aKYFIt`tiJf9|8QRZtXBMF zzw^_5^dt5wd=P>*WPZ00dg(2H?F|=FdW{PS7I`O;_)K&8OuMGC{6s_5E&m)o>Q#T- zjQUSH7*_N>E&tQ1wV2OE!f4Qz5nG- zD9lMB?d!{azg=VwMj|)(gv*;5S5onYJ8`&M9!(;1`|PgSPo*f3Cw2NvzBWa*P~r`5 zv#(N-LL%vQW8o|`TRQ5F+&$%Ve90g8aP9pPtx#t7jQA2VYmCjL?<*KYakcx4mdRMu z9gUzrYE?sbfO3~7EUypVgNE+V#2SssOsy+$X{$Y9)}-tjW7SkJPG{(_aq-KNXTU~O-W1Fg zzF_dm4LyI{!M!)D-Im82rtTDh2anwj)wTzXUWl&MlV=aG<}8dR!8adspPBw875aB? z1C`Y80#aN8h(DWu*!-tm^!NR{$8#ai2w__Ae)l%wfLXce{{{C#IL`P9jTYzw`{(Z& z7P`F^^oSSu1F{#^Z#-mrl;3U%hH%F^BDRPMr9FoPs!BoZ@*i*WZS{mn%fo=vco9De;j8;vebiI#F%3>diec>!!}+Z&1I`^!DR0QXe_pRc=0A9U2n2Q9#xNEO5Ecvk z+=GEXt~uj`si0IQ6)24H!4wj}pP&Be2`rNm0!m$)p@e)@n7T3f3Se$T&BBlK*c+5E~ zi#@89XzLvbVH^?08C-m(pIfT;46;qNV!|2*dlg?ku?#CB+c>SD>Mcm}SqvC^OaF)K z{IE33{(6!@TWO{-^N`EGL*ScEU)>6TwGBn+3E6R{qqxKtfY?L<)|5+krBNhfP^Da$i{*gDlYlbwG%Q?J%H(NA1X^K?B zIynd-<{oLKwA9LkHw?Rera-*9(GHt{w`t4waL%w2)L1qs9HOgjCmdzh3AOEDdJ~LH zqnJVn^tzOsY~DwzsFl9b(@E8j(cu_Q1>vf?=d)$Fo`qiyHv@&8TT>y zrm*;CO8%U2Fne&#DDp}22p?-R7x9p}hRcBthekTdy@|H9lNCeqpd)MD&dTa=inA%ecAR5`$_sD zng*Fa8Q>f&Rdj8LiWT0^SU`9g@5e7R5%1SUmR!KU9{-XWQO=S~-n4m-s z!@t^tQX7~nk_1DZb?<}iM*O_nt9Fq%9tp4T?Q`!yXj#_7qVX2+VWvehsi?bGngmD@ zyRzd}M>8%pe6y;JaQVGePcvq@R>vjtn68u^*V{LbbMF_?Fz!g$Nees*QF*rfn~X&n z%%A&>=B3nul~ESC)CIIGS(Za^O@L$(k^QD$s&mhV$U2k<^H_$&>pu<_Y<9@k7_pT* zHDu!=DejtJ=bQ=?b+Lp!CFzTph8{d{?P0U>cFYxZ9}TjYI(o9iN_&onf=mMC*W!#T;*4b#p!d@kqGD+_KnpVa7`&_6ZXZ1Ty4! zo5UZAY7WOKlZe9!)qQ5)9E$peyl+M@!MDM|9e%U;yOlc|k;{K(fkrf2IB7@%N3NC+ z8d)ysSvs^d#5tqj*ilsFV%ywN@5hm5x&T6P4$^(DsOWGzJ0KfI-L)rbNIUGm?b%5} zeCJ2cpZKTjcCg<^EAhCr!RQ`+^vYFLi&Y5bJCFGvhKhc0<-M2#?~vua(eT6jO^VhI z!=vKJI8lU**cD!4+5#Ce?(Q!lB*V8QUGw%2?bm_Ly&;Wr=hVX*iA22KJN<*&ibwW3 z0BsnFI+!9Ze-HthgKeSpHB}~~Z7GHr9Rq6X0Mhz_BUv-@;Qv~v!KxizVZ0o{vt!Qy zp&d^8?)kowhQc1*;9G_zOmrxD{FoK8p%OBK>fE+X7wJir*hV>u7jf5o_U)DF`LtW; zE0q?;x$$ei12M}?)}GwWKdCRJM(GU@hR?;9?3g8qj@tcrPgsQum7PV&D%j-o9iqR+ z`B3^^I%f~1;Du*oSmIUmfRhZ}urc}QSd1%v@P+p~@PzdB_WnDcb7MbF$7=kTf;ttN z?y_h8D(ZJ8MbF9~7m#A%wz~$Eel9Nz7oqc)!c*;O^TV$Aq^@&N)TB5i?h${a_YH9v z&>xk8{=_^v2^r1?IVZS>+hU=RZSadcMoAw3y2d@nid)FqfqPLrE2tY%xeIa+b-1QK zWz0468IC%Gy+$RV)HTHz(oLn)HNo29Xp!wA=6x^asu0TSNABC+0Guwm7ARx}J0V~E z^BPK$w)o@N6OmUj)1P)b9WL)Eo-;hlNq54!JL8DlTiCGn^xQj;)eRT%PBgj4zzgV# z(Z51Y9q(kcOWmc9A~gH8cZKj2GLiKjn`cF>Z8u+MRA?%g(HmQi@^djvisXUR>J1j} zYLp%5y%SkH$}L7zB$beG_v6D$bb>83d8FLx3SC;Ul-0TW5VhepTGr&?ojrbuc9Qe2 zX3gFT-NT%l6Yop@Y~SeN*iNs%e{XSup->El9_fUysMC$gz7GSxxCR)!Y*sYv8A{-8F)_z)#5Sf3d%DOSG~Z=!I42CrwaQ zi15oQ+a?zm_GuyP_O#kohfLroVY5k6)@n}P=6P%cNA^RN6VE8V3nl24$V!q3c1}wY zaR?D@L)5`NkJbLl=y!F4O_5kLx#Ol>RQ&HJi-Hx2EI%^GZ| zo6qczgiCfVm}lE{-=B;{@pK9eEs0Fqr@#h`yBRqNGXgy9N=+-u;{Ph`9l$GFwzc6T zopd@KTOHfBZQHgxM#r{o+qP}nwmQa_z3)9|K>uG97C!qbvBj zibL)_YE=>1=gBpn0KIOL>}3AH0Zkjs0Q0MbqQ>9hJe$jqOR8#pTFNh-SphkbPpB$m zfO%I{J}9CeR252^H=$oz2EcM;O}#^vx3tI=HG}WC3)Ew}?=S5^9%{%oYwUifK8o}~QX;9!QVvlf% zMuLjFK1geI2oW*4&MrO>a1^~Uqf-NqE4QZ^y@JE1O_(-5fu`;oQGer{*dmSS&~F@; zGbDI@9g$rA8JShZN*d;h=g0$Xz&f6|F#5KlgNlm1;QLdEx*9Q5T|}>}Bx}|zGDBUA ztn@^ERGw{A{$>G^ZtevVzV*#zF-_t`N`^}P$By};mL9L|0LKOUg5dXkop_nz+P#q3 z{i_sEtZ>zP?}@Wm^E2*B6CY@kf}r{FL)UdlPlvnKz4z>%$fp=EY;yqiZI(eDac%2r-S4O{W+ULYL-nUBzD25rz}Z;^&-SW`eJ8H0H5d&OSr z&(wxS=_4PRoL2`2Nz3Oq2qR4iqaE^U#||i$aX?Nzo>mChoSYDkl6-C$mP`eWmfSa3 z%d9UDp6>-^J60eMQfT^q9x|QpB|MzShn}DN0(iVhBA?1?yuPfNB$vcsI2UDy%VJMo z4C%dcbCkVHW1R@0nZH6n-)jM{exipZjIpvMtpypYvryG{EnL2n@xkCbIb~*@xtkGi za9q~C&qO~>w@XRmUOCgW>qz65TK;he(n9q*b}sZ@DR)TM(ji8X!DjlX?sV%%@YsPt z7hk_vNFX4079(j>5NHO!mR5EIMf7wRwObdF&Fo72vpy<*rAIv`k2xkUaftlUgN!c5 zf)>izJotk3I9^vvs2$ui&d}to#9ZjrpL=J>2P*@dF z)61B0KycM~SGTgg2tTc>-;LV##2D(z@*1uVrlF}Na!_B3z;tV=!<3Oz3N`N$)qxP5 z1vG(Jpqm}z3#<9p;{~BaSfR5le2biDP_V#N;?u~w73bZCo1zMiwD3wJ&#EN)amq}S zyDNEZWLZBAexjv5d83JZhC8rwzJ`xgidovOS(#|lE*F9fnSZd|Q1MHEB;<)>N+I>V zb_WhlQcLd_DETr06)D+x7EwtAAs@Fr$zBw}K%YwtfnwKrN}JNmCYjni#)`AY$|R6j zZK_;)S6Ij@WV!k_-{8|c`hjvg6U*|RLG>E0Siynhj8x_8j0T35I{3PMWfQ}cL}q~! zlWK_ZX@@ik2O5UCZwqTy*+!w&P)#g?^eMZ9%w0mT8p78N?$_i4sf~oeMJ~^Ww3Tu8M_eMJs+;^;PxT(riyx8cupDp7-#p zDVO~a0!?mf8!vdEgp&OOn|%{Zu9KvZ2*Wm=bbLS$=QiYtY3{}5FMA!=5hIr~$Ih0;&eVpsNYIF?J@3BMy zbR)amH=SL|C{r{V<&hl*#{xs6t-XhKr^Z=|mI3*o9x;*g(CRzE_TWZH_5IbM_1!3A z{Q;uOh@O+~vZ%8FE<^5;C2jYo*mlF#4K}B1UD{`bNEotN9_ALwPfCIS_I`w>^p%>Q z)aWl`ttX@fiIeRkXxH=<4mbplFwJJ8jR;NCiq)3qLT2nnO4QJAFTP%^lGT@N-7%>RT*TJ=c!d8I@DnD}hyI$M(r-#pCB^ zHTU#ajE>1SFDgF(V&jqW(X3ttDZ$^gIPZ5d(c&JX?r|HH7YS|L2m>rugx6`2GWe8@B@L5tz zT5nK>mHajli-ZA18$G9R9x_uMV=t%T?J$lx!Ac!GtW2jE12RmrS+9d9B3aB*qy)~N zCjln7S%+f;bJ%^1qYu6eUwsqlOV25skC7~ya%>}-A&CjkmbWDxF-g0@!#}v=d}tlm zwL#is_7<38^+>DT=RLEq6>d= z3eW3G2P-Gn52SmCsZz_6yEa^Z^~3>>djFo0&C(0*z=`0uz++_LxY8M^NIC;*{ItgtmirVcQQ46OnwClltsLQe?4x#RqxM+9THo z4+=Tadr8#7*jnK<41q-=)qH8e_i5srY2_Kj`TEf6guTW2qNIPvWZhQ0(-Z{Pvi+HO zt6Skz`lRubVC(nG-O47V7J3$Rd`(;Oft>pJmuq6&CDHnoUi5(OEoG<^vbSZHaHbYt zpY~IJe=B=0$m^hnG>M|eVY?v0epjr_d1qXiRyD>6QG|=Qu^RY3uHk)WxV~qDo7SLh zpK9lJ{;CRdO?GaqgfoPU3xthTvU(Av?(QI@I3B7!daqr~^@wP+>i6H7GKh|PW1|=W z0Ls08mE`upTlb%6>HeAICTDMLZT0_qQ*etubVSea!exAfR>~O!WR~}BLx{5Ok+(8jH`>2;d;_8Gf!9$_R*zkZXSy3qihNG9 zRy3*Fk5SkNmN+ghre_3#O>ely_KO#wRMdpa;zL=pEr1MzQQ>;WX_bujFfa`xy=E%7 zMNWCBzl^%54%XuQk`YmTAoBdcZ&n{;0vHZFQCC2 z_@oALOv&9L$QR+F^jij?!vL#N_ea;60Dq+%x?d>gyN;d#kENy6$7%K^R+fq;wkCE~ z|9dcCTCBy#vGxc9?~b4PMRjtrZMQ#t9h|q2hrzOD4tP&H&{?I4bKoFa-j>$bQ|mtW@oJxt>qq{6I~S(J#_? z^DKl7o<~*?Xn|n0lUm9?aar#O(T9g6WE1Rg&qaGChlXFkp`z5qPv>qMVnSm}qa{El zeF9~|D3oWJ&Xiz<9U(0ktY5?mW!;Wn)Nyeu?p=|M!J1Z9+8GwrGS8WyNlvK4-pvTV z0efY>qxM3zAZfx51%2=aFyA>Hx{0Ch#;q6ogdKycPxC_k9;KUvQ(klv##pSMZuC1& zwA*kQ<>(ACh0?vTs_sd?f%z<%K+pPAhye}PT(lb!vf?h3V`n%UT$OJ zZjeQca^m`HF&rL)z}Q>oWYDhXVrovM^Pn)k;K(uWt9YC(M#v1v_`wr{1{> zlwp1J#v;RNiPf*Pn$kZ;0=Epn@y_YC8^hSVVGG(?ym=h^^bm@)TJ?fQ1{tU&Bh}&W zePR_z-5`COD2R*E_v8>VvbDWSBP#_5zs`_C)(p^1QsBl6Fc>hop+U|?1slr@#3y=h6ej!G-axw$7FskJNmY2pt*yuC5>KiGja4)YL zA4(b;O44utN=4^7e){_Uu6m;NeERzQp6-m=Dd5D1BSSnEh9nCbR%^c~9Cp07YTQR# zlk+UxRyaF*?Fc}R7b8G*?1(hdk+jNRZvGQg?tXiA4w2>o(#Cu zxu!JIoX|w27T+>+Nqr~KU_ALVfoTq}Yq#Ms6rQ&&f~C5~IjM;XPAYN04JHW18o*)3 z5kc&kDMh9=kFIWA5~GwA3gE4YvX3+&|&Jt;8s6|P@VZ?7)}YstZZ z;Jqrnj-?ryl%?idpxC+IWIu%8r8^`tHfFyezOsq8JO-_(K}{sli9}?@t=%%QT-PUO z4kKdz_#0xo*s-QoO9j!-5o7{6^aL?IytHEe*q)YHL9@VckL$d7vH2h=@MXQjNo~Lh z)rTX>w+6YE^P|&G+Wo=``$#g5h_s0A%`>2LL}O}hq^5iyp60V9J_?(B|FQ>aP;QJC zgFH!9Tx{Qn%Wg83!w6FKods+$#ESeL#v=wD^BxD$4Rghe9NDr7u)*7Gjs-E+g3#)^ z8fn6my9a%*C_-zD0XvS97EK+Cb&V<>7zd>dqE%ug>ror_Za~BuddKxRLzx~GirWh{A8dgoYngO4mr3&ro|v`)gsfw_2Sw+! z^6`Bm%o4lY!(ZUHPZf6?_PwS2Y{2c-0gP3G(S@Sg!Bf(c(qk2EO=H&Dv*xLCl}j49ZBpEIcktu zh_0O*4qjO+&51H~HNKbP_U%QuybV1+R@! zpm$2_t|=%4Z0;i}XN?0)^y$7fq+6t;rJo{+v?syGyC|$1FiNGhhZ8kQN|k@FFrG!} zW^!KJQBNyC?0+q;!Ox#Jy*5XY+ER0yo~=QgU>OezfaIf;wseQc=D<^kQ}o4l9*K3CE-%a0blS1 z99F~6jW>w*VvK_xf)Q14Ie$IKSYU$#@Aa-?P%sy1<}wwYe`F)bb`9F&yxnr&S~tOK zTzy69uC%__^A)t9ZHCFIuE)a4l;IXco+2jB3Ay5tEqHj^BTmU$V($ovgM?JN%G!l7 zc>Z1yzk{&m)B7sT*WaQ}q0#8g92?qi+>2Te65b<2D7k+DAHE(%T8;@$uD#-g|5~WS zj#Q{K0G@u$$s6P$KS)`H+@k*jE;o9rPIB`{ECiH#j_etJ5#UosIbFEtiLqn~N-&5# zO;T?6#kKzfGs`m=?OY6`pauP0Y$a)XJ98t+c2ybvP)_Ha^c`_-o91Z&NwJaeN{xhh z?L-hUZq*XwQzC!vL;L3C9x;3?tszl$a7qD^HF!alzZ9HBaXSA>*9O7z1?-vaBL9Z{ zaiSOoJQ_g`{ZAA0vPJH;Wt>?%cZb4@1WAz3eO*0x9A2cB7~o&qUP^J0!JmYgc~F@} zZYN?)B!hiR%(0cavFoZWc6m$>v-!CW`6uw=5wT+}qY!hG>;f%im%b|9RWdyn6jm1$ zRnHN|ad@xfRtu%D?mP6i^@O5La7jt31s4*`vr!WAtU(w4J+;`YN?Jkk`% zd*n18jJq97+up!e9L9kjU8wU5p~Y`nB`J8MxgMF`Z&CbhBA*}OQFq4(Kcm_}Rxe_P zeXeq3WgOHC=Q#w}7j_Mm9eOrgjY9%gyb7yg0CyUO9T-mEf_zxfym(6jZuQfA@vFaX zKx5o$B%?SN4QZL)F0XBD0UNl&Fzv@KJI)e3jg&}~#3T!Tfem4w!oGG!dW5eS?0`F5 z!N0b`zdn1H+m$+c)ed3b3(V#7#iONYX~wG#&X`*0JUQIbVlUP0dUGLA=`ph(NF(ce zVH0@*jXTV!9eWA+`f3IDj0Ic5(rsS#wPIv=^ZkSG81%~w^8lRs2e;td$I&4Fng95| zty=_549xZavTiApw~&LAL2##}%03AOGmTb`RICiv&x=c@N_v2c%HfasY2c->z@r`C zm!C1&QIr&&lzaqwo0B}=R_f_VMfcNd&GsVwnf-%DaO>^uW(V1mq!*PJM2|~V7|Rs( zCMQ0;I4{R|Y1VcsH_Na#v3#$Zq%AiEqz9zmx`yG|JUmUOa_?|A%cYdI5|p)^GQSUHu;ikE%lNzm=oF!FP*(70ZkkgSRI8;&?&iv5OIuX<*h==7uUd$eUNuiW? zX$uIYpBFvBr1<-th;=7k&Zlxp-3z10d$j81mAT$E*U_Fmw|4E~e9b|+QUC4LOJm8g z3n54G=z6XObW0z3F8KhnnJvK@^~SDADq_EI!S$n$BWP{3{IT%h{B2@zzDFHUm}-0( zMDdPDaZ1r?Tf?r6cd9c%iGk_G_)Jm~N9(VNuy$EHFVuEmm}`qRc2CKdp^8@vfZEz! zzFUwze>lHV&(~d3FMp$bj-|>hZzV-{bmxI%alC<_-Rf~~AdcOga^IHMSr?G&I2#lIuFLAu+jE5va zXC|T?&6*zNQ+QjOGj%tPUW!4N=XmlUaYxlZtA&uVw-a|i`+lC zA?pJI%i+I8FnfjRK2fOx3t0!7x(zcygg#4IlU>1gISw~=cuxs~ICjhqvg6vs)%F$M zG5`gE@#cM%#=G6Z$E0SY0w47_6 zet{I8s;!B$gPhDEB^$F2qg9+0caGMP;!=&TDgtk`MH$F|ZMzCzN{U#@kaHfLe1F&v z>P4z_Lm}^G%ngp!)StIHiTcrmb1+jQF|lMWGqKnLi4O zKaQL7Mv~NUn=~|#hsB@vMr&5bUNETDmmI4FC*_b8Gi}NNK3iaqx!&gFAxo_#>}r@W zCloe7_#zE92D>{3X{N$MVOf&j4_feQvKAlk!tV`b96oj!7p{tkO{D-@6#KH*sfBHP zE?kwI*^f#AR*Yg`lNK#gsoa-sZHpYU9xoluXTib3%ruLs*L+-X&@IKlvmAg6sLEe= zs!H)iq`NrtxCkkVoB&g)CR3~zcq~TK4nL+SHF`!?gl7a}^-${+X0kUsD3fY2Q?+-ru$RNZG{QBbh25XDPQ zf?^|~&n%Z-z|6?6-o*$b^;L?KZXyyFceSeGqb>u*dS&*amW{6*xwx{iU)heYTtVjI zOAPFo4~_$|F$Beu9`-T`KvM=vQh?+3AWpnZ3wjvQbMQcSz1*Ne8JP_yKF<0a_TFx$ z>i{nD;q0ZvfX~fQxt{*Y^TA28*K7irLtwF1#k-cf3(vv>$~eIYDG{E`c&s>6b$Vb! z-nTfxpk4F@-Om6rcwC+n4BBn1%01l8<1GzUhvJ7XY`BpL2HvgrVeQyHXN-`nOF=Fdg$hM~O?(-5(4bB4}n+(lSHVa+RU$1jjNQ#F z+9=Rh<`OPAb}6V@H`z3l2UN2ygUv;UWXtoD;S^zW`kB=FBwmB3WMZl-t<|_{vSq$r zc_lStwD_=O{$kD_k0$n=^Z>E0GJIRvw#FF`y(cHP4m(XqVh@0AgO784H-*qnFUXfQ z7oU}u?-7VoTyWE<1PcAcXR@g|?3!S~T0r3s+!6N&L5&?lPTyv`jVb$4pFUIX1nweM*c z+YWFaV>j*N^Pm^2ps*W^VBQbRsFbx+l{n^xJ z&n~m|@xt>y^4>k$6i*Hn2UuxQ4O%4EEnhRAQK(!XR&@ueovr*FUM zEODd80M4{+Yp2inIHwb!j%JK$@7DWLaLetA(9^VBQKxv(@a29tbMeyD@6gmu(BxGy zs>{la9x>Vsk`jX|UKynu!)FSCMm>=5Ah)l`H)(H8&>IvbU25+8C7^Kr>Kd6t+67Igprlf;U2d0NM$ag_1G+ ztRYXRRJt2nO1vC@g-gtS?McweBhwwN6bmHG-jum%1=eLk>7(ihijj(-L zkf_-Gt>iApa^%l$%XQ%i3nR`F){;RnaW47{qHm(0B|!p5s_U^jnblr6vH?FFE8i z++g~vZIGUvpE7A(!2MKB2Z|_{Wz{lW*!lxtH%v<-5Ow5LQQ~T;*1<>^WMg_eHRd}} zx zYW@PcpDaNVKb-5l5HgMz(PA5>zcZwLWmBPSF?mHvXYsgdToiK`GQp=^dFxO)s$l8- z63bLZ%6qh-VzNmQ*b2U4S>jiEip(;3F%vMCM9W^FvYZ&mN!FxHI1-vxP_q*KT1~eB ziBaoaVpI+pX`=4{-}=wI!gc3R#qf3(wpUjTZF+!h5|fw-NF zSGB`Sjm#%DFUN4Ptv&qq&62bkl&#}M!VtZdwQFY-yn`G$Dr;0c7QAZFr$!n~VqP|=)RY;pd@(->AD~RMbEDwP zCn{-Ta1%w?a%71oSsZdp0xOtsnwtx&dyr%odB4sWg$+ZE=$uBuDX}RrQd)g}vdlqy zMtwzU&V0I2k~14@C=9l234U2O8#-x<-e2cIwk&LKHds~5hylDOFH?rIV}7mrS{XBiu~5AqWm8(L3wfwKg>HE z*G2%=s+DZEx1Yu#@HSX3^vyF8eZ_3oxi5CpLGwRA`x-L{%G;B3J&K`AggiL zlK%?Qyb*p&ekFm=KhdK93RqI$t~n6gT(#3GZ&zGYA6Y^W9660Qs2LQC5+TLVvIcF@5o=VWwKS#qIj6#NN=XBS zVM01?u$U7JE8}kWHf4yYV1p$+RnzB@gPElbYRC0%dI*eZ(q#Gh-q7{EOR`f<_;v+> zmxGn)3DxJE`c#_fw5(gZ87G)mXcW+1=dC2BgO^fxRXP zjXftxTj&?js?W%rJvBhbX|QT#Kk&9gV*OusZY%Z-2rc$UfZC8qQusBM->?qb>80o= zfu@9?LkLxI(eiqTj{PYD)F^;{du{Dg9$AyEt`cBa`lUpO2B;Zm=gI22yfX6(;4ZH`MwOMA-;$ERZC5M`OIS;z_lOc*s2kFs9`tKZ6$o_bEuD(O(2~=Hay># zw!Fvj6Q@GNc4MB0v%7?7E%F?eZR-NQvc#U{U02ZG_6BCf1YI5A008S)|G)MIR{Hkl z27lSO$Hi*M%}B%eZ_H%-!HJgPx@QIk$0HPNf|7p0%|sdr@NSc)5*H)yVk{C-!?<`6 z87EG=>c-lP_`b>k1~TIxj%*_s4x-_%l;r<3h9fS;|7q20%_NdV&{hE6)lPo8> zP%KE&W?%;LV{W-T?lyG`MPVeI`wK*a|Fd^DkWbkVe2c0EJPe|j*b=~=zeS?LS~@H< zr&*$)z2v57wcMq$q;`?UEZ0-gI10#Gpdc)71-Fv@fHklzk4o8vkbn>=C0XbfZ0h7u zyVBvh^uTOzFpvS*Bb;{+QAFunH$F1A$>3&hSf*Fn0~EeP1JN+!kkU8 ziss8yL650z-KQG=qKdg;=e70e>(xX4c~axNR6}&Pv$ar5Rfd*KE*eqR*EHWRT{#}! zLP?4;s*X7U?Mg?cm^#4#xW&Q@nQl1-U(9Gjt)yh8DN1LKYtj8VdjXn`3nC&b3qe=# zl@r~cXZcf&iRP}bP?)*V`*NT$`#za_6Z#e6FWvfaQ9Hz-asN*J#^NQ|ig zBtBj230~%&pP-uRO_&0gyGCSM-vLjC4ClQ2eLJ58db6YWv7IOR<#zrT-173jJ%5)q z(6jok&u3*}ZDna-Y4;b~EmHuKg6Bc`!Ke21L>8o0l_zgoCO&5!R6!aLk-`|jj2>;R z91RSek;QTq-6_&DZ;aF&--on7;LYPCo9kZMpvF&sXOHb zX)k7)Fc(go7MAfd3OaGqX5}iIWUZTxeJlm)Hs+QgsdXk`17Y4YxvCR&d;utetdkOL zj2p=iSEb)(2xc^;CT@a9!1FizAbPa>>dd>={O%FYRSj5;;{$0{`XBh9B|uv$RO$%~ zQ3T~YK1J-I#CuD3#nX8l%uI!FpfzXG`S$tO4OwTc6+sPUQD?iq;LLxb7SBU+yqLM6 z$;1ie!H~JVjn4TLYt95Sp?#X3qH|c5x?XD`l-T|KGz~FX(xT+BI1_uGPHF(?0Ua3f zI42GMTOW+G!nMTta#K^I8WY=b;m((EQrgf+wnj73_nSCQ?2RQ~a2`&<9!Go&zOEmf#B&4j-;#WS2xw#C zjdW=zb()DoT>Ruwc?^XHZhnN#ih(LFwy|KR9#=zsn190Xu8kUQC$G0yQHylCEt!xN zOHYfYp9@x^hiN*?W_v0utRm?}VED0AXlf&SXVxyGO8%NgP(BD(L<59pVNZ}R!cQEWucTo(XwRWu z?%Nwy;%)-1IB;^%HT?C!7$c&_y=>CR$2~mXaAjFm76=ImsSuvxqOD+LScFluV6d>( zy9ciwzj^3}LN8eBR{PIn9ADdbi$<&|=Qv@>On=A!DZE6@9^D)UEQ=z?2zP6qibC%~ z%j3^mD@GMa^&aIN6(23@ZmfIF?F4?1k2mbCI&X~w3s!j*I(?VAs~PE+5bK9L+#f2W z*bvn#pf1Z8-55$%Oe|tRk!v`unmFk*tm+$i3-vI64y(GYM@pyGXeljXm{M{TJYFVO z&Qu@A@o_fr)@MsNteCK~oAE-1OtE!}RYudsl~L8~+Snor^S)wNrwj)ExP(JtGXsy` zg9E4_jGJRWEI$hDS2yclFx>xc`Tu0N|BKDXDVfQu&!Mh?3ZwXoeDg<%jzkG1?B@2) z???7UAk?OYq2>$Xq0lt6)V51Z8pTU4PORr#emd+a7AO;Z<}Ojl@#5E;D`xmnUeERP zFm)H9VWHHr{AR3_VK#SeJD%2)rn5~O(l^?4aCP>2c42ew_U=*9`gs$`O)8Th3DZg< zT*FGJnrT_DOwq8KAt9_!Enr%)7iyp%UXd}OK0q%?nK7x{m)IFMW@DlGlE105IJ%!v z!wRDkc>D|G?Y(2Te+wv^qazgLDbknQp}6_RI20>LqnWB+rxb1#F&|J&ujKO*|7J%E z=oV;shD9cWGk*FWiaDSWmuJxD=I0hiNW{}?hF(HB8`o(`{k{G?p!M=`>-)pW^{#Vj zXJh$2w~jd6T>?M4Gs+L@rVvVlGrJYuatwA=jFDTy#?}aT@Icsc{xjj!fa)rJUhO?^ zJ&h)1c2QUTxcVYl-9znQp|(2-ST+J^gTkG5eV6IrkvN{@qyB48jUjbh z)BS|JYqdQVx-+Jgu~|4Wj`Wyo-x|f;&(r4w?QxQ7plLzHp%h^>4nQF*dO^zde3{u* z4JM7>;yj^^^wN+KnUw%A%`dY6;}nSba-i#!359sPn&q4{gOI$g`}1{73}cYdr;dA) zlqn-LxD!wLL*dMi+0xwNqAGD=*=XdtY5=n{-1VEX`kh(1fC@dQyD+t46G!;AfZ7UMycvD!r z>&bx`NNOZl&?c*prNvo3S~tnJLa+gw#vQGDs}Fzjw8?M`v2^w#84n35wDMIdM`gs4%%D>{(L*Klj^<(_ zwloiPQSVRjYa2GQ%C0o1Po(A`N1Ni0Fa!DwmH;jO9m?sVxxn*d6Lb;9a@5ET`i$*cdI*$c~p zYvL{9^phIV%g~9&H)kt`^tbEYhiB6%(0JK@9SI?{_6j{QL5guqIe~imMzA^`88z2k zKt;W+VwP&lN?^c2Ver-5QR~YHniz90-cl~+^Cba0KDc%=NncNkhNG|Bxq}A0fs)oq zSAugR2b^QF-IyEFc_l48DLl06m9J<}l|HSd8W?mtZ+_7?#Kv5HWlrsScSx-S2P0)! zJF zoKQ-qYihNQPE7-`VonkhI!#wz-EYxArPAGm(8LUJW&v*N@gf6!x^%_31+_P3UM%@djo55%=V%lF7P5g zH3rsPlVNQ_U%k?nGaoDC_5~TpwAK$_8TUHhIrB6plmZ)3;@I0(=u0-$>uy0 zyl@;~ZcBH(Ds3B1{TN^gYVv7Q#(dvng`VB37?u31c^aP)-TOT-nc);+72i!IAmX4J z$pr#IE9Aj;B>49>FZGBs(nz4SLmO$}6t7&$ ztas+(>JaXRp7a9MV0`W0_pEaE(X+*4(PE*nFU(#-Esd6>7b9|;4x;-;R89^Q+xLl` zR|6~WaUiigVu!uH!I1q3Xx-S;5q&ckFn;`7-z5F3?&>E6+HtxsgP=XAOp26b@M@56_~jWyT!P`W`%Ed` z6OQpN1)C}2Nhg?c0A(1js1mQKd9UDTFL@=SCy@GWBsXfI-5TND?r{RH5xT|OljQkG zu}>9&S7zZJa@D4mbiEtfoJ|{0ZmDh;iT!>09t+vCJ(tJk(-_Vt$0kV8BTOD89y8z0 z=TY5@5eFXIrnh^^&yy3hKUENIYPi3J+q`j%cVSthz6F*&!24esg>F&>ZdVY(nUVB! zgvuqEmOFcy<4v~pXAK-ht}~Ya6$t#m@VEr>!28)z!}`{B z{6JK)qg5K<5i{;l3I1*`VA~mqL9Kt$Ll|J*AG9CedRT&>+h^|_d={N8H{5JDd&|h8 zkvejb6zRmrFsgzo9)Y-pO;RyKJ|=~UsonalF_;_%fq2}I)`Jf=lZMRamNu5dum$T`a9b(}fSpzsM?urpT-(ONO z?}x9zUEKV>brcS8e0P(NXc)Ol#e^QYvSHv1U(n>`f08MGSrpI57J?>oM%^$$A8hiY zO1rMJWotRWURFjrb2C^ACSREvTQq#Rshn#aZ$V^Mp}z4SuDl>ELwfx^P9AteNQ~x# z6rKXFM_;=yphG%{SAO8#afdcy%gr|>B zEPi)aQJ7d7TKx|l{okwqR^uOS|7*)PKR&klZbbE~8c=@`i15*g-)dOtni}ZYIU4Bx zvM$_T>dO90T^(DCU)KK!zxh-7ZL{!K*}v7dw=*&S7Z8`aa!@EH0D%5*004qNK>z@( z{AUmzIf*|}`TSicyU+NGogYxjA1WvR3nV++e{9!3Apep$@pt4x9q%hv5C8z856|Uq zi3WcZMfU%Q%x7$%XZA z|6=3Q{QKcGbrHOBi3%k5cM~1eJK3?L&Qh&&7aDP&Hn}d ze}w&e^%U=V5bJ#;?ywsG0Q?s5@<-vZ|3Aq1_u!b5CZF^_Iw1X_{|{68QvuBRFW|q= zNdM4}q>hDwoWcJl4gGy+=G(<@3Vlqm4KRQ2!EoOHLCC-FfmNmU1jvW_M?OZ+Zyv-y zie>q)!T;t@{N*?T8~kDi^)ah(eEz$XrJ7$$`KRyVAF7i3$RRTL_kGd(=`a8G(T^om z0D#|oB7YQ#hX0$Ce~|2XUXIqmE>2DLv*&GLUl{Aa4! zzi;KAv$cLpSo@n^PIalScvv3{Ue}?`=^4H&o`k#YJ|2mh!A^lpw zzb3%^9sAEgaeu`g#{4z*zXqxP9sAD@)&4rk7~=jK``-lF{T=+z?*G4ndsF@w@PB&}{|vOh2LGG=|HZfXcPW2fHvZKzsThAP<)8NdH++zfnFIg;>*L4u!{R%+ JKW^Os{|_cxuZaKv literal 0 HcmV?d00001 diff --git a/tests/providers/nashorn-core-15.4.jar b/tests/providers/nashorn-core-15.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..b472660654ef96a02ce1c6464eba68620186bd00 GIT binary patch literal 2167292 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⪼w7>j`3KzB>g!?fQvgf#NxFj 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

1A{h3WV?Q2ihdI_%!Dw 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

AsrsW$xhSe)P6K+3~JR+3pcX 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

(GxAx-4#^;MZaERvp{t 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 z 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%0DdS4UwJ$b|Ed7|9)bL?4wV0s0Q_Eo_}2vD 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

02WSRW4#+BEp-;_ zt zzg@$K0m~Py;OR7VRrQqT7S#@aVp_I@NvpLOOlZaY=a@zyK@91`3!Qil!Mg!N{RhEY zTkfCa<%Q4($5^*abB*HVh0+Iy^gpjpU;rVrwNC}i?wo)ghIIaZwaAXM!4UN@F3>F| z6INxe;SY#mq%E2K2TS_}_TjSD2hFF__~WysP=@rFbSVQk@=wV_YK%GQCCJdl0`&kA zq}iP=I7C|NT&&u9loyhH35kV73*7N}b2+(HX-Q-`9q93kXg)Xkr6DrfW^!6dAva;5 zacsV}v(QA@8%eh!Vf9H;WCo)4+FaY6qhv0q_S498ZAa^>bZbM*ppWZ(MyQ6!c!dUx zpci?W-pgVqT&7a835ZnHG8*&pvqJKrvpo+p3|`KffcnRVE7xT(iIWv)gPjzZgLH7+18S z{RT`TUU-+PM1Cu#$aOQOEO=Y1;Ml5)xX<^8}h1{6PBUs#Q#LaY2S1VEUsG! z#yNEyJjPU~u!iHOXmMEDCP(R==b~QXdQ8>Oo%m8)$LJe#n<>Q3R-zLVljrFe;1&oN z8WXL95*tJ9w@{vijFbS*5JV1vPs(;}>0EInb_(eonTyw&QpGmRNVWiUw@Q!bQKjI9 z{%(esfSID3yWKLSU5pygfVBut7=#nYY#U@_vku@idYiCrEnru*094_M@=+=}&$=f` zztLpeuRU*0>4$OEM&NBq$Dp7Hu*IGk)alGOk=O*Nx8s6b=SsCJz9gG4JG)QRyDzxB z%+q)X>6CqRq5FXSOb2eg5Wfx^hBSP?)7+4Dw@L z7Ju1MZNbiM7jR-rWu+GZMHCOr+q(N_FHymk(7Xc+4=a(k&OIecaKf zdGKVnp~p;b3Z92z3poO{=K^8kzAQD8mawKV0eweY#-mc}RcomD;N+tz$QU6`~ zOl9*(N$m^(i@MzgSg2K%(r(j~@^Kd`ChOXBzK?D#*PL7pi`~s%rSkLR788sz+%nf$A}6E)KX7ifw(R-sBp?rkVRyO4Sfx&nPtO zDY00iR4U&xPj-rCanU|BT(Ts%soHQ|5fGooqu~_0ar#$K`o+-ASxzC^Z$GqHxL~9KSu11~LQbO0dp4Rflo9Pitm81r@78u#jQO3Dlk`?%I{(fE?DSGv%K}JHK(yG6 zqv*#t*o}L!14={^$`H*={Rc4A z;3~t%95CoxMMahMK1nz8aegr6zzCcmV(ESB1^(RgG{7!n^@a~h-3yef=lhEgX40T2 z1FQ;YP@&0ZmF6AoD{%-?6@kf%Vh`H4i&f@I4xKqxs0p`3G?Xc3O2UV^uaje)mgvLw zHf4L$u*~8MA%AH%&VuJp0san`v@YkzxA{`SM)LG&o#s8Op8qcYntyobx?^DvNcSl= z#l1#$ALaPh@b6yhhN%+aSoqDt^RyjHn(YX3--eM z^;r6PVd(PU)n=14%eSrKq30;E6;#h=LVL9+$GBreE!f-dCrr?XSGhxZ{^K(G2pemT zxl`-p*_xOMy+Afh^#7;avN6-tFyH9EwU5bNYkeo^p@f!&!X0Dt2e?XMD4NaPjD@@A zHO_T9`I7yT-F9l=@a+r^3WbMK=QKC6-((QlV^ESF7;m6DVi94rpDp_)>Os;?zXuoN zC=8#G%2F?v4Hncab0xguYTBkL>zF)MmiH?640I-Tj#%fq8I25u5)w-wHQqQG-{_YZ6f@5;ijl$Cs1M zXeUyMJ|p16J}cZq!{z37kfr!%vugQY?*I~;!Mam>HAiCOxdGlu{RHbi5F_nqzz7>^ zjF6d&mYv8;F2T>f+2V!fG#_vGO=kEJKS*Qi!f+$7eQCNoG~W*vs!jZZRLlbP-cr** zSJBU+lkrehPgV*_dQ{8WDz!kkA9d|lDok8Q`T@J~beFJzOb8cAskp>^ zny`<3nzQXS@ieV($Fw@t&K8D|RcbF;bjOLqEP^!Q1YP*;hMJoTi?r7c?r5`264~P+ zkmh4+yWS~shtyNL8qJ{;B&n|*%G(!A;xj$jmAG|YxWFAA?kIT{)io zF$jj@uoy&ZPk%VxqO^U+bkgqGe02cNm|U2&e8n>b1qNmNdX@xF``B*Df-?qfV_#@G zDn>-$+IY0JbY`>cNqZ)2&`21zUJD`AVP(6%hQE5~zRqL8l5l77M7m*&2;YMiE(Z%8 zhmdt|rm;9&WGskg+0kmQDh6bb&(fmq~AwE)#c4ptG9d(sOw1 zNmUC?t>mbKWhZV!9foUNjoq!9*^}mr>HlV_W%*6@_+E@4{^ow+pjYf<9W0;YYlR8` z^hW8kw@6NF0wNfWf-aXb7$)JN00ZBREf^Jp1&{TH+Q%xT{y{$r4q-)e9y14%gwz&8 z2mZVK#O<{ORjWoR%Q)r`s<6qhLk$f-0O^i_^R9{i>M3F7VAbg$S1GFsQjf4mS>A4roNSRDupqq5E4V7WZ}X@VPubORG@d1FZf|whVDALZjK(Wzaz%hoqsd`vdB7LmAXC^P>P^0}{wToq+Cq%$kD!)Y@>_v6Z@c0v5 zDh(F=mQ8KbeX3UVN~c}P6OrQ@)ZoZG+_2oAs3%{6?6?WT1S@2jW3#E~-Z&T8kiRX1 zv@Psku2tTa_((<7D8)MpU~8 zek%p_87HnLbZUj=Ht05W_{3#Gn#-g4WT%tJdszr`LR4xwf&OZ*Au2T_MK(^72lhKV z&+=`cb=AFdV8=D2=PtDK8;GFIw2>=kFEe#v%3iWYc_Y*Rv7!A)So^*{J%5wN?dWVd zurM4WmP)(}=;4Wx;9!YO{D{p@7EWI!`ra#%q9%UM>GahW&C4d{Vx#tb+we(SldD?N zTk%CZGxuhv;D&YXMN{-V;{d0`CaLtsbaBCVxH3;VRJFQ-d~w@dJ@2S0Cl0-V_cETf zR}+<9GmEK8%;(90-iJ3Y^v3!+o^yPX&^gQ{S2-H7>mq(z6Ws{R)A*6R z@DRc+A*m?$*zKN2g^tQZX;c2jQ2v9%K3oTC6%5pIecT5x(9OYOS?3L4Kk;T6#qM*B z8lSw9dYg4iZefawaiTS%KD=Ra%&0mar!`KrIX(a3_5&+j`<{MdQr&-�`VIM^f@e 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ªyJ>{MeDTTPfu)E-0+x4i=jN|Jla4ifl!*D)~YoU$8 zI5xX6p%KeLR7az~RMNdTnK@j5zMw@)l`0xCRdLZf8?hV8Hwi2dn;8U;TOtO*(@I-O zG(>QjOc7KY$Z$oKteJAngqHRkoq?0DVE$4x%fkEw(NC=i)dtVD@58iH!xEQ6iJad? zU)aNkq!VgKp&*FCPtd`M6+-Y7l~w>igyB?Ihfc%4PhHx<_+|I+As~z(b26Al#h@v- z;_F+zyYdqlmU~H;@Yho2e2xiO*zmn#Y1C9VUt7pn&+K`;m*GG55vqeAu*wHk75D#HSUf1ql$gcuBwPeaU9i3Tp*;IH zXmxVVgf9wY+-O6~ds=Ya!W99iDVJ^7cgN{YGXTz}wcj(1`QjoC+wFlLdtLOe2wU<7 zf~g%zADD*!;??^=yl0_`_DDg{mBg_4ruHWkfyIIhQn}R?DI3VnCsy)AvRLofraM^1 z%k3MSxRas$?#Tn#Wk_nlI}IhTxomvlS!aGh6Sy4$2=eXwOq_%I--N{HZaEJPisjzB z@Gpai&h5+LXz~SN;-EQNN_<;?03qiGIj1#anw63Pr+3VX-XQ~5 zL>WN>DEGIqayjw@#0r*ouq2^mc2CM-hPmHlpOf%)19kJtoAxG;OkEY}aX8NIc)rru zpKh#e1MJsVfgDr=xai9{f-E)F=X&4V-SB;7xyOjyyfSLkpU^|{II8xZYC_Rd62h_h z4Ir(-1Ee?yF|8Z_c7YmXCvku|AzG2Ii!a(GjHVU|S+ie159y)bhO)L{)Qf?8nq`e& z67QA!Lime0-j8T?%yV+uhM|dcgsGo)9O^Xc@H<#bTr#|o3hVm5E^#sD4&dHS4_i#S zK!b9nP#ue6I=f-3&&!FhgQbKeg((@-DL=Ui$ALTMhrvgK*j#aa73)Uq{%H(pRRb5c z&Dm}jH{4iZRfAn|Hem^e%-~an`?LJ)Qy=YeLBGpDVL@Tyo&Ah951kN8wY(`oWwg8+ zs_razQViRY^w6Zlyt_IYqDMH6dafYhZ)8q^NLpc&l+iaK6invz_ekJ`KteKON%x8%Ahqu5cZJyhbqo4tLx}qtVN^d&`GBTUe=iY zMZ7v28*Lr~V5NNYV&2@GM%de|e!g^UJ>A19JPSWi(NNg7c4o%(ar=rk6#j*>vM@k3VRuo6YKErEHPaR&FPppxJ>eQVzUIR|cuTzMN{Q z2hR)muU!tE(KMJBI!1{k3~>-^5sy{%^7KkS^MhIQz}6Ac;W;<)=1y(X(_HfOOi9f^ zH!HI_-f4l?TM;X0)Y`@OhglLM%(J&^K$eXm=EBnR^BY)C@dk4>(bhUPQVMYide6K}YPwe}Doct9PVPGU%DE|K!!;#a z*0DKlw2cuh?HXfwt4`xIUyjZn;$XmaXg~V;!J+*A_qct3? z_-RQMu;jvtf63qZbuyJV`)3OoE-m8y>j(}+^^pztRYET^)3LI560M@bTu#U@+jr|6 z9Z7I0&<{A#jTrdCQDZfu^#Oiqa!kP<>a4DY?jsx=R@a_2E_PQQ=Vy;_r9Yp8NR8ga zuMJacG0l|>AC*l2z=@*VW(&tu8W{eoWx@qm665 z%`ogu1z7e*;oG^0(LO1eG(2!$f$0%`*!O)&0}Dj=1k8nYM~9R3PEQ*oji{ae!4-&6 z$b-Q;mmX!|7UPtCIdLGbGe{Sbi}ba{N<{(Nc4(rGG)Z{OV^9S(4 zEyhtYS7n#y@PYia1xxjt=yO`z*I^QfT^uM9#gqW)}ry?zF3$8dj>z(oiMups(D(M}pAm0`k~UJ}EtqkVMyRh>5m+;v2R>aL|QZ=bN42$g{bmL_zzCmL+vc~fOA8Yl{=)dDr77B^~J(# zJ8P9(by6W*o#W%Zcc4A65pOG1>-BLxb`c1LgQw3d1x|%)g126+ijl{xPj{&LY9Tjh z7l;j<>Uysz4*MoKX)8C<^$jUv`eYe$()nLo79=Yl_Yyvf1&}{z&D&(pGp1eGyZ%1b2gt_!|hdc(W)!K~7xZY+vWGmT)?( z5=_-XozqjHq^!EbOZftS0{z=Cj>j0Vzumss1T1%ozWg!mK*H+HED|tI`th;D&NrAP zq;@z`(#FhxAAw{y64#WGeL&=PWe>Sw}<*twuTz1GL*LWZNF_CA+5v2Q3wF$`=Q;l=NHVq=byetSD>j4C(4z= zB*jr%`py=Yow53~NY^MtIyJWWM_)*OYtt96B*wLz^<4F2Vm@@bwFtJ?g!{oDJZcKN zjom+s!DY_;w!A6{;fRNh8)xMT#a*(miaJlB%Ga09zTpB=B6;~J^P$l;4DO-Z<3uwx ztyxxmV@5nn-=Z2DQ`JYXkl$z_QDq-Qtplm)$JK}@wKM(##rFiraqwSvuMt^4=>im0 zU+TX?+cJbBg&tZGvPm4yLVHsr8)OWA&)p^d5(6w&h9!M5d zhkKZo;8JUw>-5bTG&?*^4OCV)9}I}l#kt+-vAizV?``G+bA|+^2Pxpb-YbNZJ*K@~ z+J57FYeg6FG?;2)T}3O}eMjJ+vfP?Q30FXO${8giZ008&^lz$|hZ!kM*R%H z^Em_6%{8{HA|Z%u1Rlgx+7yl=Jk5SGEd6|<8nI>AKvFFCn@>`!O>7T3G)!vjm+309T|O7l^{mtIlkIK2<_0mSb_T7_Hb!w=R2kDH@y|^ zc>Jd>qk`HHEeVDhw5d>0&j&Qe16`!JbkVUv4Pa!@>&UeOiIl@5g7(vRyRi`YB|X52 zmG4-_F00N%77+%o_&G^(-XZ1;it_nKv8@Oq+B}kMW!)zEB z)9;F?Q-Gt@#{TS43egZXL@Ub^i;@9Yp6?PE%75b$eLDL18zG_l{Th+KSYqk(5 z+E;4U7WPjYkfr2@Ozxkyk7_1heU_fLr62O6#*<0e`wqXbKQL3CCR&@yA6bRfy9}{g zsUWlTo#orB5$)QAJwzPpxkMZUb#SN5I~5QLLKrZBlscT?f87t@4pSdmgpB_&XPx(8 zL{Vu-b=;%4uM*}Q^@Z=P7L0?K1zyxAMZKg!zp@j-(i1V~IQ-Od7);B@e~xs?g=ijG zG_Gx$&^AlbE()$5K30L>$1u#r$3Y#{C*2$uH#o$yUK<{=zB^C!90V z*~1&(w|f11Sw6?ZfnSDOA5 zTYD1Lk;^Y_ssjj8>6Io2EzIwFq@3|Ql=G7U*iDI{J1@8jXbPRuX(ZW8F@1OR@`IMC zb4f%svYJ1hi96;cEb*nqT-!KpQ6iZxrB>@jSi__yerLID2Z6+rrcO3Z@A!!-JoqV2 z99XmZM-XjPu=z8LQA)rN`fgFid=Y6vT*#cCh;;LO?%lyjYy=TN!T4|aPgbgT({|0y1r1l{uwPeNYM`t z5{5L1qnA&Q3{@PUzWPmm}jHxJDYZ01H##2&vyDgH57GHjVZmd2k? zlE2M|uYZF_cE>X6M>D>4P2ar|V)_;*oSh>2#0HHe`Qc8v(ur9VB8%l3@8;VfXlHD7=osdr%JI9{)VB2A!d-Q`K&H4l~&={|0Tr6 zR1zv`4-LIs7;I`9I7H|JQaP@me6i-ncz`K#L&DZrBQbBH!OAW zR-AN$CZ4cDzp;L!hq2b1AsMq?BX*3iU6(4VX;EvnYKN1jiERdut8m){q#7V!pUiFz zFfKrHW(Os&U(`p*GMc|LM!h!+LC z7WIj?i6EW)&Z$bg{BIVxaY(oZKSF_O5q!&!>|b#`4;un4pFKvcrrx+5d zcu}&-F=R6<#D*B$6`f;!K8m>1a!1YVH^c^@9R@`ovJ0Y>fEor^JOiCD_2?*5-IUUz zqg;RMm>L74VdAlZ3Q((DfU4}*IadbK0~^YhH9KNkeZ;ZhWf%i>=a$54p#d~umd$B?G9F&x0nXg(_(QeEoRe}sPD{9*W9yDBLBT-} z31-BCV*~xj^hvqrHwRY>0U!R&#D&wS1^xmU>ccvtw1z3N9ug>!5g;d@IKSBj?TQyg zfv;EVFy_s32Txpc0ZOmaXzrTQ1L}|za|hSu=~L}v5%_KwtQdmD2{uVo>H>Uj0#?f$ zjkHnzu{%&$&T^UOv@644WI?}}K`e|Qz-RV=Yq~EFgwLTSST! zmFNCInMPsld|o|`g!&b8lg{&$wepP@it9}XvhFVO+WM4jcvBX_t2vp&b4hY$S3in! z?XPGxA>gjbME6Nqk>mDEGi9DXN3|{y@VuPkjc&AkS6U zN^*59W0^rNR(zA1R56i&wd=3Wopm!~<*cp?C(f)d?6?#!hTS}wXY=LC-M~*7x;Qm( zdvQ)@+=UAA5{-5&9I$PW7>X~Pe=iHEv7B2G{~d&WuDr?% zM71q}3=_nanM_Kb3yP2nYioNgHkxHViX#@R%B=|%bx19}CVkFD9B_Bq8u}!)5eo88 zKN`%>Dv@q;SNN7;)?8qo8&hbGbIUf$c?IZ>=^FJV8xsuwR<_MHf%&%a5T z!F!>jpm^!S%0N(Q6i9F8;rVpj6beG7n^2jv9D&3^?!U7v7oLR7MU?;hXOYhn2tvNGQvpKHK zYEH~^ogfJW6%Wep7iA1a&uJFiXC zKUuYfNMC0^)_=656z3oEbNXZt=Bdl7-ht5ir)9lVNzN&4iBv|09fD5*Y4)k&^OJG^ z4_A-KJvd!0WnC6>_v7?#$KdIwp-Xik<+UbCtnc~D!YC&b7BTLulu^=51gL;TvA;DkrSH(C3ErGl{B{yG2}~14(n5~6dxrOd#_m3 z^Dh%$^7)x5;8<~t-{GrFqdy}op|I~rTZSbp_CR-7nGzhKwRnt)w*iWu%H}P3q0pa7 z=|c=b9-oft{dN9ipHk{Gb;wFwh-oSaFb`az3z`1QKVy_`j&&`i#VeVKnpT;s)I;6% zCF-Gjwt_l~zUm@>A)V@xG5%VUO%A}tq)BYnq2%J8#)f&gX2)4qxI8<`*KACQFR#RB zdwIOEP5AK4XWVPqSF{a6eVb4o=>?$lt{!0jCjU!4V=PjgjO+mB?ha0ngM!S3+E4w4 zRdIm@LV~Sr7mQV~lMwdy`jp=N{yLnBKmIVn#h%jRJ;`ssyz=(}8`3gGmw(E7Z@A~l z1p!W;450**pWMqKb#GE`7nZAq)2Y^zNRCobI$KP6)#(A;dgqlF;z0J$K({Zqch+of z3@1r;i*qhknCjw(vm8KBM-YZ;$EvOC@^@gGjjPO{KcW*5}X~j$` z3S)6H72}GXo2bS42)FB%7+03oDE|7&-4eDl)7W;18={MPit{XF8=)B$DFklK?VolQ zXUEdX&SxFGc7j6uM+i93K)=*WQm1O zeDHJ-cXdSeuB6r~ZwJRTWN7*hp0;{5$JI5i?@@ z_mI^Xv<_B-d>#5Sy`2Cdz2$IPjfLF-FIC;d+ZYtWA>iOIC9jDa8?Zg@E^P=m0%#=B zQe<>@Xwbu51CRwqOGKqrAd>$2b-ehK>UxM&wwvLbhS=@_uXbDrW*n(Qr)0c}8(rg7 z4ONGEElGV4frTK~fCv{bFxD;XSbFH~(R_1dlmbj}>oG43J(~p;`f~>6M6s$4bSE$2 zc`vQeDsfSMK3K`YyAz+K;ZrShT%=XrH6>=-6zad~vnfZL$1B%8*gfnJKgC zjlc=&_*UC2WB8k6mkzT>ViXb+#_0im;cxXpb=wk9D*h-Eg$~gK;yX(fU%CW5za23b zj8_(I>n-(B+OvOX!zqiC%FyqsNR`!^55h1*cY?l3$cJ+&A2`nynwA>0nHZ#0X`<>@ zD3)!uL5CfBA$V_kss|x^fbVubei%xN=;4!ogz6-7vL{QllI9$zTL1c_rFwDmpruJp zNyV;ltt)DuNarGXH4K~^x0;k6+DX9dkT^4pG_-c$55H^<`j@9@=_sP&f-UYtw7hYn z=fy=WKI$olt~4a0eTa+WAsp=e0SYTALkh>zMN1@KlR!lG2_ZJ_2}gmmZLM-oVdTTS zUKz8NvZxO&Pqp=R($;zfd=<4ZweSOyTL;LG0FiklVStaIJ``oYNSaXcWcHS9S zGb=udyu$Tee-tjSVyq}z+vqEXy7ylnSVgcdz$Ldqm!#}K8e#<@7SH7G@_azJ;+Q{3 zlT#ruGh_D9lVWqpkNLmW!Ad6I6iT##SneL3M-X9?UDZp$^ivwaSE#fMf);?YlB4pK z*CDp0D7>ZC5z?p)+6B)8|9#wNCjDos6*nRrkt~wMt#Cv++Y-3m?;Di6jY`SkbO!MgVzP(sWT;b)dbP|X61JS zw?+}G3p;g$FUHEJX5^2^R5ziN3c|Np0O)&XW#DedN3HR=$2OTC6xj27qlw|!&4`ys zVNBJ4cNuWQ_@vtSD#zyOLE^{WerH@eQehMz>!hPZ@Tw5Sp_A3`_&1oNX=O^#h3aXC z_&TtO63!_u8LzEnKgI9E_6{0=HP+wQCXLll-cR+cXEw zFtwSHn7_+edgwq_cr3?H`z!TM;f9>IU-lMhLP@(x)hNF;N|;fFC9U<2YMx>`rnL^1 z9q#xRrs^Du6$e5&7@6p`wvm;owb946B>Wf?kdq?z@IH`Zyk=}ZuKVRdaI_d z#phDy#S-SNf|I7GR*)aV{B+AvJMW-9oH5du2f8=7*cjRze2dH&9RVc00`{PVUura^ ziM+Go`uR9z7jRIQ{a8td3vEl)UW!7rQo?qm3uT;3V!Z3Jigs=hxHL{eG``9=>1fgy za9L`gV0Fl}Ge6Bn4B_(p9C7NP`Gv13FAFos$ag<-r}Qh{g=%v=w|Hu&6Sf(LR)uQo9IN~qSgK(5(9O*05~YyK@t z{N)1vs+DZ=%kH-XU<7n^glT~R6M{W=K~EONl6$=l^UdXQdPErpL>W57=qbUAA{}S( z0mf4t+?Nx*o%2qwKOUzfEi3x|GzB`wX?TZDEm6EfKa(4(V!0KSCih)@weu%J`X#t` z1`1~6+t4=}?YzH%8d|M|+F2soqnp-<8-^-Lg#r{W^vaI;yV|ze6#C&qvq^{^Eg_W> zS9lIn-tNG<1ao?G-A1Vu2VM#WTCs}d;4nf<90f^j5hoH1F4$p^WMTOnRqKXDk%vS< z0TZ}TN&cHp#O>A@(ktN>Co2~~aR^#&+C>P?xxg1qkhv=d+Lq9{svRIjDcjKqRf#3u zcydl+iZ6$D&TU|c>giBqtx~!`ZE(O^Hk)cJz=!F+cd`5Q>6wSHzjzaT^X_t*iaDmT zOBE31oI@$_*u@u^Bp3E3WCk1HGtRtWNTNeW4fOBu=HYD{kcA;7&3#g`CQ*=lyi@pJ z;h;~u@FC4McKF?UzZQZ4o)O`DLi+y<{w8n&;<%ii&up6BYaqP18yf$EDrU!yliN3q zWP_bmfVaa5QceeleNc^a&>eQaT{!>IH3%<0K<@Rb1Ld>WHt2(S^sFLO;ub)n~gei?jB7X%V%8tl=}AW8p%~o{*^1t z7m#^~+8sPXUqD{ze0~i;0Rby!D1wKMT zxyVqNn{*EBK3L6x<9P}}mZ{(UUu6XEX$9|T@?Kc;+~CI+Kh+T;CIx#=yK6F>1M6B~ zk!ybowoaPyNB?HZb>Gmx7(Bu++M6IuuzsB_GF&FpqM*Ia$Eue3lS48oI4d4Ea6eG( zcH-k!5%fuTD#kkhoug}Ld-pS;ecdY50JO^!o#F}a^BOp5Bjp#Z11T#Z4xBAV#*VXL zVFsZnmC*6^oEufW8@kK$o2vHuW;z0L&Mzh66FhbWiU$7FHJezy=qi43hwh-x5x69 zJj`yi1d0U(X;E6?9coAy;>UD2WWmanB!TfFqtX_3Icx$M);O5euv|7VN1(xv*>u)Q}# zoMjyqSQB33(ktpZQR8NlY~}Y#ehL)m2SU{EB~Gi7@j;W;8?TvGPaVewAFprGyugyX9)R>L9mWn4m*%o{fJM_;)-_fWWy^J1 z)UK7aokT-~lEMmS$A+1uyL8h*EA?(O_3jHaM*qTR+s%4a&5sQ-jjm0Ud#h!nreg{W zLs|4XmQ03G#C}(r?hd1?6o5e+^S-vNUF@#-_A?3@?5V&odh-4*Ohh;qgDj({K?wfs zbEsp^Q(vO`h#RhbvXo*7UGP2ZyAfqE>pGLlxKQnMUozC>>%Y z#(55*8~WZ-{Ti+sgK8b|!omVb` z35(CMU^Q|3h&6K^65g1Km?8o*vowFG< zuUO}yk3uw(c?)d-g^&labbCk(T6|d6`;4f>6Be#G#YT}^vBbEJVptN21xvfwx-GF9 zywWqm@KaIYOjHI!g-IGXV!OEgGMkFYK{oA;DoVdTZ>0fOM~x4(oy z;4!P7N7{kdy6K zo9Z^Sv|OrPg1ZPIylZW-&p{wEjgGOpQoVaplBy%DkpvmbnZ_>LHWcz9=d66j3#1}v zoiMffr%m)=qB^a>&fDGNss}$%t{AiN%_g?NJbQK+%9ng^p}=;9#bS3m)$DhvOGb_X z%|fZ~qUOK~#4d(vFzZU{pcp1|lK0o%=2Jht%&7)`ft+zyXThp`C>d*&_>?hY6{pN+ zzJ*X#^98l9|>LHVE>uJtkiPzW5_%70hxP}6n;YHH%m6`B%>>+ z3BfxBdt7%bI-7>r*{mO+k z(ML4uK?5|-BVcCvoFD&UG4Q6q4UYd6AH=?va{QYvGYbC|AO3X`R`FCGnGfLue5AMT zYd96`HY!EXhz}0_mr#NLNQoSiwvq$`U~tmXoUwO|@|kHXNBISaC&9?Z85Bd7Kk+Ln zoSt62yq;%WA6MJ(0>SPP_m^BAIJWl;nm;zuMC^rKLT;kOarV_XdblN;7|Q9;6|@d1 z9R8`Z61rw@YA|Z;5zvJd_>|m92xPj~+>TJIFoeyG#izPVaFmQSn29SNO3T#%jGSf- zlLau~Y~Hb9gkL|%A^xht{BBBey})ukwIY4|j3i(>StIUwiz;yMxbUfrcpKLc%Z$4D zU>YjV%uaJBj6Y^eu4a=7b~SR>A(sQ)XMj15Qq(augW+(^E6b-8S5zp&79}U0CbYMQ zAYeQ3WDzzwl&px&m+aqv0!Nrb5Lmb{;}d3y;3C!>*35QrBFVtv^vh7-TnpK~sxw zGD4)7N5dZol_X4mmx#|UV~d8N7TwXuKt!#?b$K6H@(qKFZs%M3PXBP-T!V&VTPT*HT0N0`XlQcI?XmfPj|bTGY|@}wVASM ze3zwm_##Eo60~l`x1ono0b@n6p903NgyhXzj7hLXv_+Ac#01taZh36V2yHi$f87}h*$Tx^^S4LyRZ8dc;cxKvp(VT4HmWxR7zel~b{XhCN)EH+A?-6v2PlcQ-Xl3uf%e3s)+i_(( zpjoA#(;438sW06QA@k$K6%3Q^1}Avl&k0+~f*bfU(Agcdw)({4%T>1sqCsNqrbrJo z|0n%bAqaP%c^M+bx=@^`XU{W-HAw@ml{x5WL(>fyJftW8d}CqQuGrv=b|pzkxDxM;cD=5zjCn&-X zNWzj%5hgQMZ8I~ymDS(z#jTakm7Wa4S?D*&2Muk37~@}`XwVxJ{w>RqJvb$WK0R#aV5=!?IJu^86~ zy@W>6woVjk7U^Uv#-Pc7A_Hq`r;&~8MV3@IQ42P?l5auS-sq}sGf&XZPn(bNnncCe zpM=Q>U_l956NY{mdX}n3BB`|r#iX~$_h;EMLk zhzf@x(8sz~+qqoJK^8?-Bh~Y@EIaXv%UTNu0@l@_!)qFbz3+D5?qtjY+->@fzV-oj z6dj?^qE@aP^kal`Yz9}Z^hQiEOZD!xT$pScTQZSA`}un*27}}o9AGgD^Y3L_1J>NmXRoEs$%RdM zBJM6$la?k*a5sC0&}IvGpt*`B1A`V92swn(2wb;`?5i|{h|xlKF6C-hw0~2q$j0nO zO+*mSM`kjA{?3y$54R{?Fj`E1@pu8rh6$1t;@mpw$9*7g@#bqk(JQ()_L7wtrY6l+ z0kf~D97q4*huxO6GxVoG8*zFa3jR3UGo3B{a0I12V?e=<(i#~NVfW+LR^SicD*;AT z0WOFjOs#+~nM$}bOw?a$LsI16c?NqWU1C88ube@EcK||}X%bsMUCTVVNSJytg?Io) zfX{#rzhOM&EP!4h+Px<#cTOXfmC7QBWn%)D--zI_zq1CKx|y@T^C1fY0&I-RBG@(? z#PwEMJ-L`=b@OPm@5d9R>?1`B2fXVy?y_P-cRF(um7l?g^c%cn*mXS7KVck$)3s%H zhZpmdJ!_zA2erBAij7cJeq|;p1;LZI;FB7yH_rN%3=J%TGw+_z!}IVfh?r=v92Uh0 z3gEfA$Z1J(J;TXaZ!&$QI*9*&;RRGmFe9 z(cr%qMTXosn3mV?bjj?_duM_E)FFd4`H9NKCdriwre&O7YJqmHv`*Z=k5qRr7#2pz zcGi#V8YgFBns2DVk{4}-x_g}4Al)T@%iccXnPoTVLeUkHvT|RjrZD{AcaNEM2$!mE zZ$Tw3fnrJNE!j4Jpp6ePYSv}Mf*K4?;@WT?Gh3_0=aL0PyEN<|oksk4fR&+Q;GqdA z#kyQgZVTmfm9i~uqSDl3W>V{*X5cstl|ePUOfT)RKZV#%Q{y|ADkyJ*y4vb`zvS%8fGe*P=kuT< zmVZBf7Gn1X=uoICT&Rr&=&W;h31nUP)zK@WK`7i zWM^PDw0lG<4qV2=vF86M1=lzgug6Yq)!xTQ%$wtK<&#eo4Auzvh)oyg>9p z*Br@|ZM9=d_+q50lsSI!2Jzv>$T+b#lC9z8zOi2zmZd6pXk+gyV~d;c0>P*|I{o_O zrbj_bj#M^cvGG8u=>WAnJ1RcGX_7zXro1g-H3DsRN(9cXSi)H3_uk}L&u?2hLQL5^ zYfj%#HQS~4kB~o-;_g>gj^U*B99*F}bqG3t`0RfowJZq9PnzEWKlTN9hi!=*Zw6S@ z=#*KMFZup%33q|G{jFG#F&qUa66?%>;J*Cree!tcth|^YzQqH3*X|;%ZwnbyaEz!u zma2JaX7n8?6wCdJX1Fr2J)XbK_4b>%*M@w|GZjlBc#zdx0%nyl+VUK1S0R(&{!_O} z1ftDWZ0K~e%#TJLtU`BQLZoy|%OP*|H@8RqCK&X=N+Wto6snVl_oiss zbDBKSdqB5!FWAm`m1e7mim%Al?#e9ju8p$e+OSRHr}vwl^3^LQUL2}dGdpzwLM#t1c$P0Z zR6t1+lfb{hC%-w3=ouCzX{s(aF~%N@JEbbgo&rJ4MSk{QFapu3;suxKw@X0m#Vn!N zt$Kkk3tc3Ej}jw(M~a`HNMM9jL8F#5B5$Y3zu*vAO`Is~7<)B6t&gf}wy&S?iRIwq zbUFebeW}2cGs-gbFOaD7ph32jlCE)6RaW~rvUevAkB68xad6kR*B^IgvvQtmGsiA2 zqC0f~JJ%U_k)JSnF<0JG67#_S>OJd|Y4>yr@#Q?A2a>a%Z( zWwRN|Tp7*rBFyJ&Wb&P5jna|Yg)f$Mk>_c@g~yENf{oGsqx>#o=1Bf=nDZny$JKZ7 z=jIdWpOqZ>=UMXXFEcpnuWU{9-&Jy~9rav=?Covr|F4{r8D}G*L60(&UB9H=>3mj} z>2UVJ#%dEDgO1}dk8iMPNcYK?o707H%d*hGOm3#W40)NkRaK{C_RqRVW{rm%1F}YMahbIB?u8@>65%zJD;dvE3}?H*(e)G zU;8XHEZwf|tyE&5&1|o7rVG1;y^=w@%GlnesI^5y#2^z%EF9wAt|PQUpgYqyS`9)a zE4EBUtxW_=jTHl}_dcaY@~~i+W89S$q}l_imjI6+;jQ9W1}RXn1B89Sh@@>M4U0|7 zNWjM2w5a-UYJKY-iA=JePV_xodS%p>X09L>vz_2OYid372r@OIqMRc@(jy-byI|w4 zM{hpT80{Y}+DOKkkMU8Qq$?Ki*7Rqe{5EgJ!|l;=B3kl49|AwtJvMg`ARv)1S2*GS z{1E(0&Yt-9oSkH}PE555Oz?K8L`*h$K3sr~Kp$8=k%H28B@SIBZs&eSGAHG-~g*mVMb#~~kxAsE*;WKZF?PSG%#Det>?4Wg2lNIkL4p@f|g zs2-f~6CxeFqu^I|qwc5cnzOcI&i(UBQH^+aAURrFcsWf!P|x@A0bYi)?OXv?=;}52 zSn{C|=t)v1VVLZ24lcbfi6wv`&Ci%am`dV#?7^f-Hv>i3qLsI91SoCNrA`o6Oe#;i zanvl#ASP~+N$j%dUAoI9)wpU~?^8$pDAMpAGg?KQ|CBpk%ReWhIXhz3lpzfGv2ev} zh^je^uh>NCbPyt`P4iheS`8bqV@!J2k|>z$cNNnvr*ARwOJ`*B+a?L7u*{k~j!Os& z`PNM8u3Ew;WS_GIFBb*))vh;|%WaQu=!Y(rNlEi+ya;r#2~FfTTm}r(U3Ba#Hw$XC zk`qb|WmCz>L?^3?eJ^O#R-t7;Lzm*a9c`p|}t9(1(McbQRf4ZF1&aAIhSU~Ypb@WM(6AAn+g~`x&_%*c%VaF2Bre*{j1;a zGmN#FsxJuNP@4^0TlLh_*sY~;xGoG{k&XLh@w(D-!O7%&iBdm9R=FowYiV}m3}=z` zBGaP<3ZB3J^XUfvIkh(WwHLDbYcC|%e|)C zIWp>ymHgJ4o%~SU{rQaCqvSbFXTF-)ptOIa?#69Z_FHM3Wxnmyc3m;DK}mf-O+f}- zQDl2(ka}K@m9fcc^{-8kFjkj<(-WmE8uKP?2kX`>qHmbsk8bTPMb;&-+-+(ucOBX) zlo@=ZP-bhglj3XjxCS9FdJO4U)$DCXaY`A6sMgAh>KVKCRfMt*ETt<`%G#B&%U5=r z3z<)t)S-#!aoDcB2_D)6gU+m!cF`Z5!Bj}?UJD3RgJtI?HW^O|ws8dKiadjJ5~T;v^dnK0 zP%duhCz7qOz3&4?jsi%I*(Nb%+B(6pIa_270i236ek?S}@#75g7!h&12!$RYz`sTA z2?VN5Pm1s{xy2iWE@||L!kB0n9ofs>Gk4WZ?rUs@%q9Tv%;HQJl^dchW43i#n5Gwf z`W_`k!VN|6Y4J(23|svvDKU1l6?M8)|?N&GH_7Rg^L6y+n zk3da=2z0}g?4Ag__cbk6W@wY99gmpk52bH%%54b8s#E;%vF7ES9^R@_>&dhAYNid0 z9xYk1Br;eHc!t{a7JsYA&&rr#VsJsvZC57ply0?PQ5l4MpDDsIP7`cv)vLUB%;GFp zekn3f`p)Cr;}k5>go;l53AR9Xl`DAhXZXCp2qQ4;NR~e}EH`QVbAFKNnX=@;*MK>K z&I_decIi4Z!YNuefUF`;yf!lIuvU{WkrsXp&nl@Yux(k>(KzLI?AQJya*4^GpqSq+ z1$TG4B5InknoH(MDjCo#_e$0{N+-*S1Ge7m8#-Yk%|q9_1V`1<%&E$nXtuocCMY*E z%;yv2E&Xw+)xT@(&$so`^V7GT3`-+OS{iWBBc^4^-O@W|BM8X2>&%{t%)!nvCStjV zAiqzo`Y zi6qb3{pw}dRm+Fr_1Y79?gax+NrjBT0VarqzdukpODC{@yWYQ$IJB{R6L!N8r2*}M#ASbNK4_!ssVFYwpV0fz zS=we^Q2Df{>mlSXBDS=fd5=8lo^VNa^DoB7-N(eg#Ped)`=EuSb@faBc&vsT-Kth2 znBcgqidz4kYq?zVwD$*I@LGvWAFLd=Rv3fwEUbqKw`B(2ejamg>3j3KqguAwgvoHd zf@7rR2jvl5RjqAIckex=PmO2L@9`=$-hV0Aj4XU=x_t4U&=>y+{(CRv|Ni~|hyKpx z=YOGmd=04m=%e-}+iEn|Sg`#)Bvlc@<~y~sv(w$UN$%%3zxoYZySMn3BWLRQt75Y+ z7Eb`GB;oULvFh+P)qH&Lw|Y&)E>wR+F-4OKvVDDrk;IjatN2R1 zl~|{^Tr2z^YH#*0`_9N%YYd`z0$Js?CO%g!2Huf;)zI6=sCOe&hWquQQDl7Rn^nf} zTx;aXI*4^+7HWXmLS>I~DAWs@dn|kznM6$yj$T*+2K1%t%KSAr?By(luxlE2p5H!U zF*1YRrC){F$pNzrdOF`;CMIN#Iq+T1?ZRFEQ0J6f=g@|6SY+znG|I#hY8mF8(-v`N z3Go|W4zK0F%0U5@sEymEz)mXv@6~@2#_K3aJ`JXtg_q?tAJUd>H-DkAp0s|(C_QK< z%d%%J#F8?&wf;#K@wU>3WL=l*sbu1ibQIqBj;ujQ;!bciB>KYygXc8^5l% zc6#qs1fgl|ZC8%o4*u!;#vmv%ntQ)lMqcw|CYJ&e7bCcQiV+`?VdA{e05JB$ZbEUrPzQ9MwB-<~c|ByNctTjDjFv@)28uoRW2-!=MX zxQ{(@5-o^SvGoY9xW9d8B`VnwO(M(1}#^QX~_MqwySgz{O>$nc9`Rm_;>^QjaG5 zKj?>f(;YSVg?@!!=*RIN9_;@HKP`00H8rb|>EEe;=ewVG^7am|H<|OjaeV;!BygCxgqlSV_kejgPfc(#UVB=8 z{5gN(1=1XJfla1rk83jD*r2j0lxpnKTQyc^#j#E=i)|85*K{~L^*=jV_cSXW`aAbM zJ5c{4_qDMh`NIlWsUEW~S|#Snu%1h2EuWhQF_N+4OZ_NQ6Z2?7hNa&YJIWfeh_X&K zL?u-VN>^$`GigaVrzikRSF|)CtzLp`bdhIuI_3tPhkkN+@$B$$s`XQp4hh-^;j31v zK-TYxV+h0P(dzl`3I=gXnUl#5+n+@4+7qm5*;jhXjIhBGhhcyl2_i(|`69#q2qpZ5 zleKmb7QxejAOx>nu!h}tnD<1oOC(^}N*AA+Noi<|LF0%%P^Wfu-Zna(@JY#r+B@$0 zJ4gOveF`1y?TZfsamr6t+Qfcyt>-ewQ4`2SZ*=;x=DXt9VRpuR+cd!}C|h3CBPwAW z+t`6ab!-7WP`8&7h9;C_z%F!zr%(G(%`!PbzuaITP>GVal52;-h$e^(hvl9EXpPYf5BDyiK_hyNyXY^lc&D?I6(DPjBXX@n6t4*0h-U1$}Yokc{-llb8A^ z&9Vi~V?)`i6Vr9G8;s+sh%?O){Ldi~VU@xROC1QjMe8!A$xe&W3TeVeS4fU3VSzH_ zE|W)P7}?jHO#PbSqO8r8)LBywCSTh&WGCx-N#R5V z2Pgopfh#&~B-fAy@1Cl;A-swAU~BeFvV2@GDMowdTE9lL=0o+#Cmx8;KFVLvx0Pg? z9+O{vx<}LsUNkxhi()8B8qSy^fOP*1u$K0qv4ctmep-5(=luy3l%aMI58qhe-Tw7QrQ?>*R>;a0yhHG(ea_Owi$5=e?(Gs=fElCm%Aa zH1VC@A3pZ}Z_u~eKPkc^*yZLid5DAI6Q)s~t3_2wF&a}>wy_%U_``8czI@DBlpeL6 ze5JoLoBo22zIMX1WS<8;cY&7eX{Ni#>PUwYrEI5(m6;!>&ZcW|_RmLG>*DIy_obP} zB-iI(q(65D%#y#LPv;Bzg#NwYs3b4;|75;ZX$^V=z$bYOGMpZahT2+PV}_t;mv8%w zJ!06eLf^1o`mOY&%C?Zs2W+t?8o$BI2yTJAO3Ivb@IeF-lc?4W-nS?IJYMs5R{>Mu z5l~qu7`728mfDV?RA-syrzk384310wOct~(w~W_NLvP<+;{B07X{lXa#@x7Kj_eBw zBf~E#rRGMmB5i3nShb{RS*Q%W1{q3RTJSDt8qVMmc+d?i_U$vDM_b)jnqS3J<>xLL z#|qgkhlx>Dm}NQsICq30i*^i6GC#UJKELA=$&3J*5cL6$MFBjRMJP>$ zof292#_oJ*`jwI(m7ALP@m8m%F>MgsLy)Nm@9w^Yw%xjE`K6oCH;kJa!4N)JM6_%r z83nI>9bZI#8Ko7^@+XJc`U;oM2z{yniwI!!#$F~$} z1xJjttG`q*a)Py07~aq_QO3)m)%JhG_CM>lH!cR6OuIKi^3RSU6%3~q&Y$t2w@bpGd=Bm2TgTHtL0SHhu{^q^k&F367Q zh8Wz|5>6WgYXzm=&;$atqfH^4@`+!pC;C7kb#tcUCug*qmhb0f7^G<@&*5 zyhY_QTcH;(PcWvrZFMvRU#M(E>@KF6nH{yTH-6RxpURL0=Kk_z7Wgik2PDSui5R5C zWvWlTr}uZckN^KLpO0%u?O)87^TmAZ|JlL*Z{*YXLOv8NQhZ4yKpY%Oqk3gplbCN& zLz7Z2g^Rx!^tTe-PS|(>jFgFiFArw-(yrGlnVb8@O7$7y$A+SH$54v zCOS*nZwxQ@)!Uyyw#eJ`n)Mg*Yh|_$>ki)Tef>1LabEmmd1YwFv3X$n%uN+R34 zrIwW5qi(90Ov`^2L>5*CXH&gW-Zn);Oh+5(%O=DQ3TLzR&cOuSC$b^tD3g2syHnug%=dP{*&%%x^2@yrz#J z{rK_l0(ZVKfw9zP9zmpn;eTiz>Y1bo)!##ZLHt1Xkl3PN0u%4a# zj%>eAw20Q&?!QYm*D^MXoP{f=W1Vmgr?8fzQBD_M>;(-SnfB`X!l zCiRnx=pIF-bc@bPL`3bh2T($@$))WitTEs2gun7b{#*X49tp< zf`cO=xAFR!wQ@Dm@cmbyxGi#~4+BFfNj2UJUb@p`fNPpyP!2rjc7zdN(|N#rCo~I&8s&HrwLZ%4`~rZp@Q!;F-doVnfBS3HxI}nJ#$^7#LzB zEQ4%Ua(B=Ifo-&WA)f@)@dn#_)*fF*$WIYhc=~0|{x55g)~w(Y@?YN)Lq#BXIJIW| z&t)E~ynGPHEXN?2_vbO(06k|rxD1nU3xEMa!Ql*e(;=CSfFt599HJl$twGHYL{Cw6 zHw1&54~|E>x7uq07h8|KJw zw-r7r`}+0hD}D9l^8A~z(ahSBo`uieUeAr_%L~a;&%vQG!Aj<4rT_N8J7x{aYG+-gwk-%!oMof_sohR?g*KQn`4O z2f=~dZ9a9YigRKEPH9kFhkpADq1;SX=VYS;#p5UscXJsE?~inC0{ah17#oq2$S^iE zdBz$lfwRsTqvn==R9>`%V6sXu6?b(d7)QavPw>|(wdF0)oTDo+k7fsMW4~=*5X(!aoDPjd_$S| zB{od@54)^R7iWU7-~5$Y&1KN~^Yl`y9{c7B;Nn6XF+pMHu?acp7<=n|^EO&Cq%(kT z{hAJijI;N!>7T_n^i)JQNZ}-uo0XTWGk8}D{mgIPgEbR18`&u-L8mg?XOQ;OEDmB^IWqZR_ZGQ))O_31&g#^2 z*m>+6Jhs4(LCW|9HX4~5b{+$w?p<36Xj{#VmG zH>e-+_!zM-dPlCX7(v~J(l4SwAU|7jzKR~us&sNy$okUBZ6;ygtAugIxV7mlfyEY2lLxNi-1?fUm}Pq_4(z(bLCZ zDeJKa9urZE;BI1%r(Y{}5gEMt;Sr8?MGZJ$5xGyA*rb#Njg!BH=4StSt0b{@DhUjdW?V(WsLr5i5MZGYK;aCd{3$IZ2nc-H1 zmnJ8n$*8^l0wUO{>CAHiu+06&V@OHZqm0@`rYw;-0@LD@g~z1>l@W?FBeL9@Y{yUo z{^M9_GZlToNm#DTin;1SVAe}2yJ!MFYJz=5ql}pAFCwD7R&9fhE%@yH$G^CyiHt&= zym)9AWGhAtLvcL9&+9d(u0Dq}w1rVwYg&gJ7iP$XHwUE-WAk3J83Qv8r;X zB%nNng_IP7nP*0IvpP@!PkxD?gqqZd4=Ua&f0s9Fh8#7nf| z4~K=S)YC@vlTzcNYS|HlllRlQzk(p9F@>vFLPCNYjA$lNI~;GZ6k+|djCES&u-$&1 zR_&k)XaUAW?dH}b6pca?YXODv=7nUh~yQo48-n>B6h$c+1%Rc zMQKE4h*~#xPKQZFw*nkX`=Q#O`AZoL{SH@%4_y(90;dpzXLbiDH}jh*iCLJlJY?o~ zgQsCmW@(w~?84EW-7C8^2TZl*Um|n`n*3Zw?)vdPtU3`V=Mo}Iac2{lqbvXZN0jh< zt;x^L-92#rE*hOr8je*S?LavCl$Bvy0!QCkZplC{kC0hE?g=!650HPpInLn+sSRK9 zgVk5B`oCFs`p28|FL`LC5{wJ75XuLMRotYr#5%c<+(9m49G;fAgjWN#h+@zVAZ)qOgZ?EUq+n?X)B8y>R zvbO0ulWd#M){88_(zCA9nka*=)uT?WEJM>^CzvJHIQM{Oi;dB}w$Bc(U$Pzyi;3%W zVHrQo2W{Httf3uLOt_iS2Z8#3;Sqs;B~{K;^R*iQO!{MtIf<7Tw+>m)L@T^VNjUNY z(HbNuX%y{5N*==*|3^S!hw;p)u_rwpwi*V$J#jZF;{t+xs%CS+F(WMPVfz)hYk^}1 zTjC#yI03h?4*ZRx#W6-rwNv!?q#GlXMRU24HP6XfEe&l#UFfmjw{$s84LFPgUW0Iu z&8(OV_q7|kUq;q5JsND4`zTYUqCZCX=SPQ|MdnUAm-H)1{Cn<^ zpX$hF(wTKs<>@@PK~D~8;x4aT*ZVY4?dQw4n>N}Ak3IslF5s))z2$HcY~@4{ujO(t z{FENCgFEWDS;NIZ=F67hOQJ=lJzd~ZEiQwe0-mmgozG*v4P%3Nf+ocu~>Z~$Ac)~D_W+|$BcBJjNYIqW0 z*FEzr%J+vL#FX`reoCH9vuQr4Z2EzIfX>93r~Kt2gk#nYo4(1TTwv24`B`e9?VUm3 z8aT{R_8hlsh%O&rX~-rsN40o^I*I$*a=N%CwJR<`zKxpcC57TqX8-#;m63Bmu9IO$ zbkYGk^XNGRrGMQWb^j^oUKnWyKngHhGKfoB`6Ael*8if7oTq|m%XZdF!Y#VWx&GE0 z6g1EVZq69cB(z%VuRCcT_HIEf9M>e8O#QuK5qw-*YbHWmj?~wiyz%9PvhY#G_3p+~ zfEhcJovsn+>OV!;4Ao z=XEJhY)SS>B@bfhv27fG=<~Ur?ynd4j56b_eapmbiF>Z@8<1r0en;qRl^4mh?rGjN zLau)PwB(mctudI2KY@)SY^DW7L&;wgt?Qbnm`hpv-l5t1M1Km#x<62v`SS-%TnUL* z@bwx7np@G4x?rG?#d>)BS58E+Z-8t>cK7~azVt6nb;yn`UHN)VClP>vB>!_x{f8JK zsOPBncWPCB`&)Eq6009I4!6pMv_eFgA;LpM@NCFoiey?zE)oh4vyQiFb8P?N!iwtI zy}bvz9R~l0&u0f)h&wTVczF6l4)=OOx>*7V*WZ?dk<;P&$;;t-BJqf?ED~ik<~3W<3Kem>9O+wnGIyvgvRKtOUM9IXloqCXQ5 zCRdHL`jq+sh0#k##ZD&t=`3bN4l6WfM|_j?E6php?)3nPrFg)h)pqB!U4ubDTCtcc zOj02yyMlvOCM*D8RUFq?-sV)mmE6eWvgAw^cX1k3n5yRS8lru-aZz%F3F8iFsenUw zSu&WGG_&Xkfuyb_$7cvP zNDR?h#}rlsZMGLn2A3{?0ajk|6Q|tGfDzPrlA`@3pSbf3vuW@F(^8IKV|HHLflys( zK+ZY&mxclXJjbUZtjnBzWLV+6HbLnFJ``mj(B>UUl+vi%A-l#7J!zR>YzI^IQOw|( zaQMfBHiNpDD(ItJrzf0lgfR?SQ0?Z=h}b`F{l6pPW<=VR%cHb%Q=-m_3jbF}W^ z!3K?Hh98Tu{y=yI(`Px4k+26oyv!HS@XN*0#ZfTP;EL-!3$inZv;D3Zl8#FM{xfF@ zY_j4ObTg3Q$UUl;Net!Gd(It9@PNk^>-gkBTVXnp? z2jwzNVeXni^;Ma5qt9w0dLZJgiq)_^E5byHEBIKcqbCjhHZCmERc#PlvwV+Z$#Rg= z5hU%V)D^W_Y{eDoNr>NOGk4Og3Z05^ikxKE3Dsst3e{$}B`}JvrdS1IpXNe2)?{+s zRb;50Vyhnt)gVQ2HrN~Uy8IdFr$o#7XQ{j*!g=8Mv*2Lyv*wT(s*Y}P1TFD~_e*I) z&D&jpJX(2^#q+Kf@<#g*zxiC`Z&g0#NF`>Ep}$s%2{d-cPdEM43lR`^{DZ zH;vu9o(FA*-eG_3VWc^952FpKF0<`H293Pq);x{X3~rsm^RolX z@GY)!!lMF>MG1iM5!1XOJo~kDF->d>en)O70jf~>oax=K{t2P6O}9lGeRH#WFO?nX z=s8bQlLvX4a!%7955Cr21+qINuhdOh-cOA=>4{8OPW5HNUJ#R310?3G^UH)Gb3VFC z#uC>A1y13nYJ!GZHZSOc)3%@IrAj3bBsEC!Eq=n7+YscIz|W?X)`0^e>oJE*<)Q*; zh?d268iBBDW~G@V>!WTE&T;I>HIeQ|81IOcyx#)pc~}QW6@9c~gzOv`w^ci@cS*Xa zDGK{-%dQLg-;0H=Kmwvv?zk*1ltpIvT>Gn zU&EGi+fD@`nr1`TqxkM*95V?jcraHfFUXmpD303EQy7mb~=F)HxQ;u_#EIWv%~y2xS=Y z6bEnAu8MuOkNG;67s|sfIOJU!@h$1(v~6UWKh_5E|0C_3+w1(decd)_Fk`#1ZQE#U z8;#M}nX%E>W@9y+aT?pU&8BB|?X~v)t#!`1&e?1K@m$yQ2EMw-Jw8Jf^}J7@c1Ll( zx_n}JN4}1u+t!JV6Wef=iisra><)P%_!9{N^3&$KRR#W0Z937ii_(q0NPM$Kdz3y} zifxP$@{X28#!nyn)2tDnv>hzdh3lq}KMPYGie~2eLj>l2woJhhKA_!LV8%xZPl14L zO*?F7renGhVH-TP?B+vJUJDzRI8fpbRO>`Y zsWl-jyUL?*0!*sKh5oGlZJlk&`>FsWQr(0(~C@dbRnaleg+E zw+;UJW-TMoEBc3m%OL!>rPsfln*VWPHmcjItVkgHBCf!|D@iF!$tHI480w=!$Wx(9 zLG(eY+@LZw#5L`H8JGA(7E4A6`fTYZpZF8K_{inDb*5|T;@5Od)_E3`dsGJY*XOOm z_L*~m_KEYeqvkHZH;itJ?>G_!&2bJ9z3VGWpBGnGTy=SkmOCA6?Z`7vvxlDajl*B| z(P|Z5Km{|^qe$#bz!ldcnx`~n0mfAX6}~xS<3k>c%Xvb-UYvbgXG(SEt_h#&yfmXq zVI2^zHuiFb9E%(Ws@<2E1MfldUTvHP!yX4KbJj%N6@BSyJIa~i{#|8YK#@pNG6irR zTZ<9q#UINAKzg*W-A2B`i6my+PmZ!6sJq(_8_vd`p{2iO+VbLg$JruZtnD0g_GBkr zjYBxtJk6p88@UqFS2vyQ?&4cfBe3Xvie^|(pPhgK{7zZP*U_SL3DE6XQDV!>P*&Me zI?lCZens9W-vaqX{uqZTpQQxof?TukeR5dZ`W>Er7yvY^-DLwlr#BsKn59M-1b4t8 zQ7D&nBCsT>(L(ck9Y*eXucHZ^Y4U?h@<-z`D8RmCTY*^&*oy=v1;U z-%uF~3DzsLhwG}yo9g~Ik)HzYxb`@Eq;Iatrn8)s3`Lm{dKd-je!0=@>}5X18tI)z zcepFU12#{YUg;WwU2-4JrMLWMWRT()*3>!X=oc~Oeu=F4udkqd!=ZT(MIudw6deR+ z**guzWdHH1A-8&+=dY^qmm5gRQqET!)N7cu$80NZob}PV8kWc;$b`8rQA3 z>A0g< z+r}KIPwg%|va^Wq%>ta88<~vs^25&njwN^oSF~$GGKOedN6-_qLsT&BX2W-EEf_fUH$y%V z%h->73sA4ZEi2vL_$0b0a&o4VXHJtmos-stQ{}PEFP>fvqy>7d^&TPbH^Ap_NnbCZ z*X40iL9vs!#IO-Lk4aCb6iM|)j%Ij=wol)zLGA1?#@^Um^CF|`im2l#5DXg~!m?>3 zJr~Bgmiq%Iw|>=3{$kpVEE=jDHUsI+jU*z}d90aDmXkL&lk3_A!mnzyujH>2TDr~I zm!(sA6;pil0_IZ4dw`-er?t_@2AbOMmu1>=d`EmhtTuIEfGx= zR=FKv3ccu=JNtJ7)zF5+=g)*ns;arZrX0$ABjvXlbxj_M@tzxl#Jd5Rv*Vbt36hen zYw`+?;cp{|KNm!!N8)|A2dy~dO#%mQFE=T_$Fj+OvY0oVo!ElBgNTQIjz{7zbc`?s zltgoS_L_D(KwFr6s|c2wvh))-_St=%q$Cq`dCWrkg(zwL(bqN;WY}WA<1N?7ELLwi zbS#@764T^F#wJ%jnfgRwzTZXCC~%F#?-mr%pRoiN1h z(agyyS(`Z60OWd%;GHC4l6rb^X37t*rO-f7>vV(u5Y2TC8`0Yrk5epmM!aGrxL}?W zZn73jAvvW;t0J4+LAes=l%`}x-nrN00jlicEXo1I0=qMYqf?~vVM%p^T=D``}NUDw8L@z3KM zV1wHuUfsi83~x;QL#S-BEoq5UZ&C_9x+-gf+zVxb zY<%Cc@$Q{A8D_ZT{x|G$h#o4?=H3vsB6j*{Ze1xfqblRE;)LP_<&Y~wQ*=~L9;dnz zWF#Viqp5(MDG=va;%5Un9k;gO54ou?xV*sZM0IUO#c(aHvfQ*hw?g-Ev@qyz>NLmtk&5 z&LhyBx_tNMxjHBc%#Jjd7N$HRdHkr&`V*6pOE_MO#I4myW9idH2AT%TSB`k)T zG{L+uWzf?_5z`MaNg~>^PRpIxceXx4t0*~CE9-v2wV9f@Y*n2KcA=~F)z4>S=(jdw z2nTS|liK>Q#jr8*1qovi@|j?_o05_cPNzHpG%kDlZIqcIJ=1?BOE~WO6t)L`10ZNI zfQqC~=v0!;lqI%)^rl8AMX_2!IstrTjKB&nxh6}E(efDr;ZH?g;+;a< z{ifD9sHuJTH%Lw95{5;(ecdi>_O$O=+m`tWw~7T8mS1U;)aM$C%|$mno^A5ercRea z?xD^v^J@)VzqTJ{^70^V$$H*9vLCh27d|d8fr}V?g?I7*S2K?zSsQ>0fXJA z-IFVX)BKFH9gjm~(P5t`GSQEziy+mZ`cNu@MbRH&mmmLm<=iwP+kV`zXPs#-)XM1OuqYAG~p%Tl~z=eBcRnE49j7rhOUs|3xs?qulUhx9zxw+4%OO+w$MsLQg=u&T#x#Lb=DKVhcTG~rz zmGt>`Ht<3x>y=<5G5fSZ3^U4TU$E-YR%2P-==`uU#JnEc5E~}gbaOOgzR8RyWrc}! zpH9@1nTRD8Goq#rp)h`!I0PS7ountcx5BCa1)x2t-<-4n;8}`dga(qrlZsXEel-K) ziZH~Vq}+M~xB9&o*51V?#h{iX_(?laeU9f0UTM(w0G93fGF!nKxT#lShE$Ht;jc9A zseLHU)g?lLu$En67G3>z=I`K@LClIZih!9C@qq9nD3M2DQxCxkLd%lp>2X~O_W}<) zFwf57&@E-l$-s_&)|v!{PtcJu@QM7!Z2;l7L1Kk|d`L zXxbWpDDg!fvF2DD27+8Inyme{1Jk@Z>x&AW zy9=&uKI0BoxkV}cBtQSSFyU%0NUt#eyd+7-{9ap;X+*Mph;O2ClBWrKrdSdnbj~@N)(l%33Vkcd*3E2ztHk|xOW!e z#*E{qYQMYn%dj(XVd0r6+Q|J4xEq|62wTdWRdF$wIq(a8ll>_;t?~z%pHvB?m&QJ* z=19%P-l^w=%{p@_Oz?w31evpXHPi$W_ld4QyTN{D z%Go#bH3_{2jCL8bvmoM9hhK^JDnD`Pcm~duwCHS?aKj3gUthclMNd*);jF{Jx=CTv zU(uh31QUt&ilKgw{lvk8`jM}DMXBZSFO6J(?zJRwx+R0)J8d-DzXi_!u`m2V0Du4g z3pm$!@mrb2_`Uu}yrLAC(FZF(jVUpL-@GZzX-GCU&$ryNJ2h{m3716Ev|X-5_Blb6 zM;{N52|Kp-R*q?hwC?w*@OI&PY-?q zFCYD1uC(B)CZFnhu}DMSr%Im+Co^uy^#*1I25Cp=Ydo!^q0V!ih{J9qT$;k2xknHQ zWc9MTr{s8NFm6bbKhsQeMAx6UhG@lK~q7S0lXA5P*uq2YedQDv#O7ou?tr59E@ET8Yj8BhNVUd zID2cdvRMQheVxZ(Wn(p9%A8uBPOwJ6fD5_-OflD2vaGTc;T+4^nkhBzCy8gd@f80| z$e*B18QAC;D|Obd4}WPe=L$XNH`W^IckEZf-VHP=olmV^Y?1*asuZgYXQ<7!DC&>n zMfj{Y07OnpQ}PZyoUa14olak)wk<2Hg_eYZjGapRHBAq#_uq;BbVDCCGK2z#StsF4 z+i@v?)2H$i*n1PoOUIe#(kACHfGhPuE5JB-^pg)q5wc~_8X~wjh-MHp!K0!f1eT4e zCFVHJSQY4ldc+9mz2#==F!!*D#@&-0>~CcId$|=#Zd{xq+qUA8)byWe)ne(jW@&&T z6ycqW!KtICkZc})G9JAM;%qwDg+i>#ErOr4m`0A5zl^2n;j-IEEM;rbmZ?dyB4mGx zCSs9F_T+0#a9sX=f>&Fq5mxRlZ*^}4qBx{6V@)9715I|Oec<%W(n-*aHTdej5G_YJ z9Gh-q?Gc&4b%yDlJHx;%o2tRKG9^Pu;lwU2xnnNhS3grCnz_SpP+4jXlyj3cU9;^) zAz^*WllV`8Fj5In3dLV5oKV)|_$ViP^i79fFi`wRr)3onJ4v*ak z<|?2f2gt1fH9V?rW|X{XSlO2=A$CYRns zx;4n+8<;cVp2#Q&nlLlHg2#Dr1Vi#|1z7uGcf3&YSLJ~k2k>`=hx4#W5aEd(6t!U- zxD`XNiVS=MMi8ic85G>-N->Qx5$SDb@eSQ@j=C6jCn8c@vx{hRrQShG{if9Ih~w86 z%JLR0%R<I8g}m8(eUV34W_{u?X4MNo@NJQV_COj zA7WvCk-qS}Z;2GGcC$az*O&}sww~(b3p(RH8fbS>^+w<>6hFEp)$WKl$5P ziH)onJ8*22XtUe$Cp+%b z%U5(b@9@s-kB4ZTP#r%$qs@)#u=Dd;F-8h&JKwq?Dm^2)>>=LP1f6L~j`EOrJ&!IR zcaZENO{V8lI;5K(IL4J`(gw8;Aws$$okO9`5_@YoeBGiI6L%n*vPChLno&yUm?85P zB>d!txM}C<)N`oT$M6EZV|)?CMA3ebT}#!uEXT1<_2Q1EW!;2LBBkqwz?|=&4WU8Y z5sTfPIa)<3Oy&M5;ado{#sb6QNL(&Sc!Z)?2JP|7MvxIcX0ay|$?NZQ`^+4ltWr`j z$?gP_Ps5nY@@V$B1s@)h{DVRNKT&_8`TH=&0u;7dyRj|4y7xp?%Va_)?2d z(d=R(5Wu{{iT5yBNA^}(6g?XrT_@7u3#G%fGnZniUUbN_aiWN;%{KZOh7ZA44;~VN zGzp9vNL4Wrn*G;0&q-Me-NO2*aLx~9Q7=~!p(?|rXg|GZC_7|aX$$=z)}pO90IR7E zAr2!Q%mLl8Fr-K?W-U-(KbydIb~vwYbO)9=t%BBc_jup`G)m5V#{X$?8D_EhJ(Qao z@MUv;ps)kWKkL?(EThL4J=Q}zQor49?2cfy+s=BSBid6C;Bu&LU|$j2fnia`-2`)t zpEQOzW}8*C!9B5`Z{a-NUKNhtmFCkbA9Swy7zQvv#Fl@QKv~n91*?yLg&7M*z zq}iWWyKj_h2+zrZmgA_YsGn_I>+F-4$Vih@R57@&elWQm_)V}RH5@{9sOar@yHS&{B~;;pSx%z z@&b!%gg)G%Ec)b{J3-BMqUCkhKR+ar%=oH@{{1?^-!BROs1vLx<4Gd(7er#w%XYhU z!`3<{3+sM)-wF9)0Gci;tOXkqmJ60JJYG}NO|6}_{V{mg#<-oPIH6pX@H(!LhA3`h z$V>SMhdviIf<9>kbUOai?cqpZZKl5K*RNl(?*xEj4l9n4(j{c4YiAvWV;uy8jfur) z@C*Cu)5B*GKQ73zTI~f;Y#RJbOh;Uw0-y&R>WTHp1@#!vgWK&QN^pAtdL;n;jlIuY zpfAqneHHr;trP5-CfK&q^WTA(z=%pK7b$z`TaL!8q$LhbU#SZ+09Xny{SLJ; z&FGK8RvB+(psYTv#fob~zN~W{N>T1c<4Na_otiZ0d|NZsNnL|*AN5{IflB*EW!u{ zQf%=|8VCg?F<7oggjPR3#8ld}3{Fa)iJVnCSI{Z=9~^(kAqY0k7>J6YUZsqn<~VNX z+#yPW6s_(FdhT{|TD6YQD(}jlHpk|INCC2e$gi8q4a@Pn?%L2E7`5KeaR_cS4O`rb zhmLaPcB^tbLeT1@#ZhM~Oj)(3P(U=&eYB{K5H{{qI#klG<@hN+Gcw~`DdHUR)^E51 zCHpDbn&Wm~%%&?`JlsqbAX$qkeb)L4usJTFK$BYbiUMqIxy z)qZ**Sh-zo*u*4h7ADZtqD^!C!js3zVLCpmk~yV=Z0Mc@*;D zgt-x&UM%v^NrmJ&RQYJ@HVM6^^j1B8r5ea#>h1MU6&B32Clq|MDpT2 z)LdW%wyGliVcw>Q^}KBTCCZ`Nhm*W1S<*n87KLq8)y^X0;QWf{ldn+bs+}JVp%bv- z@D-p|Eq_4-g|?_wyh&ovr;|WfbKp7}YIyT=gh7l>=@BNir6=+dqj<6Uq`0u3xlm>ACxKu!OpS*`W-2c}s>pxE_ zgJf5{QLx?n3igrs|NAZy*S}N>Y{1GqI&i~E_CiSfvR7Vlwh4w7Ch&tqb#1XaDnoeA z&xWj~$Q3OX-u_#IBP?mQKT=|p#aG&Sl5kbdw3RgO1CN8Br`|7bPqhBzbp0rz6oFsx z-^)o}eu_ah&rgjeg(D3NMfu4ZtRZi844y#X!r&}5R;-5!_~oGzAyS}^Zk>ZgUkBJd zLv2QEWm$6^#hzxLF_PLZM7N&Vjv=*tB(tw5HzI=fQ1!KtI-mVQj-X6MN~k@*)T3Y5Ww?c3;UmQBV?9`$Bsi?H@~AH6cJKsS`8;L2m%z1%{` zg|}(?YHrbu8oDQ7^U*7c;1-b0)+ESS*NykU={K)`A(Uyn(P1n)kWkWDJRwLV<>;|O z#4FSwcg6@?&cd5BaKm*LkwYbS`eW5pMY~7(OhpzxsNghmq;auh_!&T1+d!vnffYf$y_> z7wA1Ez&X3>hgn2MD;;(m5{r&Tzo+3tGn~%LS1YGHyyVo}Z*Xo3d4fzV)WC zcMfGNVg7TN%X3ZRlcbKeUpd%;r%6;5X*uCu2l-fwWSKL(E4~)wl$1dB%($4kx%K)taUSDshmySAfIl|0wX?6AtW9 zV$A!+Lw-{EKG6)D&*^G2nLG&m7DpBF>rw03ww4>4h>W-q73UiY%Lg&{K9336daF{;b{sUF z98ID3IqupaNM47p0o4~=*kh9MJ}$sWJea38pI z5$J}&MRrY(I%>*TYvHA)^y@Jwczkz<@8>T#va=?cuGkdkbjp2ny1KCXvbG@j`|XL- zABWGp<=7Kp##Y**qSKbAcf*B$umPW(Uvjayy_Vc&gmWRd&TW@J-$4JCZ)$ksN_VeZ zLDswn+uXpq&xUuzTIP@c-jLpp42omK0Q>K#yuA$}GhmF5FX~B6#Dats!T48 z2ji!2tRn~f9pg)o!kd6Gz84EALRreNYo2bbkqh<4SI%zIB2Zu1ZcZZpu*xpN5t}&e zgoC4*qMY`$N|xGGeq7^Ix-EA~n@BhMhXqQI|ME!P81e(gDCO$9`OwcnCs4@VwhzSM zRZNTtS01tLCwO^Eq76LRoumL1aHqCUTb6kXTbxd0WBRSn!b#gV@qqP84`r(1OOp?i zqdsJ#OODM&tu%ARI$hM6ZS)8FJq(D@3;JE9h%#Ii1z|{~ik^nadMgw)bPL1A>D}me z&jNQ>-Q6C#*a6{@xDp8X5#QUzEPX5uNqWy&)&$I39Q^hF=0*}X**Lj|ha8{JfRWFXs`%`85{_s2k|JtLBQZ|m9Y#tl(d8W_p8zlLbsLccpyD+MtX+044Uh7vxA~TvWz$WalKb$p#}PwDF{=13A3|{ zBHigiBGRakewMW;;C{(rsY*6of%^FUBqPQt-?I!<#@{eLMgwjZ_jkkFncoC$@{_}d za^HMI-p~RCF;4?i$E5kiEs9<4IS&N~Uq!=@cl7SUP4BL%pH1_hRW*J|)jg{gW`i=& zW%>+|ARtEkwlvvq&@a@VhS?@=SSK9Lk$x6lnk7#ZKS{nO5^lj_PXG96f2y2qw1-tG zJ|GM#wDAuA4)D&P9%nT+{dur|~kg!a8q_Yo(q$P{`_Q>&7v=@oks2uP6 zM?nrA%#VD1e>gtUe?80pTsqVJalF^SN9hPyfS38VkJCRG|35Z}f1>#RslU4gG$|G$ zK|-;?N~!i~z{{+_Om(u+Ypc3s$bqKM>{i!QFMs()UOeddO%icQDt+ENyt=CQOY*dE zYg?n(%7ZCkt!I7k-Qau}U$y%EdUCMwPB)a1jm(kA{`??1&j`(2dC?!$e&mM{jro(d zcs~k8F^~g)Z^QXVtuk1ChtnaI4dqmeHYc3i?A^%`u(!s=yv&U-kv>&-)CGyUYl2AK z*}~t@33uMb(n_6Ad|v-XG#pE_$mTSw$>fw`q~TQDvH7!hmFgyzYYzgb*Q6qGG$b&qjmt!_HzAk3Rvq^%fGnIh7JA#s}(8;*rp)z>b+0-PzMaU#umBr_9? z2-tKJ{YHmd{Q7RuE3;W_e?R+F+u##%c#8(;f4AL7)B+wbYj>ITInr{MjGPwiwPxD7 zOX4q3^b0uKf=0MmCAEehoJ@fB4ByQvt7wg^c0;`L!F&aEOx1QmW0t2z@N8y%( zEakAPnJ2=c3N11AFD!i5jc?!>q%FpAgwGXqofK(h3*6h{hzUaizk`QzeIGqtbRQ$i zA#kyFTN)*ztu43b;aIS^7Kf^c=u}F_V=re3D{qEePEV0G+*V=jpk866rK3*!rS>`8uZ&G}D6oNOWn=W#2B zEph_J9Skt^3J#2+`aVIE>Bw0<7k@BjX$@C zlj&Ng=AFAE>Rq{wMv!CtEn)(F^>O2-)bPO%Rff8fCmW~FP1OAWH?mZ`dG4TpmPOX( z6N607a2@S))DUHG&U6gWil+<1H_gKq8$C2l;}C4Vx6Op~u%h%)mQY?3uSYx=N7(QQ zj8?2fTod0YP^?VQ4m4v1m6tptww||ndgx3|ci-uI&xZ!;rv&eDfz9`b2`fI50XUWe zPop1I0o2r;_mvT{w+tGoS$IzS6)tLP#Dm2TtKaExVEmZdvr3~@)*W&P$ z!6e5owA+o46WoM~c;b)x^!PGtD#cVR!r#;Oc@7u0PaHZHUB7~Dj#Z(T5;z>GbVb_t z={3_4^2_+jH$thkW6G2p;uN!K@jOqKu|otJnPQdpab%Ps`>K(XKSF)cPy6Lr-gr%4 zb+#!_@(m)zy2O>KTX0@>V@T6j`b*kA&%y_@ZGGJ-S|4CiUm6U=gOZbjzfDA-f#zU@} zGqC)e)Il_r1;?#C<`Qcnl8V}j*@WCoKf4ZGaSS!IC2bkr z3+!*TE6K^<%}mHlyso3D?@(o4u=oZWMDs+B;)g*j4=fyxW0M{dPL|8gy3Hl)2+Gyw z7H{J8$){a6&Djv6YK&8cr)KZ%P*JoFWLb}532!ewg#PxT|GfSCK5IZc3y-J$sCd9n ze8MFPx_<_&!2j}}H+0;2XlY^B&5V(hM=k05qrgLQpc?7w4~e$u+&+NQgTV^CJ9p$1 zgd13aM_TXyuwLNw45J2&-7fprfGeT`A0ZH!Z3}@Nc;0{a5dCMg9Ym(CjTv+%glc?1 z6V$T!!Ppq)TWTZ|$+j^v*(RAOnS`;?Qjwd6+ZF8aLZ6$O+@AWdP5CvR$Am1Q${(;j z^siw1?QZS!yYp^yY{ymOX>Qc}w{zj?{q^jfW$Bsw5F2OOquqP)^@oJfRZgM^gk_>E zH$C6V0Or+dS+(JbBsi!oyY%gNAhM`rV~5VmE3rkY%~0ColS|Btt|%)gYk&^HDvU+< zxkMNkEwaNd+wRh8vC~LFgtz@)U z87MT8?jM5=nmtc~s`j%l(`~Q9qL_mDjW7=T*YYhkY4pSMVD$jD7>Gqkv2?&1v=w&U zNBe*U9HHdrmH?ZU>cj$jWszo_YQ76B&>Zf%@+Mlb?XwCXF+4I(W2?uVXRTct=w#iW zdl2JOt4!tITcRhsDDF4W?G<(cLn=J`>{To|Sh%Gd&2dbnX1R4sw<^yz;3^&sR1J;{ z#}!u`RIso5BGw{Cx}51R2*%p0fFH5K(wnxxQjHNPCk4=EbOOaKSRRW>cMUt!qc8{3Dg z61YCqJA)W=l;ewP#R3JkoumV62VT z!uUcd?OKYS;tj^yJb$qE@hh%C(Rhls_L$w7>GX+FdmPY?^S)-lyxBB9l`;tpw}D)&MQ2$_N;a<<$>YpS{C2*ZED^x=g-4#lG;}UTv_%;a*#@gwVh&A!Ga^aqE^WX^E!yk?!to=GwvTUV61K9F8K#*>rF3~c-VwNE+47z+BVHV>nZ*R z)|PmAuF>-Yt`Ax;)rt8NGc%K?T1`QFa@}5)M;CDByMERUwMXtDtsn8hkgEkvr z<}?aZdKfv{2p3u)*YA#HM8rRzHfb35^|$`}!kqU<*a-eEQECv;)7S+lAansZ|AhA# z@ttUe<;-uTnw|eL7)s4IodY8CVj#a(Tf1!E%2EM9<4O$cQ29)0; zljnxF9FfpNV&F;GEczFD$80KU*^#s^Zv68D;{#E5{REiTiGXDxv43N46#Zgk>tJU3 ze}&sp!vBEVVIq)t?}<6)Wp)!D9ozoTaGMwkmBEgMm(^lF^MK#^<=4R=3BYHQREw}`&FN_(54!;1(lt` ztF%j`*_*r&GK7AfypRkte#dF$JsBwxOTjXWb6w&=+zgA7 z$8(34_j#tkL=wjDETa8*aO~qu%ucZb-q+VF^mA0=-DY4J#=TaDQF>fn9sq}*;_TJR zoXqgvg_U!{C}u}Ll-u!VCf_hV|02x7`^o;N2M5eT7fp1iBz zx;vf-_gRwfvvwEh{FwpQ7SV;}uM!GT>ry%8@Xfr7N0`{Mn8)qD@<{A}@QzkkWdcFcdhSOG7XM)0d#;eWbd{x{BDr1oE&yA}O1kaL#CRyRe_8A%>m$q?ip4v#fsdDY_+3eG097i`n`P5KIQQ$@%?*}6;ZGJhuJiJ9 z(-zIo_J_Sg)!fy8lyzEw&e!!CXBG2198qnZSNf`l!7nDPpc$nF_UoQh(z@xfv}s2S zCSW$})M{qjJjKF+{KCjkZ6;IE9t>16Kz^quepbZW*NoxKoIE?~^PmMLHjU^OBxur% zibfF|SJtpT7+O$O&{}a+5sz$Bje#RtE-sH(OP$b}!Pe2tcX0z5?GI~QIL!e(FH^}tL9}Rs({@neJ zJs3p^rPl;OL-C;Gg8=bLmXisB%XPYkiw(9waEih~t1ii1y;hbpLfDpyONHru_OtaZ zi#K=m`41QRT2K8cMut%*58@jfJ8ht?4-4Wgqi(nbjko(alUo_I_?JS`8=UIRkkI8^ z<99AvUZZ1spPqA};q+N%&03=f7!AE(b5f{?$nW54)BvO-xhO-#kA9U3@0p`KY5K}q z6@_LSP4z)73-sQpxzM7K1uU$)79^hK3)aO3%rXF#aT5DG@3}a(*v^#!H$o`|R&r4Qh?||pBc>JhGk|ORm+{3)g!rx`&NxyYI`&E7cu8Dtx-Btr`qY-~*Wf_GXClxQHfp;5mswRn`VhQz* ze51MTX{D2rf5|qS4jZ%@-}Kr0~N7^Gp#-+wZr}1Mn7wU{&)?+L;aqQ99kgbkMVpqo3z`T%nm!{QD zdcp!*Z}?#q;Zo{|%#r4)giP`p-9xlRz9QM8rGbNrf_`iCyloE5zC+SY+M9mJbfYJo zBpM0XTJf5Py`&Q$+to0cVj8z@^;%z52WVWS$INgQ{DXaC%kIcSkTT7l(YML7>JkoB z6=};u1{_eHnpbMh;r0$uzg_ zach-UE_<;%`F>|2HG?}B)g3c>PlS^i^{P$sxf+KH z&Fm#GqtugC3n-Pg_&91c)~w^ydNyG81SKC=npfRuE4j=7a;u5OI9jpW@jK2a0kiM( ztxhoeHsx}LsYz33DYdYywtkG*NB&QRU2YY14FUnhQL&`2 zfrFZqXOD9&Ie`7y7hwgkqja+NS^GW!NB}(YO)M}l0%yN0QSi$?WeOZG_4n^0Aj}3s zpP?oI0ud^x5g9tLb%5cbnCz4Lcnn^y!p?N!YagDAX59*Fl9nB}hCw%Gb4f1y-Lna~ z|AoGRZxFLYDmtX*&dQ&s(x|gupq^uye~TI1U5WYRcRjo33oL2)&?pJc8TimBPi&FH zk#E}r1K#fmI<%#GL@iXjen~%gTKVko#`&}O(j$4I=*jCf$fuW-uGk}u@j@xv@Qm_{ z*nqGJC3B~UCe6maZ0z?d?3|=WErnjB^4AjsB~msS?-)*zKmybOS?_ON#9QrgOv#X^ zq+!u9{qK>k+|U{R)wf`eRhT6O?+&4u@7`(tPY>F^5%9n68UF;r{m|zyevg~i*T=$o z2*t(uYZI=0QC4p#B_{^*`8RsJ!|V3Pq-;+#8!|UfOOuyYYG}l?p^vEYQLLIPFQdnR z;Si&X;YQWBXkLNp9Qcxuk&C$1r2SFsasZ*~yHGlUvP~($kftTb)o<6`@~*YW3%* z<0CuVR-vHo%u#C;DDZ zT2>+YZj_tSjosNgk$M;AeY1q@t^O(&N`Hm5EzT%JF{$6vD?SOd07wYQrR!LTBg)g*1h%%NbXVlt&1j#|Q z8D*6iUAW*R)>{Diw1S>l5^?_N)Qy|1VyWk8*s>1!D?bP5(}b6dk@01!xqTu#hH13_ zn`Qp3gWpXrx!Epzuj)1nEf(u}Rr&}lcgBIM9yE)gHgLoRb%^?`@Wk~Uir0keclcfM zuAYvQ*n6g&s@Rlr-Fe5Jd)4{Y8>$~kP{Y5a;Zt&)vE;p$;DKgVTxAE*Np`li82roR%es)vVAzPg8eqA<8@p3+Dp6Rb;ZSXuc0E=ZGCf>?5iPr#l4VGj8FWI^dQA#2p-$h=M}ZjN#aYp5|g>(>5dOTLc4&U*hFg?@xv|gA;f({wGse$`mCTSpN<61AtYF$}Yd zffVMhK6E*e4_D#OuD=oAf2G%rK#b(8pD-hrg6_M_y|jgfwdL$5d){X$a7 zSIRSef(DnDaL#Y&H|MHtHs5}p-H*-_ODyh$G}(UIorA|!7)Mw{LAgTG#!}AnsBKPZ zou?oVUcqXh7oV7q%I|N#yTaG&lW4A<8jTHQH{MT5qOoY-`;x-%@Z@41ui4&Bet|O~ zk z5n8lSy0E+v^?A=(L&Or_?Wi4Vw}!1{Xt##WWKY?*Ee371+{Ae`3^7GfN%jUU(Vsd~ z;=6JI2yCnpzu9OeTK(U5er5cacoQr^+ziVaOkr~(1wj?fvT2@gp1W3}*Qq|$d7r?~NZ=#9ap)2Q* zXXO?b3l3eJ6G!7lIAFh6>BZApxP_x}ABHL{!{sRKubhI^(we&v3{uvsJ|)ck-by%D zM~89V6BQtw2IDJz+!ZL3(8UvcFdX!f8U02+h()Pr=NP4pEaLlWiE{!q0CWB zedwR-!qh^eX9^Zqf2VTiPiw0CIbN0WK)ic@tWYJ-B^8a{t# z@F!`ACcjKHW001DRp=#>u6b<{TUuM8*r5$!wX9jgqI%=hc0zF7&@9hjQ|{3CuFY|f z^#SWEr?+I5sD_80*(4zW<6lplAec@gJys#hS+tU}XK5MVLHjp!H-l?TY*-8e8 za(Yw;Np+q>tYuzSyRb}(^X~jkn&%-uy z>!sWJT1Q>fsu0z|o?i-)|B=ZDA3GX{q6(|=9kVawxtFT2V!ml>vPRDU+-DE4(rcFY zbELPwlx}rB--9QCbV?YIP7(RP1ZMwaRW?-CB+%b6{|F0Z7tf>uC!YZcgF?xeuq2;M zf>iI&94$%gPFAa8g;M#FI@dvtXO0X!8ItL! zbO9ca@R-V7y!v>5r3;ugVwK~zXFf>@?ds@CYiw)TYjvM%^xIiom*HvCPkij!M}3_1 zyNP%hIife9jlRHHK7hi-9+)hpr~ds@rhs_eO_gsJs4v7nbRMhUJ&mKmw5#(vIK_YI zf=~@VkJZ3iQ4m{DS@7HZq>7A8VEV3m4>jikUZ>8em8~ODzS^$3SffQHW5L9lW}l)C9b;tcdmky2qD*l{+xhJ&B=fEH{W4HrX!Y)o zzL0ng4s7IPjGXb@t~+yERU;Kq-a4)=;zNoYvTY?pZUVy}eIZbwzR)jty}m5v#{BSt z@58mO<+jYP2`jeSV?(rGXUStOVP@#h^A< z)r}EL4x!$^mIfIAZE3)C&@CkKZ%YFq#(F%go-2-wcQ{E`XcskC`Ge0#~_?vjv#xC5J|a}*~%ZoL%Jr7@V(Ud ziG4oeH61VEnn^?UB>P>~% zdM_DYiu~8&b~Q@6ZYzo-{0Y)5N_{BY?n3u89lfzXTLU((<-;sYf2UUl_oz9p*#}0o z9W)TOG#n^1d(HuF25eait9Wa@9@{+439ODVX$KgRBQ2&122Se_>Lh6FE>j#`JglV* zsk9~kpjWzp^h&kMA9{tgnflMxKxjuNIdqmzXJWJ6j>;v!;*5%+3$4Auk1Ul=KWWeR z!UPTem?SXcXFQl4g3dsNnIXkW%)xxw56exmS`%-{8~*kg`K_o%Zz0@Urs3nZ2ly+( z-M6io+oHMk0t>pIpWXwcr;#e0J)_N)zPH>IJli85N*344-o+uq3?8e-aF{jf+$_BOq;SF6cIuij%uYh}h?R~a_ z2huA%_6h%_S5Ehybpvr0|3X1_u zu?<^dKc&1sRZ_$K*&49V+s`{;7kXw+h`Rv_42kj2aK*ylD$DIDfTX~Gi-0E}Rga%l z9}p++O?V)c`w3@C_z1>}Mo!L(jPnj7d++X@l5<(n>jX-Me!${~M9-Ko#8I_voKXA; zNU!{cE5zHu8Wl(@c`5NjzoxGzNp7b}ax2`ReJWN(Qvxt|OT!zH z)oif|jKNZGxr34#)#2l?G#m+ z(MLYQ`z~AKprD=tqVkD79_5Rk!eSDzV`fKH8^FOjD?BETIaaf3bXj`7IzaY?AZ>?} zHlmG@4#H=$*-f8(EYE-Z_H%MaG=g9@*3Y7yFIQ@k$)VZgo0DKJuL4AIroS7YMcOsk z*_-M5qIGr;%?}||XD&6OR?35rR=uVf>x_?^_Kim-mcK2iM1IwQ7)5Td0p3$r7!@1h z7je87Lj1I|u>l7MkinLJ*O1hTMdYyQoYW4kq+L6)lHnv9$|~R^BqkyqToJ?E3(;Bd zD6@@KTl7y*v(v}2m0cmz|NTR~ghVDhEBI$pl-O<9YhM>b$*ut*Svov(On69>kE_M9 zWu+Yz#-~y~Iy@6x){sZAuP)W^reXmjAvdLUe*9GQl3+hiyKm9IljLsWkX#WjJNExz z_;XB?`{OY!Bpb~aFy{_EFpHI^+e??{BcJZv7SCfv0B7dY?T3-`C0$egww!T z$G>RY@UbuH4gntl7qF%Azs#EddID9LbM%!>ojFR+#FpxZ7T42DkHv~+N-pft&2~3< zR@iqGur;4JuD)d-s<~=E$Kru4v1{J5(3~^lj)S|wo+`TKe)gfN_`%AZ)7!64KvX0{ zkX6UFCKG-{KgomO3Pok<^s}-#s~>>@rjwD!M~AOm#lMILm!){pv6-rdHG`n9`STKq zc~(Z#qM04xK;)*g?YQtuIsA#LRA&DCi|Fuxrl_vM`sgF!Z~xSc+|s(RX&m?p)3LuR zT6Zx?iNFSDg3nyD%g<8OQUWBgi~}~sL&^C8ji<>-669W_Wu)9>R|sRq0B*aJ58c(+ zt9aPd!;bUL_&jr zIAH(-#s3ak|078J15j)J_CQ<4c+Vx*ZAcJK?88PIkp}don!%wq1kf-zY024+M8-xI z&=AN@KNwj_#7IDv*|nQnWnJ2-9E)&dvQf|^fWvd!B^f@8Dt;7a=lWkw5>IF`2W9UD zn;%Yg0BMnqiH^Ig4}%w~fN&@IZU40lg*6B(g6XMJh9WLgUX9g8y%Q}7Mmt5qG@O=n zLJmC_1;W(f^8{sRQD|FT6`j-?R5 zY-v}voJb{8N*3Lf%7@cu9{5z4sl3cweFA8P4)Kd8Jw}yU-k;liFW~nf7;#T8-f$hu zVUl{IcWiXImZ0;KOk;9e9M0izr6q%_vC{Qp{*})eg+2$m!5(V+WhxhVwA2TGR-8|BG;x8Fm>An?8A~!f`L73wFP))D;8E3)|XZm zPeb*$iBuC$P%@ck>A+zMVK~#2Z8(bC8!35TVD5+Bs|17%sIgan({DqpDck6qWj?gR zj`>CVoohGUOSSDsv-V^q8M80Jw3rk67V>fRST6}3U74J$`M^`DP|VCY9iN_cbzB2H zaB+_k7VIZBC7@8xJh^guJlhqf4uGn^?sq{*EUW$ z(!f{)myp}`_)01gb$>lSPby@$moC-*jZ%BxX*>VWp{ z+)C@@A8IIhBl{|0)MVDt6{b1k#g6|L_oi2?-IcopZGho<;{NpVWm2MS(biQ0V@GK5 zyD8`wwfWn;g&8R$zD;*C=XYqLlWTik6Hk5l@lT!gpTAwUxNw)_?EQF56>`0r8C@By z?+ct|e&~TUftoCAxX0?;2ypR~G&-xE`fc1f0e>)4F?}EWwaXSoyqaF!UvSWo@JjrK zvvWGbZ6UiE{=w%^{oB@*%)Q-Ekwt4tUdmKDb4Ic?Z3C->{ko&v{W;TcS0)}aJ|Rrw zuxg>x0y~Sf=XDDk(^pfsXC@P{R0;yWe5rA9$%~}q#22~hN98ql^>-{jF!A*P>JTl# zPvi@daNvx`WfEeGoue-j0ylUOJy^egu?4+?p^jZHU{uNa$K0c7&OD=Bc78ixyzyw- zyg@##|LTmi2PUv_Azgsr@#&c{&|OE>L%tEMH@wq939zddQ4~n}_$09^)g?j43$zDz1yi8|0oW@>5jFSorz_%_MumR^u>xQ8S<;3N+19>|PvjbR4vWK8P zk+#O3@cY{^vKnjVis$F*CRPUcD(XX^3I-uhG_5W8!9l|p zvMpKxm#c^(&ls?AY7OPsdcoHcis`h0awye=t&mnpK{gAV$jH)pgt@#`oirSr{UQmCZdVGsj9Yf{_%bJh_{e(G_mn!}2z;s}wj>)~p}el$oI2s&|ln z%llwkC!Fb!Cbl=T!#i(q4BL~@&o#mX$M0+A3?uv9Aw7)SS42i>EmRS7JO|u%PG3?Y zFoEM~j*Dd{RO@&6{t-v-e_0NuecDqi{m+YGRWsNBk79Ua=dx9y_h$cl#GhK2>>sso z1kxBP^lil^d(oO@+NJH-t41e|7@A-J(fEo2c{Dbw-CQGo*@vOx$I;s3wBaXPVhUSb zO|0f>xuPX>3HP`vRuMdnsnOIrB0So?I8Qq5$A#{s$%@^t1U5Y9?KrwvfzWtg%vx)V z{i?*+UD_v^+9^%##9jGz}J#;lh&%$Ewl&XrnZ$#g3FezsvMDQ{9caP;8Q zU8S9GC=nbpk}c`f98~`?nBI%fzx2iRsO!6J+m;53IT#(0DdJs7ndw)Tf_E#i5b=IB z$s4I@7@IMz=I7e={tIkQr*NfT32rJPo~)WWgeroW<5T;MI;TGsjwwh zJmNHOL2bdR^!UXVt-QS|nRrG+7En?OVIZ%6X^PMwq*hu3YhiRC)cp@?;Xm)-ztzHa ze@1@%A*CAVWdjS!)z4(vSIMjCQ#EY*bn-DR12s$y^P73(hd&#$W_{=F8!)_HeloD0 zKKc8|Pe(a)Wthv4@b#RR=4rpnoQ|H4n>7~@RwZ~B;T6~5I5pi^JI%;%7$M{LDX9gh z-FSSPNp1vfXCgklx4GzR?B%j8142;VEo5*;@L2EcqATjFHnNZ6KdQORJ_W-E7tR`O zSL&KbwstGYb23k{?5E1q-*~n;ubdNyzXBiua&b+A^;!bwnr*UImXXv7l5>5QsGOK1 zL#e)^&%pEPHsvXNJlm1WPAW?>{4Y|G);2PWm^r8ezZd;0FGrQGY9bLa%qAE86uq8d z`)KhTlbQEx9(+9n;D;_NuuxcQQ7hUO9KLm`w4H6UciQivZ`LKaC_FOYoG`&o9opisxK!CtN_Y^wb&jLG6- zK<^~JCb;$6yn1$%avwKtXwRq)yllmm9AS2yEn)zs3%%oeSOcA>@e)SCWFjA4Cr^F%*kERoB=7qxS1D;!JkR1>1 zX`xS+%c|5hGSfi7yYzE~hAsRKWbEB+i_oV^_%xRE>0|K+41<>XFkMrxb@TL-$^aey zUp&v=nk8Ky*~V-ctD!YVHt`SIHiCtji2RGMd0}@~Ol7d$y##pdGyIID*>Y~)y7p12 zNue*ULuBQ(5cl@Fvbl*@W6dk{hE8a~%$OXk3 z-Xx5EyTxHN5$D&4(fn=pE?;Fi6lGFtfTU|RSKqiPd`Y#qtN5?+@jqjH7(bP>n}MU_ z`>6j>!3y-`zZa|}nlGAo%P8+Po^<>cX0POWu z{o-h?@2g)m=>T@C!}3`kMACWgL{?d)Cj)kNkH$&a-To&?{BOz5-aL<&I#N=QJN?3| zK95sBI9a!T5WK%_A-zLx1MHdkYh_*{}sx0ejI zUgFC7W}ZdkT+$lBhjXO3jHlY5dy*!*#T33&Eff-uB3I9{xhfk1XCILbVN_dgi;c$& z_9K}u8CTkrH^wtM7qXf!hMbj!=?47ph}4nIE$^pT!QpuP+)GR3ezF7Owt*ZApGJoZ zTUY5qaAwu2*LbshjqaE!`O2nj?qQp_w-1f$NvR27X9i_N|Hih3kez;jf4C0WMxm1@ zWUN~EJj=wAhNs!aCc>yA3fUcSt^*e!HAHPyH~{OeJY(Xgx@*}I#dy9ngIJC5>D&yu z4NfA`*wYjlfkre=&>88HrP0i;a-Zj*YU0|{)_p+kH?QBlE;?ut+f>?EI4bASnDoXN z>v;`zOAbl6AuS#v6N0ucH@u9)XLo_&M00@q0SHcv8$Ennw ziFrIQFHC+eZ1B38dNMZGoZCbkH{l|5r_m2?Sso?Q$&_vdpL$=U=q3SCF<_vqW3pYP);jjwt+g}+dCc44R)jRN|tAI$WusB>9>huCjtcjX17p~bj zZQrmZii}{09Ndk*LuWvTw%3O7UHK34S;J2oo);2U{migo5r}Sk5`pQ93E~Pv_{U-#lQ%>!@`sLOQ`FgJ13E0>Nf=US?Scx)!3;|sDSPF+G1 zIrZJo90QKr?hcxJANu<6qVhx6F<?kc(+nlQX8KzTBS#A zvuvQ38l+F0be%oRF}aOzPV&ZD66TFyi5>85{fuH_SvTYyE-v|mYyQhie_fIewp)&y zzRC{pT86s~nXJ+v(By%J9@E*9mOB<}_Hw#a>N@`eo3U)vuuoGBxP41QZb$)nrtRc{ zg0ckFSZmF1*xaz|E?azj%81n}>TS?Jp^R%`SP)%8Ge?kDB%5dAo%G-qRTZ@S?Gv7} zc!^Vi@=u4*NxXgxJx?%0%^RNXzPibV4H7>owvU1&Y})Q7ZHwwITty!`2nS{6lwV!~ zRz5IWogL4EGuH&ZnnNu&{HK$W-%AB^!hE_$3=lMjsIhySGRv9GvMAGPW|i*9Al*LO znm8YdJbyM%qAaU`E{;vI+!5?svwLFpAHSXf(i9MaRPHls9F(QMRTs~-L}$+<)cA32 zo**{WZxH>ZMYV@Vb$RY(NrkA*j#{630(k~K+W;du(tcC>yW=h2+qp}4!?a$qP~xx9 z+C^i(R?QC0COX|$wcfmv={Gs6;k?6L6q`9oHk&CpTMskxYQHy<+^cxFK_~;I-cSXE zwV_dF!6Kr{@!@-tDiOb#5EmY~>&zx7=Jk{=YJ;C(MGU_{*_&Ftin>m&59bP+ZpqU; z;ytfZD&kAfO{%Xsf%lXwj_}j?!t)3CM(qkTKw7mVlh@9>WCfrU#6qvu<%RGSUFC=R z2kvw&BgYCLKhq&jffxB@sFS~UM@fzdiW!+-GkLyZCbc;Iik!)v$*XfPa>P))gxMf& zBlf2f{D~Opz(fKjAnzY<1f`IVSHvY{I!cnAq!rF@ik`KHdIq>0Q3d`Z&Zq{Ednb@0 z2q&EpGw>J7ZeKwsDTTyI9_L^0SgBn7cHBf-zkH&dGX(>$`+`yCuyg+MWQ#n1gLl@? zDNbB^(o^zXeP@j!P)HzFMv7s-*NepvCJ(?*D{t6)Jimyn6e(=5U&sY40*MVwr&tG~a+w%8ohec(^}__J;X12QpGWTq z*06x|El7@rv9fpY6Mxq-?G?{yJ?jefTv@Mx)K(CbSa*|e(|Gk!iP@yj#XKMVu#XPR z?1cY&#|v;IdjIyeV`WJY`2(aDwDRe2GOAK+zJ`{tQ|Ma_GLk`6lrN{ac5<$ptO(0* za%h}yPkTA1`3|-DOdBM}UU}Ix&#FV$`sL@CG)rh7RG^wDaUWoTc(a^cCPjLFhN-%4 z>{jXk!=RwFvFl28ahr7L}e?@lxIvK~KTOT>8td1{Btw<#v7Y zTgkPv85q7WbRU0Fu*paxA8h~y>Qn$XV}$;v&1)b6kajh5HgsPjK*uNrx)vNolhEYD?*QE#|2pB31WO39D( zAD_Sb*l*?D<=pKZEnk69CYq-=<#gI(wXIBEob*#)0Pi-)1MfD#%eEL^-d53{;4rGX zewJE`6mYS%nvh8h@5L^Cx4KFRWrNcbZ1&P|!w!n?V|ua~%P;YW^%HG@Ph7T{;||-l zZlt`398w%t2S>J*#9NhS-LrZaR-hej!R9Fpp`8}*qn5y-oU7htaF*2Ih$St6nYLh1 zO?M9L!*cO+*ChK|Vs#Q7XR0=59(lPSZh6Sh|Aii7OJz2e5CYeG0Qws=Nfq*ObRx*J zx~q_zN1CNahH*QER`gGLrmwa~&<4<&H0DcxPBu7XekNA;_#EZu6MvSace&EWF1k-I z;1R{Osnd?@q#O$Aq~FKBo{##jpfT4>FJiVz3Qij~J$mr2(uzfEzvB)ShHQ$>YcnY# zrDOp9Mf?N)%pd>~y4s*77milKQL+ypq3*hyJ<-QZYjA#^Yki)sv^}zK#c4YiN+QSl zgKbPNS&zdZ-v{>6gc5boFKjGz+oB;(JB7|{M?Hb3==*S2NrRQND&Ty9Y+Pyt{r87h zy0-=#`A0HtL{KA1*qbHk{#6~^xFsYkOJ}V8huX zb8zMnixp1E99mH?o@Anh@LdU`H#Uv+TXD`FHktL4)oAmq?0nQaq~7ohE{KcdCqGWh zWpO$eMKq#<&d?Wt2ZyIg^(hE6WtgC_m*&?NvY@+M-{MnT>{m#;wJ!m1YdU#~cr!Kl zyN6hVYuB&H&rmVT^uj5;4fv#<_S^5kX;LC1+Jbwl@8O<})eL^a<4%Au^(rq)6r@T; z-CB|%rvHkKLC?E1(?tK+Ag}wZ&sMlwc`wZ_MxrKO zy@EMtnHZ1)!eL^Q<_H6qrQfbC_S|r_)V)RY-1Sz~NIDE7Vv6Q--WH}51nt+*P8!AYOSn`%FNQBRZW zba#X9;S*8P@#ls~fC^F6LB2S6oJPwbal`QTO8`0IgMP}4O)UHn0 z(rF|0H`5iZ(x`%$0$-b3=z(T$1RK3Rp0H;qEqKXF*Y8_|N3ct*A>c!GHIW=u2xyap8zFD z1w)TYT30+*>);=0!mf(m-5=)bm$?o;k9?STs~>X21HkXr&A8NtI3VHywv)~+#R=5o zH5XIUN~w*KS#M?%w0qdBm*V9eq65Hdm^ai9mMalq0ML~YKpSKPhPrxQ+9VlnbCI+- zbo}q?J(G2G{Le}-ac~Q=Qh4gaI?yQx9Bo@M=~2{(${A*wj%HlCO%1T2g#{4RwEYn_ zDRC6g*+aFe&p9!a;#6^2?O$V= z!s}z^^`=M>yzCW}aL*J{3;G&1G`ha3w7a#bqH2F>M~yS(^@5lNgoyi%IfTtx{{B>S zK%e)O8mT>ZhqW*^p#W>9vJgr-EH|qAH|(9W3H=g#2- zlp9iC1+}fqX5*b8l z`p@-?gS!v;oZWzoK3Q6hfQm9^9vzbE17t__eZYj0OATEHsp?TDmp)Y-gU zpTcJTDiN$iJ4en`dZ>>$fVR|KKY7G*?i$iQ0?;*9C;rTKhHKur#v!jFejwMiXD8Dj zw$-cjY~4U5j)5j!15Fv**?Xwpi8ajY`K3BSEj<_39`yk;FZuhG2%Gj8Y){TNS6Z7V zXrjs|ueFe$Pa*zc{*=2L` zh1Y%N*+OiRjQa5+wu#>43iM%XC~&1f9N$1}jQ~HApgY*KK9!ftU$^2lJ2&=Qfd%i< z4%aMI9U4_Vl;Sqr?l7O(AU}+hHB~i08io}#2{jD-hGOB_tgs^S*3Gxz08y zE^)VB5cvr)3ojROLsXLoTetuB0_wAW^jjQ=HjiU;NYImvz)udVU2jt1B3a>}I36Z> zU4d7&T!!=qa%H(p6hvfRkr-%Z$i%`jKtbqc|AW!Mb#>uOQ_|NI9(ISeZF?!X=%D*A ziP-@rj1OpW>ylvF{Ouq)-EmAoTd3>eat{RE@zugGw$S)9Wu9N&LC&Z!T zc{1VqXN)!XBRZG>!*${niY;+DHE_I-VS5O?)#BgGy8-x`qtg=b^9T>953_n)5o|+u zusr2XdZh|^p$0L>W(&YQ8B*aKx?%SkkZs1*-?Ro~BM>Rk2Wg zPReEz5GZK?fs*L|I5D_7TiILuzhb477u9(oQ;#wG_-IE z5FFey^iT>8oYwCue8YOR@eStLBibf9Xth!eQGviu&d|GjaDQ~^@Ok+gS{nWvTFSP^ zYF!z-Jng5pU;8(-bR9!~iZ{nB&M00P)|hk5I+EHU<@r41W@mE~E#c?W$v$s0WB(h$dMoCWnlXP1YNYJFE2xjw=b)(6xr)rSxz0$*iV|tCcc{ z_^eNcIv~Mv2VGc{YJER9@F!5{6U(rgP7}b_8 zHjW`lSiC(Y)^0I0*QVLVuSAR{(8=(s2tmul*|AqM{16_@7%;o{QCFp40$)-SC9l(9vvrxwOeF`&+v3q*F#?jLA)8;?X_A*u0S?FCwx;l(*2_lyi(y z>{Iy;$E>Vf^t<=QTmdu4;RE<{&1_YU#9n8tlSNn)CR?e9KOXyZjQ5%!xDY zO;AvtjU^1e!*d0!VP;jHv?%e5Wz`zeH*o(IF4=prI#~nZ(iI#C2={;H6@N!eIU2ey zs_N+P?k;m|J4zzN5CztWLDDj)m-%|+gP?tlDWwb{6~EYi$}9MIEASnGM|l`#-%N5^ z;2Y9@0jXGs+q)lv`VzPxlT9&42KYU-nfUqfIC&El~ zP9rw!`$1b{Ik)-*$9(&V^V&j8BRl_uY)vv_YqPzEb7MtYDglTo*V59^kH)=KoiRVx z#gRI@ruKy@cUjYw1w|d&_3UNqH~8>7#RZ3DdCk0|6O^pDmCGEGYWJuD-85RSpRB#e zV{jW*qO5eW*vD!!`*8MC^mh=DsiwzO*h`J(0P;NB?>O_~UfJkwxl8gJ{1c7>%}Y~Z zu0vGVkQ|OA0f2!TSPxAyY0K7BpR!>Z{(4UzXiS-A1D9H<`!Y4LI);$ym6}4dKw_Sm z0i{^}(2Ce2CV_%NJYG?y>zF8H4w$uuK-%^6pi#wp| zNYW-$YR(>}bJ@j|qfn&w#@c?7P^(`iE3vmoJg&5Mb^3_;td5y={+KF##^eWzth z1JTtvt-UEph;FOs*tm`vZ3BZB>A9nyf99I$YGa?cl@7&Z`i!ldznZnqD+|N-`jn6am@X!V_t^GJ3hu82FoamSFg@cL{7BeE%zPyGml!!(A&jKi zB2)fFq2X9p7qllx=`S+U%VN+j8SeWe6lYuJTA_<8Ajxb|ho*fZ7hi_(a^2u+j`WZ` zn8|ofcK33Uw}Y-QPpv04N8fobKDB8Bu}+i5D4kQ$vH`7xQoVSWYc}c=cxtk1^DDPt zr)_7SX=NcxZ}_+PxfCF9AC{59IGy7qmD!u=XGcuro{U#uMU*?grK~UG;`^l602-ALH7aD~{vXFuAQXkX^u?qx>wua%?%*&vtwgzg`e#Dnc z`^p%CW`76orLGU0P(wv$Af)HWf82vpi{K85%PWvMc}!fwMR5%4MUT8;%}61p$+ID+ zfWM0G2VBY0P3NP3YK4S7ajrR~E_`Q=(uI@O=WY|JRaX`$?2JV~JCpRC!){l>LI@xQ_i33sbAX zgu2Y(na)im!HW@eTFH9SZBF#Y9UV%QH%$p~o01-^2C_EX*B+-^;wJG5#gMEoZwKBZ zoE4;U5h~1I&x}U>3k9}*kVBy9qOwmhioj*U86nyoAG>u>mWTOZ5?NpB^3CuydKP=x z-XtDI4vl1%Vdk;TW^-J97yMT?{bP&;9qB?bpdnC!I8)?5F~)zBrU_M+R|I87_HzqV ze+3B^G>*yp9J=l3ciRiV?vBjC;Ym0fR9PCp>bJF9@OpB$ymnZ&x0zVq3> z@d?aRm#2I_0&^@*EG5DDH_>Tuq?&^qg8$2nfF;`*ykxX4sIlltgjYLut)#P1W}hc@ zWQxsF@R~;K&fH2uR>8tsnn#WJt9jL-46jvFMC-|e+4PVL4|CpKPr$~#;b%^+OWAw{}XhVV0 zgdBwgU>#EiDQk=G{X;H3%PguoR{2KArTZ^gg~%O$&f`76fDiCmq6f+6326R|*&K4Qz zB{RNiP`i#XgWAX8v3UXr`mX6Y|CywBu#AVvE;icEJ20}QdUWgq>7LorQiPJ{2iOBM zy$OW#_a4_)oyt~>#|oYOurq&jwi7L%b)^R`mdW%E0!8|SO7Y=SMEzqWPQKIl9|P5I zD>7SROQT7VWDnAUv`i*HV-3HlZ_Q6KVsSOyv9_okHp2$X>R~jFvbgwNe-ug*EV*_d z-DGDt&+?b!!<@f)sT)Qkr=R1g3q*icf_1&D{Y3WNgAuqnC{uY&NjRh*&}Io4=tU0e z2|$&=#(sl|VNMu&qDr8YGfKKI<&rkSWqqA=VVely?&rg-!wz3Y4#7{|{5OrLKhD>* z_9$otG;Af%u>a#zA^#o;CCyxcP4R!4bwZ(E2%VqI6s<+4cA<+l7dgsaA0s#J&F1XsoXx7$Z=)h=)9Z z$u_T9T~pdHqqOR$Qr2N$uj_1jFKs25Y9TMiiv|cN**HrPp>7m7OQ;>^KzED%BF8V&4qDbp?{QU%?vu>X>j~OFc%)1-}n?g=ob#HbOVe6G{cd!K5lWvjot{ z-Fbu-;ZXiSy-gqRMcoua#;kjS{I3Dj68;Y76VQf#=GBD%6X#TPbhWaxa&fgX{kt7Q z69g53>1=2VV^!$)JPT=v5In)n=nPCGaRRk4NgbeV2OVFJS0-EL%^KbpL;$`2LRA z(Szbf{N{@bB@hrVVvAe?tuEpi(aD>e^Hcxm{^B=8FNcON{j=6>J$(Xug*fQ^@5#MV zZzUDF`2-9L(fJjubS?m;^LS_?%}lIJBU(Ujg7c4W6mceja{jENb&Puk`jur=0z?jD z4X1>a63rwAFs?Dx)KGG1uOJwfzUA6W# z=#)*)T+yvBEq?uI$aTA@cGUs%SuP{-Jl}XSlFnGHLxHKrqh>8#Mv}bQY$H?@)ny z%SXS0sq0qf+dHxm`V==t44+Sb(tn6vbDbU0qYYRXfdmr}_acK02;z`ozk%}s+g{pC zBho~afUzS~NmaRY${$9UE>@lp!s#RZy#KPNFfis(ZIoJ&1MPVL{QXCOL)FyT%Fz|5 z(c|Ls4~J2nkP&4@&t3pF85XTMNef%4+nHst+EJ>BpQta?ku^{5J>}(-llC#6{}%gX z_}q*;s9T{k27Y9~B-dWWD}qp2l7FLGk*lLn!l6-35Zb#wF0GZ2!8LRF8Edfis+R9> zUmZrgZX1L_30ex@3HPn@yo+R2l)z1fgzTIY)y}2izQuMTCigph? zE&^%xQq3Q?jMcCXQ@Z7LY~`I6t`Kyjlr7!;#2O=w@v?7c?ZbG{#)MZ zP#6$G&L(3H3*p>B096(%*pWe2LxJf>1xLrD{>Er5ji1fhFiq?q8-%o7gluC_8WEDu z<>kQ#T=nqr^7DqQ3s%RIl!27-D7GOr^mu?Mt<(vZz!6+v zZ&#K!0w-2d3d5-$R03vvTW;WoXx~b{Vkd5U3+Y+6C2S)jX-!^ ze(Nl)3WpahgHoNEwwEs_5%Z2+)_eI=4DH6KO(!!*t$BsAVFzm`z`QdlhS#Hr<$vA+ zIxewq%;9fKQc0ZkS*i>LtNv0wq0Z_z)~Po3(}g7F%H&{*`xRfO!YkK=?0t=Z_ybnF|>Hsi4q+3JW^_N#0R) zG&8j_wzcv$`}|LQNG@k(ZzJbmVP#4#Zs+*VklCan>ww0L{GM&wE-z&TU8^!)q=ICp zzPQc>?nG<{v51OX9yB=llRa`imcSOO{FRb1JAvyO>`6J|hLU(eZCB+1FzwKtk#iHD zx77{i88eAH-io#79|Hw1tC*tkP9y5&Q#OL!Bu|N>fmUK>!E-6=L;B1uo*5}EzKNy* z@r5wvRf#){wm=b$Rf$IQEp%zF*<~lY7&zz5cVhwHHMsJL6RlkGC(Uv{LhPcR6;m}P zH|JX-kckno*$dT_;N8p_1=iDkNk3=R%1B!8%VstD)=VN)3q(_PF5FpAi6Ev!39mIF zjOk!D8d$A`kvB2DHq{LXrP6fRZ-#Thz)2X8d>!_~%U!#h?&AFC$6o!k`alH_SV&D* zUM;9_7pJ(eX0RH~EIU*DEgP2mqw0&u8e7^JKHSKH_#N`cRO^+;&34RzPDUf673By80T;}6ocex zQQNHa$yL)J-%LW;2GpxiY0-(2u|hMC@7l%(3Y|Stb2K3<18u~+Lnv#K;g=BBiwo=% zyHqHJf>jV;IiNl9Z0@)p4_z3WdERYCx2nfmRXxvIb&nDBNH*x*>jHJu7^--d{+)gJ z^T5Cs@>5EI!`FhqMP#-A-yhiDLwA?Dz5|*hI{)|0&vtu4fCSJtQVOA6@aC-oa)f+j z*rYv)6;NSy_2iAdTu;`s%NncX7J)~sUu`Ncb5Kf^q)jScgubW^<@~I6I#+*5dgx>Q z?s3U~v>vbj@wW|St>JqXd_A{oWt`+zjj+&43Z7~%Ew^iC(Q-!h7Q*I|5v8`ul!zrT z!VyyQ^p%wh3Lkh$PL38Gq@LC_%Vv9yB~I+-Q{x5qfm|hC|7CT)%aBCp5*e{#&+V`^ z-hNq=G&&4QzQls44Rb}$Bq(RYr2Qnrqn-})-CFmMGXP?WLUGu zSN750hFZWH)XoYK?%4OLudz!M(zBcC z-V_s|^zS*vFAfb%Imb#>7T@GTk2c>lQF*Th8#AZ9m6R+-_WvKwzA;GBXv?;%y36V= z+qP}nwr$(CZL`a^tt{KNUG?hDor$>p#&$A%evb3?^8R_huxlmn7;B07sw&1>b zhsW-CS13Zi;V{0dWQL600BVs6v=LDtjt}4{ZrYYSL>o+HcoO=KZ7Szrc#YHitQq$x zJx}y!+RZdJsz6zGpw>?DM{9qQ%$~@{9edx|={?|c^3%d=$kSe2kY2eH_YF8PLyzZn zz^d#<=IH(=99awx2C@@jC@i26ve-5;&#A?L-ej{La?0SZKS3!iBW#NAp-%%5bTe!~ ze|Aw!hrrhlXp;sFu6dsXt6#!? zQGgB;3vZq|7ZWCpfCj5kkMZ$I5?_z}ka2AZfnpAvoEn-^xNSP=^fo0VXr37Ig+iH9 z70&iDPapNW;Pm6~WX!(u*pVeupAX*ynAI&tF|`8>=%f3%I4}0SOr{5Sii%m@Mm=<8 zteA?Xv}}aJTm=wK+^2~9c0nF@t=ib6T>~_ZoHO3 z=u1#7B>SB%2HPXR8Xc>bdY_RxlkF&dkA(0U5(DPfdefD|>d-Ya5^hqXP*klM%_4FN z^;dW;@aH$$ST@b8X@~n)w9#W&F<%tPGbMh6%;L`#%;8IiT`}1g)?)urjnB|PecWt^ zH0c!v7LF{+eU~HwsDAT^0h&;|p|nw^*S}m^|8XvOO zc>Z}BG%85RApQfGn4+hqg!CT?0pAUe06RJcGT>h(ia!t0LxFWY#Tu(kNz0aUnI?Xo z<9z=4E;p1eW+=yU;~yfnq}b{HP~P_X7E`_Tql0f_*a3$LhsN_9LQ!2@rx73;1iXMy zHHtY1P_F~dPKJP}oPW{~U`ytsbV9t(lw)WAt;#;n?gS`PWI0Zl&?otE7(wDW-fe{3 z0miKjnV2y;q6L-NERO(k5_*4qOFjeemX3l=bT({=3~;0^oLS-BRIKB(5zLFPedSLX zPJ?wYxhM`&?;YPI=Rz_&q3?fH`BJ`lT;KquBJ~(ZoHERlh~h2l@{!v`vqMobY9}ax zH`1CFO+BSUGvQF<7Y+&`u49u2pLhUF1UgpgQ#UdTZJc#9eY>O-r^QYsDFvhw_wVst ztpiw2m{^HK*qc=?+$i@VDb7EBzT!nGrONd1aGl-DtV;P+osfB%;oC>9XJOk6CDNLF zxeG$4Td}-dwZr(mz^W}38}bGxv0Uzc{z4{!Y!iy^c~q23sD*I6bawgm`elIj5(Y7G zopL3yvKP_Y`JQ&IGpZkrbyrHCYa8h5E5vMEGvF7Z`We*G1IdB?aiZFJ;R+3BA#}^m z{Jd^xk*aZe8(lE zyuPn{@81>1`Tx^-R7{QR{}UW8Qn-?tlSSmRIa~beNK2lN8{QKOlNf$0z`#!kMVvYc zj+Htn6H{e9cx&DkJ^d+04^M{-o)V0vycLcpEda$E<1L0n6uwIx1x@4G)BBv25X|l`t*rs=_{HB4c@@+uy zfsgHg+(##oYsCpzpS{rWFMp3@AJ_f)zU0B6YXR)GE>ks|bm?tGRQYy~1Myb*jSc;o zCNT$x5>Wvrj~cbrtva;q?h74Gx4zD%IepUo+b_HV#E-UqT1ESy`Z;cHM}J73dsE@4 z`|&GO@m9ch0&4QGP=8&24^k9=*H^glXtmvyFW6to`HEt2@AHwyZgM(9Z4SM;>Z@B{ zRz+V?<;dEUvj=E}86_R4 z30OY@`?(m2jE9O8MtLDwzBTb0iBbq-B3Hg%!soy@GKqBS#i@~t#9=(ab_4jI^m&;J zQbwOp@F=o`JkCKrA^EdLdcZCaSF;(qsn4Wkj^d#r`VxV5{7&9_yO^VMKBQ%-@znT5 ziqLb6*?BA&R%t=OV2JI>n02H%lQy#D#ppaC&cTX_$Kr|5f?;u?@s76~MmMk@kxE4Y zK<(+rxK=rSS)XhW4Dp%#=nRhV^{EA$`klKDBSNWP|6n#u^zZ7jl1wz@pl z2grrni#Sig5BYtHgk0DZMKb~HV99&*0FnLr_CW{#;_b%&y%)BbGs}YkcJnXsiNtY&{Q=cQmek z<$rSJ!JiJV2FAW{z(VJAp>{&RmOQ>;qjp-51j|Q4uW4I|L6{8KcYA%zqT|_DY$Xd2 z9n8aQ5~7#?iJ?*!6&;#TLN0A-$!=>m$p=A#sVTC+2jdj%lyD)W3ELTcI^{1%fjFC6 z)(C*LPOZr(rC%?=bG`eI)g7|^f$slgqjRYRY9b<@*G%w_=T}l3o~AXN2wXi=O6}C0 z$1lQ<#`t%<7~^6;;VSQKn#A`|yrtFKb}3Ms9Z8K7K2aAkh)^VDlj$e12kmpFaSXwJ z{Mq;<3S+V%{FneD00~5=P%>ryg-K08by3WSC#}yT0;Re|Ffk2Sx{B#P@bAfXHx9ThD2piokBBlgxgXvGqH+fGkUQSDdWD0LO4NLInV8%h)MXQq(!$iiTuTJ zkcf)1Kp={Os)J5mm2H2ooR+GbzqVRkh{ZT&3dgIv*`6}L)v=q?8QyO>F`S^=RJY|g zbnlaWK5sj{TlR9%UpITs?;Cwz#z>j$PGfzenD6~DnC}WY*}J$ieD;F`YTkwsDkm1B-Y%A$Dyl+bR5L)#La)Dc?~7f>}ruGh*x+mGxxo6nI?pN;Z=9 zhHW@^hQY85RB1^BMeJEsB!|aAt&p9PA#RWkT~kwYd73Fuj>x3IhH*OOhIW4`N(71ca)~s8LQTLT~(W-(IH` z_`T0lrj+tX(S-c9qBSMti#TF|9s){MhtH7O(1ZH3sCblE8-wgzn~0IW+6Cx3v1JDM zd$oJxS-Lh+A_oO(%cW*F@dBSom6w{Uujq>YKx?DD5oH-iHyv)?deDu3k}>r2H_)YE}mmCcQ`-i`L4w9EX-#&JS97P>9nI-O7LeBp*-sGF&IBEL+LMkBRk*>n`nLAF>OR? z*!Bpyie6d@oSK|xPPZwmwGlT#?Ztg>4zn6J^;3jJnPk1zStwl*9DvK<0&++rhbBzO^iq(aPgWQDS6^fJf4Q?7;8Xc-NS0V zFL(a~y}B22RW@0VAJ*wSxC~5jvVTWl#yK>{$`lj~x`B{1?7NxXZ1)gH?xGMuSo#!K zZ)oucr^$5G*%u9{hVwZ-;lnZ)Y(u^z4HJ3WGI6p=hqmTsteuXl(F@yKd*0~7Q`nBH z10#K+-x-Gjo^6FdZhP&uWi2yi1-(cHgdiFm$J6`a2|mTK?m|3x(KLkl7{=-F`OlKP zF$U7b67z&mI{Z~d)@)qb;o@5n#Xl2rJ1szeE+Iqor;=SmV_77|yvo%ZM;gsx_ZRh;iz8k4rj(icOL0wD;-6NStb8=?+n zGx;RW@>-%Y?BJZ36mhL_`ITvyx&32S>vaCYQ=&FLf+&PQH!s{YHDo}LW9^CytV22t zN$l-Z@E39IY})2Ct}3~I-a^r+ww?PMjf=aS%LFDex|KwEpV!u+yjgZ2sA$+XQeE|V zShfoPZM38MTYFFBJ_&}K4`rL@1 z@t{G4vPNaaKEbmuStj13RV}VtBCS1oeWYZ6j(M}hvr{J}XZGNsCH+@blrREh?KBz9 z9>|KlL^Nj2f$~cwQS*vD2X@WALz5?V05di}Aekc&(}ypFW=xCHWtvF0+#Hs?W7snD z_t-_OCs`J(Kvu+V*k)idf`JP)xZhwR(d7#Gux5nl0 zv{gsp>1*BTa}B5Np3b#_M)bA_bM~M^GNG0-F|;EfxxB0bb5uA<=aTk41(#hT2GOn zdo79V<^zy-+h5Jn-dIFl1Q+d=5Exr@j_&8$L+ht>x-n!dSyB0MJ4s2k#+9;#9JOQbYxlxGIawvJ8>mZCO#EMmjQ#0nwvFD25zDNEULqyw*_&j2hGYXXX4nyGigr= zRT@J@R*GJ`o4Mlx;ibF3kVHoG+TlUW24~u*6|p+niB~sm1hGQJtr~>>qn*7QmMsKW zy@B}BUJ80f+&l&n$$pnCO~DOc^UMqLtvN@Et|y?zH>w;2_KS;~9c72N7fw}GR8{k( zWY_lssory?F7G9aR(*9@)Cx6hKY0vZ2^Dp=V&lF#ZNwc|jOCd#t}u`0-H$gi1sNBn zs<75dYA57JN+udNt?ir&#qIRBQoCT&;WH!xY7^v%Ps|Ftm z^lxQN!$xi2P1|PIfcFP^Edy$W7%R8@IK5YBrFxg%=e(*0W;BbfPic_3Ll>*ZxCPVe zz7W61nN%QcSi*W#@)X)m zJsqS70$_C+JYj=GCs>R(TM`V*UbnP-Y2k=tIE z6|At!sKnDP3UM@Ei4dStYNORu74M|_`%2$9EFJCulo4$T#fTY_|V1ID)Y9BmA7y*wym@xNb!>Xor7_7kcY;pC7vEfBE?k8z(V|>r3Vw8ys4jz$83)U(LTe z3cbowP@Ivwgv1*`X;YTBK+jn#h}`1wsqV+~F-`{R@N-?6hc5AXFB>BT>Jept6U8-8 zdDjRx6W)@SvOW~iTGVi_4j!td*kgoC3N@VpEo!w2%{u#^1I%8(8KO}y$onh_VrAe2 zVl#we&+t)!lC0R85zRSb&JNQ22#u}rX!goR0!lu2CycQ;A2718#O|1E6Ysz9DwNm47}{?A!3e8XKO><<(dno6#7fyF6VNLQ zSSzw625wnmJ<^=$_fm`+mc0lexx!J^)5Ep^|3E*Op?;yhU@0CDZnV}Dv885$#Z-;n8lht zt84FtSHyRlF+d1nnuH-?x;;Yij&YpQM-xiI#tOybUgoBQU4+z{*;(n@4eRBMcjx*M zA>|bg(FQ^uLSoYHcc{v+a>kfK_OVp0s5IV^DV&O-R-{%!(Q#_w04CO8K7kW-f%@Tz z#6wDZ*FRnSbN91x$UA?g>dF>dS*Oy3gro{bw*+0sG`x|4kfOJ*W*rB{>B5vs zMrDaRr3dT{kA*Ayspp3^OTpZ5HS*erTHHZNBsfw$q6(iiR=6zo-l8nQjv4NXsof$vR668RBMl zAW{OXepUUc@*Pin6cST2tpnl{Ice_wavI;0Q;|@GgTwXP|k*k`vOmSUX?VM^4 z5{PpvhZIxpNnSwQM4w1T&m^&sqjECCo7!KgP8gv)e1mufH5r>2V`QK}BiRx#wxk@I z3r;it%*1F;`?k*rTY-)wp$jq?qs*p{^g`I$r87E@cVxufIZIVNZGwiFH>6q_@vsTf zQ5O*mLd0^T#@b?L6RgoEEH(k8QoBsL`wLHm2SB%l+k3&MaB?FeVMW>$sC8lq)p?-~ zL6}7%XhvEOQMz-@Pt&)tm%hEs`!d`~&hNVlB07T-5~{RRvUz)O-5&hBPk_^pUyn7m!1TKFkmMWNPGk&FNR9n# zHz-w}UWw=Jt^4d2mNvFiGDxLTOit9SpC8Tu`|A$&EBZd{IB66Loub6W z=kOc0gU!6H;f{~;KP%?M{we@$vkDRFhit*T*8-I<^}X^NsKj7pLGOQD0+fkH0(T#4M!9y=i&7#FM@PyO$zQeUjxn4G{`KM=Z3mSd=jb z4+HL-3YP7Ee?l7k2UM8-_BjmwK zru^$fl-$pJv~}$e-w-u=qNETXNLr#2IiScsSR<_;9Hb*!G{Pf$QWP6G_(^Y7`_zi8 zaW)@1=|T|F^b$>g*@V;MR#2n0WLq7JXEnXFD&jrq`i_A(Vsp~dZSeWb1$+ypIGU3x z@ad2KVzF9g!qCXx1}AGorcs5Z_JZ zRVr?)<{ILh)i(@wl%&SsH&z?Y(PxL=+a2g^+1pwR4!f_Crz2JmXV-~5flR(eNLBzi z4nLohb1orAm==1j!Vms{<*FaW*g|ju<97E=3jA>*%LH^5Ph8r&3%W~_*se4`6PHHH zlQeq?uH8DWJ?iAPNBQE0W5lvKCmxo;T~SfF_G%Ute3@nNn%sv7j>-6&P4MBfN0-mY zhPws|7wKh6c2Xbfkkt^2R8 za|$n>tS20tKeyEte=PFQr8pzjUk{Ha$kf`;Q?GB-%z)^fe{kGFlE6q%O$|rz%<6w- zUanKq{vC+!EaeQNuyOL*!@GwraxOvFiHB@rmRgGFPF?aD^l6>hOC+WZxoh#KnBYr6 z++}I#iA59-VNvnMrJ2hSFYFt@i&5k^%tIsi`WHvSe^63BE+2ce-;~t#H{Sif&glOq zIQbnNXY@a@?yLk283eiC--i6!O;izz9f-Y9V~|2|AT+22a!`~2QItMp&+U^Mt~{tS zw*7PD?xU?7=#}(H>H6Vs1*5uH~4N# zFq<{Db3IfB#F4F56If6Q$d~jT_Wp2k=uH^!BXaIraw7cL4X{|F2~7Y%m?3$?eyziR z6MjQ|pF*wMDf2>JEcDNIr{KPC8IoaQm*I$_i8E^5csUVj@Dc&p`_vm(UWO-4@yJLU zq+3V7lVJ-ACZs$zwmcjcHAs1ZKrK2N>g5)jOHT}&5GUFYdDyJ(}Yze*65~GFzM>|Ywyyl2y}*9@cVTR@DHrR5*!IuR9?yK|e7k zxW4%xs~f>ihGh4XAr1tzow{gdoK6NljyCa4#rhm8l-4MfLqjfj*yKF_Nz0yV66dbP zz}-#xTdc_=Q+1>`u=Q=BrU4ysRHaL{5l&jvj@v+|*1dQw>i>$7qi0*+bnr6CL{ka| zJ=jC=mx-lm4548%pkbqu)115HMz#k8Iw3hsIu;LZ`4iUpw*DUJr_MyeOR%N$9zvZ> zdn(Dq!z}){yC|>eL#?Rl@vh0f?0}J?s0dvc8Nf_Eili{2R4-`0pBNYbO)Zb{V$Fa< z$M5VrMZ~^G+F4IpaDx*-nZ#12Vv7_^ZYa_o2Z;&zb1Pm*igMgsmHX;`4);H4(aWv} zVAO9)le~IngX7D+LTCL~m!7(cl`t#3n?<24zcu{DCf6#TsUQ z(%O|@5Jmnpc4EkNzn$%#ilx6GeR^oTYn0?gOdN%=bWS6^ufJ5MTYtf8yz(*BKV`pt z8%2yeOm(_FKe229rSfwHC%9IC{cQP5fDteh(M*TYFEnuzyVvQKY&}rp%PDb1>zoh* z3`cArgjIx!eDgP&2+IiMo1Lgdf<|nrHQ1@%H+EV)6Wx$kPn7=Qk zvA#i6>q6bGPBy7xePqvqG+@HT@;jMASyj$MkOzdWUQtuK6tUh|x&V>axz5;Vc0Y(8 zIr+}mx=L|E>DB?d4DuDVJkcRyrRdz=EKE+Sca)9-+){^i-(U)&@*!BOKy?vO&=_;+ z&k|y$7&n?HH%}M;&XhY7WOMg+i*s#jz4hC;rSIHBxw66FEtYn3Z)_qX2R{i^c6Zv1vz1FlQ8$j5P_9 z=Wpn2nt6d#VzQyUzESmw%7t5q!{wDAXK#pDw<(x@FI1o(R4Vf|{1gT1De4qK*2DmpX*4AVn_2CB>TQ|57%Sl+wt-n}IKu*o3p!7XVnMEpxgj#E; z@aw+_iCRwWi_^#5RHp=7gSkPbZx@EaM+>yhjzqn?EWDEUQQ8*2Kh|1sC8O>#6R`0V!zIH=RRqeR>1v!)yD5Xx%Udx2yMu{a(a|M@e))pql&lA zJF*y1Ki&w5Lthc$WNM4)@_1**6WU`FBn!s*E$=gs`-(ta#$zDgpXQ^c9O>V{TOAY8}qQ3!0NjR#{Vzh+P=TW;mTVj-D_-K`4^P+z@U7(Kq6D5N8#;}xEIXODIK8mnyYSBX$>_tvWUall`dR%g?Say9qzr*xEhuev|=Hf@Np&8I) zmmd#u!o?uomodJCn(QM&+fGoR?ziOXhVJG2>qSdI3bUR<3bRp$!CptM1m3LPJ(vjj2G2g&q%Hj2ocv^9yw z`jnC5uJQm7qFmLRkt0>4fgFe2iItI8aoo~0%evh3QP-$dKvE}5)2Zv4Nq(sjqFij; z+K$HXefF_rST}0B%vPv3QjNQ~rqv|xtAjD;ycNI-=x@M2c3{QlVaAhm*Rl;e`MU*g zb~`GI+QjMB9LeUVH*5^?-3oT@1-gDl3sBv{I5=xjm_9S2h}|fSX*MF0S7*a7nyqMB zcpr`Wn!Cevo6~Ttn=M@>ifQ|WqdRk{KAieRD#4_@l<*Yi`3^(7Xq1)hurD&M4-^J- z-&>%5XCQ^(VF$aR?&u+YKxdhF(Mx{n$g#^Uq#wmMLY3W@r6{j%Hdg?xbi26647iPd zUO2F?2{W?@nKY+(LyKR+Hv|2K4P-^#l@0C3iwOX$cBd{1&M6dNwaXPUKE z&X;mbO;SbBG8VGl%~X?1XSGp=zb_s3gmb<>8!Vq^sPR2Al!N42?{DZqTSsAykibuX zrHP_BeDwkTpAEgf311HXJGD5F;>Qohe^W&9-~X_GvfWwg5Z{pq3p_`iYTCpJ`$TxL zhM=LSxX>K(zeVvSfCcMu^`ex~jDrnv(+1KYGN{ZYw9M_Q76hpxcvr(eOapm0%vq|c z8k<&E59-gB78jeGYjrfjuRo@}Y={wnKfF@6t~NX_Tc5HVE>|vFZEhzSWVl2G9bTMS zJ|+dU?yZs8zE!QSIwxOvlRQN)DU2nY_v9ZMxpUU;GOrBSj3%?RdLxvv$wFd_^~a#j zu0Pm}`iP=5jA6{`3U9!vIU<11lZc16HD``E(NgPhb<%;&AdArOq~aI}g8E7Q!i&h_ ziWSjwGClw?kFv}$f6NWI1~iG!XyU@`fVIeFTY2}2(r_mQdzC~P#L|5k6N=LCCl$?# z-CWtHf14IF)(5^Dsc~lT;Lc#KOlra{+#O(B>Sj}>(M$rz70Kt4r zs|PHhb!=`U2UOENs|WCyUOR#6*j$4K1c2H&I*d%}5!+}{B8SnK!4bp9{YogHTmEC` z5IEXe?jS)3I`O(HM3L4KO=coyLKz1=6mQ{wvGE5>n_qQlX1>L|6*^U{TCG8w7v_q} zM!i#BH*0i3cB#c(sVZ_C?IL@p+77aA6BoRNR1?rLVpKy&ZP=eB!^(;XB7&+oR^(HytW-zh zDzC}1tH@^QtMl*y=8$H|7!U-jtefR~QsrTT-p3QIURQkh9Bl+iVP(=9&6A&obx3~h z8D`4RkP5H5uRVkDh(-bVf6gvz#9Re7Zd?hIHOV!4C!$4ZH!-b(I-W)5O1YiFX;P@nNp@#8k3i!N;?72 zTl(UsXa`0rVutXeHn1=VZj^eXF8-I9vCzT|K!?7lp=GOzE98~1T0&Q& zJ9(cvh$K!Y5EIprBgxFnp!|7S<&O!m|Dr{YH#ZgD#y*?iP;u}p`md;AL_8XwO6oHF zMO}k3f?15SU@e_qz2LGf_%wVdSp;`cKW7}74j zmxKu=pusGGK7}9;7_eX#b7#1f8&YExQA&Aw5X^EQB!5(OzqWP-++j0y% zse9Ua)ON?Bw~PE7AJ1ApIKW`NJP53LX7AiSDCFJBPw8Bz;#eb}WbXYCUH()}&k?;7 zFx!khYyp2YV&6Z!(BAZmnqB&Jhs5Yf;4?Cb^LR_$H9F36N2To^>fUkh_8FSOGuGaD zUuk-Bhd(iFc*NriPJ52i;YqQTTqk%40eW@D%lYej^H#4vHECC{!~%&b>zai6=rEY$ ztv2kS9Ie7ds5JSfgrHK6qse+Y*-+15hm4#R3=P(Sv#@$w_DM}@D6p+mAVli=53R$9 z9549smN!EQK zGtU0t6^rx%f)O$0ZA1Jy5Z8;gsr}Gm*p7>(FjzzgaZNM}1ttvBKfI;G@g_1Z=%uQy zD)rj(JKQu7*?u*VgPquCIJQc)aSFAq<7;teHaJUV9n)h+>!T@RwdQdwXS0?})i`(X z0un0hocedw5i6{nVwv&$+WAEhBF-Jqz=|+NU6a^X_J1MWR$x?{TdJ>-A}L&i3sIm{ znX8i-UFQd@6XM{S%WW(&FERaG(QJl3vj=ly`9%bEhBce&MLA}cbiA2bt|9_?>B&^V zc05)7%b)Xdb;zhRAf=>YJyNaWwDM{_Se%RWU?MPl$6{*SwC_cc2@~WIEB@->x{Vh! z#5>BA(kbvnYoojiRr>U6FowF9supVD1DrhX8{&8hy|A~+#X`NBK-~(x385dt8FEz_ zdM1vvnL^$2zY+QVVbIlO=sP-LE1j+@3X<^TCv|LMwY#A!Q?p1Elh{pp_b@hrU&W`l z$Gcd&PtT=Yd@fDWFEH*-$S2yQpWcDkAn#_n+~L{+JY2T^4Zhx)j%JyFmO?u)VjL0z zS~uUi=mZj7^lAA=yIXI*KWh=rz<-y1EwGxh<${hC36mERlLvelC7f`Ri83c;P5bm>PR5Dq-}pH0(=%D7ni#htssZPj7xEY2s1OH z-Kgs;toDOg@(Jh^?%N*%c{q5lENRf|9y~W5<-&<*xb)ucPq7-=mOFL55jhe+*D@6Q z65m5GRN|{?=RV5-LhN|Jbwp!BkYmkp^TJF%TZ`x(iF>{5i+jxrU`*A=^)crV3J6bF>fj zUzv5ptFJZPla6Me+<-cJCFGC`Km5)Z-t`cX4kk&b7^3Ke)u`J=7)%aO#+LAs>BpR7 z&uWm|>>^TYmgLLta;tLh+$A4cv46%9o zsj{sWXCDbveq02pmx3;HTD03+o4xZ3S?@@@jdIdiK9~jORWk3kZWLVqLsaKb`)!I7 zWhUf#y2K2Bm8ak3Q~c$g8h&)EEb>zfh+XLHm=WXzx=|pB&b43Jc_!;Rl}Z z(&1S=d;9^F$pNYQaIhoVqBSVRF0gvyx`GPLn<-8Fmes{NE(c?MpOqpFip$LUrRI2& ze(2~RjT4_@cqXyh3t8HRsP)7-J|XuBTdG7UX~ES>SlMB9osq2L;(Wu@#KsP-+GY{z zNG94-O89Y7!eoo{S0*NJ2$CI@i5~kO9zVfKF&N8wHQjnU4a=#d+gIq@OGdY5DQ|Eo zO-{>nm{?g)T8-KJoIW^@QOX{Xz z6D% zUWTCaii?BN)A11DPxtficmA5Ag~qJ>y_Oa){Mb)gHTWfeHVeH%?)Bn$$A^w)t7|RW z<(=+54_2i7g3~OoF(cis-?{L}M%IZ)UZ11|@rq}NeDCLbf}V@US`ud;153blS874>hl!LoC%!?Ne=U_b z$&xVIJOjQdW#maAwZCQxxdnT+#dr6D;0SV+N4O=`{*3(!z9rLqLvo_+?Tft~ax8b5 z_wh~(*d}mH#3@>Gqxp!1o3VMJbI*V?3Y_I=@*6w;e*4R-$z9MVD#?^xCz4rw#C7qy5TMFu(LX2L}77W_aWX?h4;-dW;Bt zFg^6elMM`C$guSC4*l|I_grsNPHe?I|EbX6VNqsSOP)z~w(>lbf zlL={ZZTiK^)AxSYjM(`a8xg)GC~gR(L^5+(L6WhlXhj=Z2e1)nZn!*Z$j6_4(%Ro1 z^RW%pe(l#x7G(j64LS91o<*2fZ}oW%vQL)R$d}?E?~oVSS;cuqI0$VGGXUYKD`NZ7ssTTDTNmazYYZ| zzra%o+ara;=C9w^-1pI!_{!%e{33hDhV`vigxk_8F-x3xY!h;r%}VEcYouf^H8MHA zwOws}qJW`C0({(C6^*+eKOwEpIL%*0u{vJXJB4{Xd*}}CZ57a2yDv!jh1;oqZa5w^ z4O2rKmzGQs%|ro-bgK2 z9B|w|4hy~ZS0_#0rn(a2`sYm$tq{%B=?*N26#pj4^WO2v@=VSO&v9B&Dw!LZ<&Bq# zVr|B&md~*Sh`)NX*Y3yfF`CitD{>132fKdpv13;^(qI`af`On*DJK~&v?HHm$Uhy7 zSvT6>S|H>Nh@q+FPH-m&kx+Af;vsd5aF47>rmTt)Z|NPR)}oBitu{cPzK}!Cu@T$Q z4tEred|mtRccS3)oZ*jrnXi!BT^HUHOfEkm)JlM=7D#3f_*4R^5auECYTwzEe`I z{A@A=Lb3Ptps`j@HWl(t?3VBV6`H5Q+nS#y`)XjCnOCQ8bQ0A$`{PDcn)0{Eu1Cdm z$poAOkIEAFdkHLCQeZ?L>H$78z4av8HBH;;?e7ZpWyvqV#mDGcp^}~LrquP=! zl+k1)^D2FYkZsZUWnLm4 zVmQ0BUjXDwzHO(zK8((Jo;xtxIPm3lN>2Ty(oyBWZJ3V0`b@BxE^wB6_Z2ongYf?J zN?5kw#M_ARWQA?r`z4#8Y7&ZGvIQy6lh;7EAdcN^7Cc5r?Q0(xuOlNa-KSf*s1wpB zTa=q%^@W`Uid{15y4h`tQ0oOF9P;M}Kc*Q0)UDX&YunpvWxQ(;N3DNEwy4wdHw5b& zGs@K0q#_zL%*vcGAmg+eK=jFgM+&uRy>{MSh>v&-_i-^RB1CW&2q2ck5vRtwYLuYzysP_c~*&T@+aaMM2^Hu z5#;x(KBrt(cZnTsUe(VjNJeR&;~)1PtPMz}N`}b$ImCzZaQqjHlR^Vg*Ll(CEc>6H z%ImA>8XIJoA9DcfM~|fLE#+pKFn?x2s`be5oZ_BW-)SHJzluydbhmlb9p)F?B@Ab| zmnD`LV*3pS<{?05TCWAKeqy;M=dzSdHlZtt1&|C{5U~XYWnn^#PBXbYvmDM&9+{Os z&Er1lr%O4x!6as4d{I@KB2CoIRtP4|2o_u}2Nd8~n$i!)b>{WS|hmDULh zjqG>R9n`!T3}2OGyojkGzm_x8SAn=^@oDwDxd>G!U% z<*yEoUg{ZN>Z@OzRaB2LuMa|=VQN4%QpNI$Ri1H5?ZmzST@~aDpA}ve#~TnvU7_K3 z0eT}dFA~`Sxt$yRvnnYAKz0vbwbQuhIUo#fm=Bw4feJeTMWvAb%KbHwu}^twj4!}a$ui+d2V0CEAq7(n3|M-!`@$X~K z;tDd-{}eL(6LW6-FU;AU=p~vC)pRy ziBTbG?8|+9k`&g~`B8#Q&BH@`Y_f035yNWK28p;se8TXEMX zMQO+TV$`3oGRb^N2_+-CjDXgq`C z<5|bL)WUkAShaoq6cEe8HKf`sJ_AJ&N}_QRw31C@CETcO)RZ2JNQ=@@qkUlM6djw; z{G^?=!(5JQ7Nf~&$EJ{kTfh{CmLt2(?{yQl7fUtg=!9vfTQ_>AuIp!DGwQ@0A^hZs zEYC1-zN|BOGirz1gWImYQI)Ybau#0irAd`LCTrYQ<>0M^swK7;cegFv$tdcsFdOH) zrx!55`tH7z^(WpI`D9FC>*QpHTf|gzyDP=kPBGMrG|AS1VNQb2`Jt6(bB6X#4lSF0 zT$8DS@2E!eSof3;_HBs97{>0bZi^9cS5qMV_y=C`AE!7|3AaL~-`WcuZJg&ZRgr@I z2fenOzhT}44&n|ffTeC!U!WT?F*YWxK44@~G4=!xo6G9Y&ME+1rjnh~*TedLrkvpahq8AJvMh=c zbhFa7ZTqHe+qP{Rm9}l$uC#4d+O{+6W%u;N>zQBE5%=6UC*u6vYsK38to40Mi$P5P z?iKQ}NY%gx`(H^*D@4Wb_tR&3Jzjwkd07`={~RxY4F+o3TBkx3#so=l;91^>p}!Uh zVreI}zD^K&FyEhgXniTp>FRAVdavLvgH`c91-?#507kx9e^rB^er&W#{A?249OL(N z$2F~y$7P0RT#3QRdswl5NIQ{$BfJJbDQ(83Y6WxFp)c90i?5nyi8@@T-l7#RbnxV1nJJ*zAH!RuekE?t>8HcroqZtJCBaZz-^UXa_PyaWnN6+8p>g zD3p0g8z3R{{w(J^jMD@!fS3z>pcWD}y_7Y|kpd4ew~r~|mf>I`XF**?@{>m4I#F0( zc#PNIDg@z<($Z~xA+eOXi%)H zWVGGr2MVNHO2*JrkPtJ~QcOG;;*57>Ap^}7Sz!_w#@IKgyP? zK(zQ3xBOC4q)kg?aZa*o_VIxPp@a9+PPV?w3A~Z)WWbd zg9|&DZX{|c0<|n~ zkRjTusIeB8)6j7)EJSuT%``_(^}Vb41f7EGr%I zc`=&B*ov|aCSY|KR|VHtQKu44%3yFMna%{!im@D8MznJR>i4E3P?ag%Q(AFTEU7%J z<$xMmz=5mopdnU*hinRL_oPIQf2;N4u42WJdK`oNe4C&{j1d#?(Zx7iT5?$hDZecB z@>R}PZDb*GlqE!eXbS-M=4s?2OpFoD`ojv4#g3M829q0`8r?{FeP9T8VG#o>|@x~-_y>nyxu_eTz{=r0Gotn;dL{W_HkhpUk~Ni-@+4X+ZYda&`2lCxcN*XPA3@yBi8!2}OgxXIJk}#E zZ^lzLOFtJODHteCq2$@a1Zre>K{4V-t$H2Hmp@ew<~O6Xu4C=jkU0~k zM21POI9Cy=#d*Z?jlAh+w5l+0))WYxVGgvM;}1=o8Y)W(d;<4{!a3=E;CJ~R+LeC; z1pgb)lYU?b?%ic5k+2a{HMv#)w9nWsdiWLGdmPL~z|4~T{kzl>OX)mHNN#~Ot6-fq ztl%tjaZV>wd3Eb|Y6X@I#nX`UnO16nsWqoT@G%k&NNVE{)PS1GEa1y5BQj~cu2E&x z32qw3dCtqShs;~Vis@b>iL;c6cVcN*nY%fT0lFf??q>%i**_(GIJ(>9F4@lRoet{EptXQupof|;W-ae_i0 z!p&;cb+{zdp+u-P;%?A)Jo2`rC@*y)^K#8vW2NUqB|1|KuNhpum1_aH@8w10zcC}{ z;0Cb5)-&t4YY93wb-K9WVSUoVtyg+P+Oyaxit><7wTNzd*L6ZkR&u;R2N~5aG|?8` z-em_C2%%pc{TT`>9|Qs8;b8fTPolb8?X=gND#oIW*=`MtEt9}!8ZoNSO}`aD58*E& zx9Fq73h7b4NUS)h2Av86R}i5iP}f%pj9WIQnOQoz#Yd9&?Cq|LKcK+#4Vs37a0*Z| zG?KZ*7aMYm#6u!HwE%)0p15!j?o-Uys!OVYIrGF)8x`f!6CR(63E_h_P$4x((EHH> zn4Z=;PGUGxqsuvKmdlMsl`XvObs9>W!*r398skhli$yl08A<91cj2D1?4@1sul3xR zr*5=oV`wZMdv>{(i~>&3=2Q0r`@LRXH^lscEcBVmaPp33OFLR<(x9saJ5DKyJPEH0 zU<2_ku7WPITYlwE-&w}_G%MsYdb;S3ROeP#Aw!{I89si(?HSLuMzXj0t^M7~=XlZ6 zFwJ5hOvOP?^Ll7iP*oqPmPUU%J6rR5gpg9?l2Wkc5t&O%{241y9-_dt;*XX;4rFPD zKi^$dA6Ck>@xl)4RafX{m+WaYt9REZF>sEtaN%MetjwlgS6~iixG7u!e=S*V6majx zSV;dSy5W-KVxyVIm|f=xX=FgSo1l(%`AWNPY^(hYrov|fCURBI5C zn9<;wNf_vGj{eULps4-;SsUi!kT`&S9p#BZ{iwefS$WjMEH zor-=;rCE%Bus)Cvh%o*6ZFw*c2#RCE41J_GzGX50G1~gjjPOh0>>Qs_s56{m7}&x5 zjqRwx)D38ZyAd4r4vzx3C8_B&W-&)fyC>DO7w24djVtQZXFcXXPvKcWB*wbIx5|=Y zOqbYXiuaTU-d7|*Tk`#ls2e6%=#28Kj^&YH3RRU~E3C+6BoA(d+GMWp3zo;1@dA3b zdykMLn%oQ|v%euZDdat@q`B+dq;fWNg`iIbVYw0>BJZdLNGm~~sw=$icogd*Ka zAe|36Q{!XgKHRn6h1_C#q|!tn)eHhKXK8t)?__B1%%Eazo7;!7Y@d&lJ)EbuACc=u zFI7`LoTs->k?U43(Na9TGkOB~y2>1AJf(b|;m>VnI+!ddb_SwsyRQ^#Tv2a3X5sUv z6+U$ZJtJ8tSe4<$ge}P+^+cJp;DlD^I8jFgwa%EeqJ&2X4>>}*;rIEI?26!a-^A)~ zY&sEW+~++X3Dx+23G&NgEItIhAnOYDhO&srh4yixBEM37MPCk>Y}6*NO4iNux-gKs z5r_$obAb9bUZ@l#DVO2FO5*2tTr&Uj@)eC`sMja8G!M8nIFKVWFEPd`fF-^q4zISy z%k&#Y5SYa$996qVlitY+TmQyO-jM@&PAk@!7e{g>*~lX7&8lFxF`|6@mV1)zZ&sO- z8wI)fLhqN`2VqeO6SZ=(wp!>%L&rm)L=LED*54GuPYMd$<`K-bx0Hv$4=X`4T9v+4 zq1r>79zq#uf|bXe_2Dt!s{oxkaLcDkHk*qzy4lrr@!`%1*pAm_S-ONXei~7XVoRXe z0T{t>Hu${8`pB=NGo!LzsdVoU)9ma^B~mE&=?2+u8qCsbII-Dq66ni;QOE^DrVcu3 z5ze$A?rQFih{g}O?)Fdb&eZIV=q}}s^g_^nchnoV-DA8^>8E%G+;=RPHFfBCrPAS8 zZ=(1t)dIH}%JP7rp0~wU1xvLi6a`CNxXp*Z`>KEg=<>k2wnRzT747$$PpYSrJL1Il zCQrYHqa2twvAR;=HN0*Og~%<@KdZT#sDe>$llL{_PH=g&eb_e9rM+#Yw^4?NQ1e0_ z(BPXXB3e~XJqc~5G|Fbz1&!?svX$}x@;CX8Pn(oo?It3*B=YuUNf$Q8wwshd=~=%P zx%mzZD^K&GN-3#dHb*ycGHVbgACsf$8hf0gR zsJ4jag_ETblH%|K)3yUU49Q-5N&?ayW($$bUv=lRdvAHlwI;TH(jgm0Yu-$@WkUp!d8 zGDhe0lg(&DUD)R}HbB&O`(!E_Y4L8u6d+uNps2T1HjFUn>RBk$#QT>oLt|e(K(7Wh zS*Ul3IA%%WjfmfpM97gjZ_{hicml!=N zSd>eezOzkEDnmFoc8A!gfYvHJNi%C+OisSQswVUeA@P&Rcw|ps-yur(Cw%t>X7iCM zI&~hdmYk4PDF;b9ccla*=GM!CgfJm)0Rpllj}UhN>Eqza{(*R%T-{l(gfWfl4*Mc3 zTM#&xeI$QWk_dR~b$!;R38jfOwa3HVGPMlZW#_WYFY+zXylRt1xR6Y`7ik`&8)kSu z7Fh~e5Xy2B%)R5~#`8nTJfD44nk+!41Y|F~%Ug5I)Hqt_ca=0%$_x`)KpiW#wV0{~ z5>D08*BUv?jnsImI2VNuJ*vLjDCi zokLP#Dd;PfbDs799UYAHyn0IR2bPlgU!1*4F1hlH4sB`m3psbgx`woy&{UDrPEH}h~8*6gS z^-ad+DtDY2mOCVP(cEZ?J7%EnSNg!$0_ts#L#L-U#LA&QZ5UgTi`uYs%mtBZJA~xj z)r6YXR1rw(Y5Qb4SX%cQNBZ@-C#Td;rMi!oWLilPOXfe43>##-dED{55!pyLRe*SMEoSQx!#>|^CS9# z`@ALh9Ivix+SRSxP4e`u!h$EJsS}c?C&$ws@-Sl3ElOsB?-FPfoKq>MOQ7yrUdK zk(aSf%GI&is#N%dOclD+--m^4_0g)70OiP=IZ~U3CF7PQX*#{$iQKUyX?iu6V~RCT z06eUG%l%EPM_xk7HlnH)9irZ9xFZG<_d_=8?S$0s=H} z^?sd3c|<>*AL;o2>2`f^{eIe{ay;6NAN~wxk=HcXTRIf?fjI?1Pr}fMbvV3A&bh(z zhv1yI-_E_M=*)3?;9b1QpYny$96|2?EtXHaLY|_1jO`s8BX`cCuBig>MShb*AeqcBh?i#H<;SU z8?k#jx?_XeGkbU~GOkHJ$Q+9Rt=HU-F(d^ZAPXe*3@VT4l}i~_F^ck?NjmzLfDc1dgWV+|oRFLK=%{G4@zzJ~nTCXOGgE}*~(A{IBP}LNLQd{id zZ^RS;uE2?Kt70!$5Wiuvr<^CnWylLGHz3_F?yAu2<^eIR0Ey6B8obZVXdw;ur8>q9 zN!FY55}%bId#h(6jX-3=BQyF?G?q+Ig!GAudSaP5H9IHs%}KXwzE|ZZIr$3qTB+Z^ z-Us&~u))}Z@viZv1oU)Uz`$^6_W-SW-g=gstDbx|)FMA-^QF=DMt*QfZE`@apqkL7VxKhF);#S{IDO^7x!s}+ z5-pU$&{_tY^9T^|!$!<;s~_F>hD(jGH9=22eF7a^bYwIol@DCPl3dX|cIm~ukN zWPWCqhMHEEta1)-OIOE3+-R+1DeGmjoscpeX~YgERzr1RoC}~lGndFqh(z_IZT#|9 z6tf&8-h@QWL+Kn)j6d#vCwF{oamXL#%a&H|;X4QJtDFtM$m2;z{q$Az&V2n+v%7fE zGi#c2pF3)b6vvJIOUKwEIJOF2rnoDu^dG+i9#KKIb9VDY;IvZ8F%UNjtL+fYyyDir z*B-Ddmtk_6@Ev&)cYHs)Wq%t>Y(c1m`PcFcARa!f2>&Na_Ak^$%$bdPaN za}07!a!~A%LDfNKE5F-^b@ZBf3zJEzrR#MGz+=v_E9%Fz~Cn-k(tMX#a4LZ_9gP1?mq!7CHq=Z{NYed|LM(JTU z+N;JsInVCo_qTN~%d;u-r^CTC1i1%29(KC_ErO3(i%X=9R=*Ijk7|M_C4H0*DP(iR-3?VGeYA zOZc1e)He%Oir>y3h=Q=-Haf6e=RHpz$IZ*>y6jb+W;rT>C(Ld;jjTZM{g)Ikk?VuxArkMVYpB{i)RJX=_3QKw^%z6zb^%#5U%gxbqdycDl{<_1~ zjcdi1PG`Cpp^|o8%{xf$q{u}})SLf?lfEEu)G^>5DzR6g09oXUZ79cHi=FWvO+Q7u zT&*jw$g5(G%d$o#qFK)>ScSSpa)Yu(bYpwoE7@1vIm1`{gX!7qtZPlXB(5ngb=xdW zdS{wX?H&JFw}gLyPw!pt*=i7mP3;lKyI4$p8Je7e+u1dF^Z##PfNI$bX#O^RjM!HLB@-T~ej*2WP;V z&4?Xe-Op2XS z)s_fWBJ4_Ook$Qr$x0tG7}#*uozC0kRAL@gq8QJeWe-)>n}-==MokZe|%EW4&5 zaFe)RexmfqZ?NX%qyv_Vg;ZO3T-xaL>v*O_b7|j=aqKt4!Iok@HmmRSVb!IlV_N~*j38(%drwLPPB_Yq(Ffz z8~Gg1_S4K{cS9}VJRM9WAfuGOICe6xoT=d`NJn)U4-+dpO!CM=2s z_RD#ZGbq<|j(lSkq3HevCVE4PIa{*PXO&NQ z;E(SY%|EUjRZ+3#Z&SSZNP{;%JqNI<&o9c=MF0bj!t17p>}O7v^COpoVY1p zx}n7BdmR|aSEhZKcGeEsoPHqGb5~Yw0KS@4>kp&L+#g||;p-VS&?)t~Eb9WqJOcRn z;68f}DiVVylmh%8*7ZWwQlA@Z?`lUkxo3oiF#H26m8;eF1ocQ3vY;UogSDvliCTxH zzcm6x0}}DYa1Nv;#mbeGqQ8JG%su4-@Jwzgaf&X-jK^po>af9y#yVU_goN! zH@pjTStGA=gh*gi0g!SusAEys>)U#c8es- z@WoRf*c4~j80uYVSQi#`46?FpVnDbE)b4LGPcC48qLvihm!=Q_bK!ABOMM_S@X9<4Jl z+CmJZqO^o!_4*9GVKpzf!AJ1MM{*f2h=WIZTrTi5a>tPEQR&BDdSwS5QKb&id;_Gi z2g2{5i;qhxTj{ zE2)q$UXXN!6>LkN7;e9@A1&DZjn&dPM%IK9lJ?CVZ%$S-@RG$QDncF;g*i=t;wbCr zs};RLu1|U*uIOlY!mL;oVUR|sVI{x*o8sv|>Ku}oBcqWYK=;=VwB-MFdi#In*Z=(g z@6+2#lXm|xy?wypXq?SPM;CWb>yl86yiKSPum(!4B_jxJ!Y^5yDET+Tf=3Wd9}F2X z0*s(foTzpbO*9B>AgZIT$a&^z>V-hR_m@ZbUrZu97RAA7$Ts4w*m{yV`Z4MZgmLtV z(>4fsu%QBhDtni`VmILohKks86kw(tdRjCx4Fi&_Nq6SylH5`d^ORgZWD6cOtquxc z?HvBmzXbwo>?p_@lgXt;N8{m_IGhs|#Q@`0;&sGT^Xa*1mHtkw9OgM(lnZhQt)#1y zM#^w=1ZLy13eEUj`B-JTsEh_K)s2K@W_gT-`@{;0HQqAAs;jg^GqZawEl;6nv)Yy9 zt)?BdtxK1GttLE-(%hxKs#?|Ee`ySXX~b39Pf8t-u1PKS?1EqrC55l8FgW0`84Y?nY!+h~7(4dtpj5R}8vZ|LZZpz%q+ zCl^tQ_sB!?eHz&%Ph0F_B|$rkXbh^xZH%xI)EO^U-3Ddr!)EdSH;2c6tPW>MOp?=| z>R0_|g$VqQs^A|(l&Rzoi(bjp*woVPf4e7R;bZC1j&F2W2*MYCFH^4kADC; z6DJm@P0OlIpR*Amghvn8%e6?K~oV zwmwap>1Lf?^0MJUx9SLV%X09B6~S87*T?w&>NKyrjoan$^p4_Rt;uhvIimV>>%>y* zX5Ff;vfM6-^ncn7gow<=`Pqy@^j{jCmcNDalW`4+SqD*<@y$6|IVjKnuNZT2 z#&9Y7i7%g@I_7^CV@jsxrXK%2##Da5K!T{>rOmpX$x28fib&0nbYotEL()F=Qhh24 zf$PGNt|aRuiJ_KsC#sv-Un11fzK zCKJqRn+M%%51o}9SG@Md?|7oGv6NcBDMnUbC0}GJJ7&_!N^(qa>RQyQnjAVLn_D{h zbf8M949AJM8C%8*9D1-e{ix2hO$AvZtVSB8$o$JYM$}^M^78y^O8v1bt;GZ$n{olJ z+9l%nEoUEmg=sm_%ZPl3%ioD@!?^_Eb1XsF4??HED|-%q?I4+01+9eYant%+bgtF> z>d_kZF089kQ$Ulh9p8-IpT>eAu8CA&s8Z0TG))+kLDzgVXNppU{uH6YI}iA91m6?D zX7^%B3k}YKefX!m#h)Q+y*YKwgDQU&Y}h{em9=?YhR?@s)fA)ME}M4F7-nW|62=Y& zr2(cIcXSNWc1v_U_y^{}E#S6=x6n^ZOtV|aPP zBQuv3UmC|9sHkxYCQQz?j>)1d`GG&+gh@y%U#3p#E%BUU_9rOCjGyF=iJ3##dZxUZ zOGyo+^=P1*^?(V_+>zc(^qo=2PO};C3zNJsMdBp++DIf0e z&r$6?$-njVx#wAjyD^W1HL)}rU%@y>hXB&st~9l5^*Z`ncn(IAb~&IY1aQy5>fs!dH)xSC`C!u zeo+9?H)~BU3;P^gNrK-Z@OGZi(DD4&PzkbCv4X19(g&L+>n2xM!WHo=#wQv)5;BAz z5Pvx}A<;HH+R0F&dG08{etYUVj{iq5w>H|uMQp=BG6Z7omc3rHX9yPurdB!;-OPDm zSp+JgPB;S@XImg`8zT@uUWh8;&BiZH*EB1RQIRmbc1*rps?w35$(7<+aPMNkkc{P% zS-A>4k7Midre^TZbCnkc`7)>eB@j<>xe~?Qg_BeDO(54Sn#IeFvI!-_`o$LocA|kE zbij{F+HXYHSjA7^E!60BS?A09yl7%GH;{x@_Fo;*)T2n69iE@!xVLKT)v3m@Q=vpZ z_|u=Bsj%}LH?8yB)1!`0LP_W1$on~TQ@ATkrre=&L#mNRfNQlw7mQWa9u-AqSvh~Q z9o$t`$>sFlaEQ~HX}rNGFl@uLHk$pODx+yx$>wA5r`)Cmm_JN*1wor4c}R>1Yo|7L zw*H}~EWudRIc6NfEsK$6kbiFN!K#`s4#8{F{ioma+Jp5i_Slid5dJmPPwMs+k#|AI ztwd1c|0SvPI&22R=X5O)m(vgV7^(?Bf`UOwC6$p&Ph})B^qhp*=qr8EPX5IbF0>Cm zPR!%<&G3Kj_$)LKRR6MMGBRzvaplrAa9eCRE-tYnN*K3vEFMLo{5( z{?PqWV(KDDD@UBG5?O9?GRa|rxqpsi?YAJGly@^4f(}KRhg;&;oG;{_|2%v_>VuCE zJ*U9Z5$XvUkAF&#rExCP$K|`g01hO~TCA(?r9bpZ_-J=6Ap(~2mPapO)YgvL+mnWY4`*^K@1cI(KeGZlC z_c9fNpVS{y?30BFZX>o8Xaz2}}{n`lo+?JTiMR zOuc()KhISDO$DMIzSg|`WsC!kj3#?_f?PvB!?ZzgG|6Kc)zI4U2=thlN%oX~fW%jX z&1IbWeq5!BHsJX{Ap22QXOynd-}4$rA5m+*v6wMuA7OGNO>(w(ACaYl>Om%Id&Q{09L6AK6Y9OXP!urJFN(W;r?u7H~$%meP zcVCEpqoX}MDk`UZmrQ4KYPf`FpP8a~4uYEgq;(RmJ19@x$q@}hz3X(7b?Zs%x@A37 z`<4qsFOL0oi5>eD1!8*$&wt{E-T4kcZ?{(%9909~opRS3qi=Vz4^Q8EmoFz~$&JC? zZcDN1b{EDC*F6b_QlF63m44dQ;f9dpRTucuVSs(@rg)R*eDm?P2@<`GPw&@X=Z&?f z?{pu?8*!EhgzhIu}z;~g5M z{x+!ZIs0#9cn8jBI<)S9-{aTL?nc(u9v!|*ytTdaojm>a-oCz0_eI|etj`YE$v(u^ zoiO+(lK)$}<$OQZ*tNrT+nB9 zI;?|ZfW|rbMZjr~i5e8qAW#QIhq4($$aMfj=pa)rC$ygcS0vJ*GelbCt|}|ag5Ie8 z6a#|ry-TF~cERrlc#EuS6d=JY(x_YpYRq!rhJb@`K9*pWxDRL8*4N8Dumeb*maVpfwgmAFTUZUKf&6e zy&V)wTq5S`7!k|by3!`DjTKopQnG;buRv?a=4SA7#6m~~JQNWX#7NT+S6csM z;24o+sj8(&#`D9noZgBErt6(Uduu2a)+rn7a#ID-7}qSs{hF1Iz?d4eK-C)2X+OQ# zrIo(-QZKERg&5iCz*O{eeNpG}x@KXjqfi(a>&U9f<7s_8R)06kr+d*ROI_<@H1ss$ zA=T<9``ft67xrp=Q#%A%0^O~(xrK9k3*FN?`ywoG(EvyUJv}yePuH{Rg~b!30jw#6 z>1bbX}+th z@n0VQCV$_6=fD-oJY*ZlRypBY1b(t_F4J7>hImRlU6f+ZCV8uRR7~QM=YIer=~PCm zDmmNK#zq3BxNHd$bZhcEg>y|o+JAwT-ZjJH?$hz_BDEcAvv9}V)kbo1I3hxZw!1rs z^(ss0dnA6KpzIMA*2Yb6CYMz4c!E*;byeJF{f5&@YG%wkDGZv`n`ypHxT<6 zIb^xU8WUu{u^!72tb|z~!@#B43}1+5ZjH2su3}n?UlKh+32gDKPJE1inb#D$MQu?@ zJfYDJGSCWF&o8oGF7W+ex_1a<(o;S?7g1&fNpPc|>K}}a<-`|UqPk^-H4aXBOABc` zmjsstj-C!%5Nx}e`JXD9FFF=mRIIqnK26)e-&$0+W%(CVuBP!gG9=i4H8#)d5QIto z7{&GP=QncgQ-y$z^ZSm)SGgG5_H8)|!-`9oXdUM7RvP4YRC>x8EUw2)yCTy^Xf%VZ zf(m>=-6sBtu?OiuVF~m{Q&bejpTu4?hF)>9jxwP~`R%5rO>$9Pj=pZ-ZM8Lw&b~h) zFa}e>_*7$95ERe)DFW)1V2$lB1TB3kJ_nw6>u6^D;N@>^!CG#g96}(}xUP?}WnpLs zUm(`;ykt=zG($EJqMWR{xtKHcDcN_%OG+*WuDG=};*GiJZEYbLRFJmNw9(!rsJwW1 zx%6%=^TS}Hp*>qw-;kxbDm`WWPF)W?7AWcU%qcc+=iDG8(*9d?7{xn;2Fba_f64jD zAcV^%MW4hV6TypyI64ge3=9}%1CuQ?qL7dOQlG_p*?4d56aeFVSTN!! z!9}gRd)Q`U$)se4Lsq81@sMW2#RW=#o;Fq=^!jUAt4@_=$T@+GDQ@hW0~R1NFtSU1 z|D|iwBpOYQTqyCxUPpeh)EnoxL^ZdyC9yVrXz%#*$5Ou&1375@kz&XkZz% z2)~m0$?NR@UUi@ZW02n58lY%|OE^F$5v|2>l{rXNv`c`{S77j1L@fGCoR|4S>uJLz z8Bvmrb{_oYu?82{S7he_i|;gep!1m-MX}3nrwAn9wMRvtyVtrpX%zxmuyOx=4XYa%NRHb<@xJ6FO6byzHut;2vajQ<%mNQeaxww_J|gi zUw=jpP`mGhn_^7btQx3VwN;3z=N5&mc>cTSHb#3&#APt;)}Gx5W+y&~d(H2q|JghD z)W_QTLpw(szO{|OruWp~V;EwSfphK8o54rP&)v^!Z~f0S@U8lu^7Mq(_ zS9$mzQ*>q#>vpdQfuX`WxO-3&9)FxR08C-**Cek~bkyz7JUk(cgj3R?Zq>RIN{8qv z(FfKlyC%nZ9US3C!2|~(%*oVk>7$fik+0xIbFwx=0`e%!b{+`Z?orrGJ27|7n1>b9 zOhdqJ8WLZQ#4X{XGNWf9057IMStXE)JOkZY*a6;A>0q4MAyYe@h!(qsbOw7KIDXa|j%yzb$riW9;ThXZEqLj{e#nY=G0x92=2fWMRIU;CNz%97>tfw)H#d z-^>4%+{@t^rJE^{bOgPt=7=lPqMQ+zfLmFGQ2V%W9<-CRqr(FD)BP%@Lp|x*!-VVq zfwIc<$FUtm4}d{Zp>~5=tYSM^W>~E<#<;pE9RIHKEF`9N`$24RQ^~Ba>%l-PSR!`u z*f}EE?+tf*_f@mUK8x!RvvoN_-YmIKxu-F2;+yGB?bW3rn#6=_; zM@}Z1MXyJmo}NnEB!Bh6x2;jR5t-x#;c<^|AFIXkk%1v+oaQ_p{ACGAC^LDNv>F)D zu%)-NP(>2UYfLbA@2-$66%~8lOV2*N4q1$D*poM98MZ1VuWWH-8;&EYtGXGZZ>cM` zz^pH7ng4ye^HS%o7u${RgiL}rvM+W%eiy;zGlVq^>hX#-Y?}H&`gbD<7P%UYo0Gk! z4Pn@sGgIihj=Tk^Y`i3lM-N=I4A%%{DoA;N2RI|iNsV@7MH@#Nn68DN{zAcga3hRAl^z!~n<^@31~7HaUmglPbzPme z63q@p#0t^;$Czxic^q#8sy$J`i_4--Wczt{^h^FvItjQfpzO zvnqChrOQB5fIGoKg+qZsE|R;+VMd=W4Wq}JwpL>1=eoY7*<4$~I_&mWkVe;#xd+LH zN_S^zqCofTtfT zcpuh?3<3nO^T0XoFkzAK2!_KUj84|OVY7Nto0?rMN?XUID8(8FZKZv|t_r~+DM=k1 z2Lhu#A=9P7Y!o5AMRjcyN|-~RQ8GqmPV3m?uNy#XQPZ2En&Zp2aX*NR)cEjEmu9^^ z6Olcmjy#3(oH>_0)_D)bQs}MqiHOr*R`8 z**$TS_>TEyM7PP~`g340OKYu)LQ3SAm@emHQV2NG+q4mCU} zTQf&SELU=*t-&*i$gOF;fp(8Bfv`r<43TAXM?syyVw}ra>0 zcg9K$X1;j%K;@z3=P%}mkzE}>yEA?>eoOm_kC>ZeOLF#wfOIp+1H#cE{4~ECz(0^z zel`2}48*JIg^mlP(@h{E>Xy9#cV2jiKS%A{DVUdPb_pckN>TS?+si_`z`qOEF(ubQL}}ekCN#5$x^!6p?wreBuh*YH}Nb=?|$Z!Q+Exi*HD&`VK?`pDDr6TX{>bJ;TrqNZqXU zWpD`N0-q30xN2dzB@ZZPpayA5x(VcTvM8@pcrsJ-DT-xNdbySJF`{CUkcezhaC3{$ z%62Q8XCkRe3M;uv($AH~YGxsvm~AGJh_G;Tbs*vY+b`(B4E^l|#R8l!%gu+M^u3WF z1d}{B_bc7U%gf`}Db}QP{yp*`4~y+-4qLng zp4gDt;(Z*^Cpn6rXvie32SreYSs1zrTT3qyh6&pz%r+cgnOkI}_Y@*~zZhOdinE5? zU6vn+D#{l@i27^cls(?>>nu%%|HcoqnIfcc=1Vb2xof(ipn^XDp@vLLQhqxk@XzH} z{0t3I#B6JL*@;0RpNt3a%}-Gbgu=WWHI ziW*0;SR(kSL}MY^xi#}a8M%#tnHU`Jk%J7ixd898RV@dGPfR2BuNxrD$q0lS{MJ_m#23&*&NU#Hu$u9EPa7ldJ zWIm$02&05z_yqB50adp!#E89`VYrYXv9V%v4g4_8-!Wxu0x?l)W(LJ~ zPIv3Etlkx@T@-dEPV9$?D!BY{9eXFDeJ>(?s{%uhzpoDm@nPPDV7$Bq1Z8e*;=47W zw=1;W-hwkoH=~H;6$5c?)_|y6^7d-D#?#$ z2qNh5OG9?pA$-i=5X25+(0lRA+L7x5;^FZV;-sa8#3wQv#*dBU8N|yo<#MmAs`nj@ zePu}a6Dn_pr)&p^z|hCeWIyAZdae!#Q)Ww*F~@CO$mNxe7AV0o zJ1io|(`<_HAg>Yzt6UMAZo^7)7%3%LdoiO-=<=+c;yPn_skw9Jq2yWwE{Nme^k*+Y zb9TeRG_xFqk$LfwTsFx^B)nJQlsgU2Ne9!&8?swDB;}c(2Ok}Dnh?85EA8e)VB13= z_C@_AkniHDkqsg-fl7#>Dn*!S(W!PZEbG-^L&%vR*&dxSLeOiW37F=_pGW_ ztLB^_?PaE;QxEFA*O*^YV%eIK&w-eX9!C>&?{@#9Ds5dw(nhp4O2 zoa!uE3g`BO@0R=~JZX^ad0h0CJeR}+{CPlnlS+%LgZu-Hl6a}JAOP)+lIBCg5`}Th zx}3-}uS)2dCN^n6!njj_1|n2l5XXzGDBOfWyee2)0g`x#iUO(9pg|p!ln5%>kd_AR zLO-M|)ZaV;>OwU%OgDP&G(NL6qwA!!c%l{iU^Gb8!Xl!mwQ7u%(A05=0DuOnm|!E8 zxDo>J7(F8+opHmbsrF3#b<{=pOTLSi!Qhv%Mf-@1NgMog=D8Ycay43G@v|dVk#J>y zM8#lAgRv5oAQOkOWOc&^IzuQ|vy^015`FY;hG6po1P`kqs;H9@;^Z2PE{<=Pa7{IH z5w;%blDdI~x>Ln$cU3TO6Mg1yg)CU&!M8(r9VjDV?kLpMUT+7=EK0f`c|%8a`Wk;z z2hyept6gsgT5Xz^-lcu6Hq}kxeplE{!j^tn2W}qPYyXoW1b2@A2{Y9F?4|!|X$K z53=jvGGjSiyy`VfIl|^4a<7z262y5W^^+ghkYnZ89cw-@wvTF4nM+xjHGxjy0j5pthlbB47Lf=Q|4H$PxW6cif0U)WLi?iHMT;-$Q|c;fgMBh)X4*mcd0wzzXn? z;^R`I@C-c}UF9R>B)DYodv(Ssix8ani|KDn(22AaJH)1@?YuJSC1_>3$+tPAts4uV z$>14ks5G$-PRU|<6_CeAs|q&kS;s1^frMehO&|dGLA#0zdB>SsA#2ICndu8 zgOKR|B)KD_3{Die>IHb;QAz*uBk#})VtQ94-G-XW1u`OG#r{&GOO(d_%0^F^UnnvZ zSsNC>yy&Kqh8FRw&%-ldb-^4ar0s3hzmP3w=Z}w!i?C3&9xlZEhh`GN#HzAG7_?3k z8-RB&6zDHOO zgwq-np&n<_P0^geDwE3-d+`e2qEksx4Xx}ie49xd{r-)v3#2uiRpL`iliJ3-t|{05 z5J;{886Y|p47D?rI~3VVaR`Z5__e-TZIlv;f1^ZnMr!2dV;YYz#GR&K_=tct204F; z5MC7pL}n%XMxZ93u}7&G>X*zT9#i0}=MDCIk|L}p|FbVfL0qALwr@;;a-%4(Q^KN9 zb7rIM1)0C5pUYqj+CkmV_7XgP%Zc;YHc2RJ~O6du;Sq>X^BXa#_dQ?(;Vgl{w>m!8z<1)0}fqWft&+YsZ^9 z{+fM-u;uNXz@=Zc za(n2a$;M#U>HL#=*@|BGWm}u}b=7h6F?@6(JK47Cih9pnwjrP;I?T!+yienv~Rie}q z3oF|V*&n**s+mmXz0AXfjxzZ(1?>kCg(*t*7ET5l7vln`e#I3k_nQ~P3{yYoS17`Z zjDt8+Ow3kS4N4cImr_;MscQt8jB939RtbZKxBa3{e!bk4_gCPg~5Izsd1X-9k{Puvn7!FUU{qvR^q44))h-GU#H zd2763b(cHua*wNis<;2@QLx1*bB0k_qclw77V$fxi!X2vHyGzNOlr_Nq<)E%7zZ`1 zXi!U{`WUnr_pMhyLxvv*u2=9-Z=&u&Po?aMSM)__r#6WrWX(X`3GOqE2Encr%@Kw} zAnxh?-FeN77vqa+92RLAkjGXZr9@C1}n5sPmd?8;6W#w-f1(BroKI z3@@a>dwmU#pzXsw$&-M3Ur0}8(zvcVmZf?hJSSXD%KC*dL8lQ$R12Sr0Qw4%zjHuq zZ_cOl3RBWgOBz{2p4n#v6j2O7G`;OSvn)Ve2Y(2l!Ej?iw92;Ht#@p4a6Xr4LtB ziO^t`R)UvSE3{9-MvZA8x)wKK4*EoL$<>zpLc^;HoiPREZmH;@fp{3-e{zT~ z$Ctkv`SzmQIEMH z?tW%7vIIlpkZiCBtGed-pj@Z{mkL4vIqx(Oh8`6A7Ac3J$>_K@C2w6fB=)m%Ng6~e zWXAJg5;ZRW9G$|S_`L@dr7V>^uO~Ewswia2)LLqSJyyC}+MOx|pt}`h-q|iO&tio6 z!|3Jn6D1PJb3yxx$ll@{1bi1p_od<{oC*E14%`M6{wM{tez0cr5(TaB4ywa1)5Mi= zg#uZ=BrF{4L-2QFNk-0Sb!e}O3(4N#7Ei4ZS*Cu2{%XHxAP{Ihw z0!_jrzJArM;L2Z`(S*&ssufobEocdiwiQ<|MIW6Qnp85a!hoNdtLYc(P19lzxf_zF z=ig1^r%Kj^CJR@E^p!)?Jl+i$>r|a};rdAnFRKW!%7yS1;}Az2z)1&S@;Fe+WC0mA zfmBIIs_S_XTn1=C^OFujLUB#o{(M`oW@@f|R$!yjjlaq)9@mf^e8oV~AE6@)K>@SG_FVjtN?_;Sk&kKwZq{(Af%+TI{S-{fo0w_F|anNsv=}-FLD0*FEw5 z_jd91z{eu~rGj5bY-Q|j;(6_z#}l--ln?ffN-lJIN$s8oP23Axn2`|lQ!8!jSh$_K zXa#?_amYChK4+X~(qVv&l66P*AzI1QpHg;CEb`8t+(yk+)9RD!EX zUbb~~i(;h~F|D)#Bv)~-<_XW@jmig*IZ$NaVIU?YROV1UT^Tf~xlz&6K@pU6qEJbr z5GnJQu}ZsveQ6iX>^R&=lrNuJfj8iCzdY>T*4q{!y&m4vr~h;lSKKX& zehWXUg4pl=WJ96$4Xtv{UH9h;L!-|vU)Be4x#C7B@*Oy<(ie)olAsUooV-ry3+-CD z$KU5X^)~9=&Rz8@f@Xi^7UPM3@N}nF?40nm)e|kIY((DNzoH~hOR2u6WDiJY#CR$o zhy?>-X)~=_C1~rb&tjzWsnR@j3TPn;4X*IQ?|W zj*58>MWctP_cs z#dFCtKXe#|AN`!BT>6 zU*=zRd9t9U^gOG^yj(UMrY0s#(I?z$6vt+d{Ks-sfTn23B4gz3|WsNfH;^f}!V68G})FE;Fpdk+Q~DB;PxA!##}HV&wCv4Bo|FrRhP9{*UMa*-^a~o^6BldJAv0Ec zv?**q{eZ~U+wm#7q}{@Vtm9c`PV2pMx&};67ID|agUtrBCdy4z8H6~aYS-F6==w@7 z4y~L`oQ}1^i`9iS-Bk8-HTz}|30pfBnbgCSzNQJ~*+L(Ol``Qu!;t7AXnV`4m-!Lc zao;g-yCzNc6dgw&r^kAGp{6x`nWLpv!2(u2{`t?ycG*3bE{zlQbv)!+Qcg5qt}=~f0kZei>b?s<;Td=YM#%qAmy z!n5EhuKwl@|4wJ{>jjn9_L>3RE$|N)cAlz{-a=1Yp7N1_Mo*AW1-am!Q_x)%Ug+(4 zrCnm)UwkVIyH-97xXYn=v&?hlW(HB$1Ma*yiBIuqE03#EIGK3uvbGrg5hL|v-6;q2#)^-A;u*`B2T1al)1&zIc)=!NwxVeCb|qj-zy z0dg;>Z>@YteG4Ir#J2nSkRUcd7z*wNx!-sPf_#sPdiSW>GoTtbisG_g#n79o@EBTaH z*)?D-EpiE_*im6f1ayxC53yUKkA3#@sV{=_p9NY80r;K0H+NcIqR3n{tVqdh8>HXB zb?QcOpB4}z(b6o~HaCImO|6Ho_%9MhC{4Yx+C|yGYRhq61Ou|WuvT20g*a%|fCt9P zZT;2%LZdvQi!W#q3he;By@$vbwF;H{Leng<4>*2NnJ?6aJUr6cC;H-TR_GyeKjQ2H ze~F$h`w7mwqw)x49B_WIy*Bznc9qzL`Y9HD*L0(H&%5;ze#vqaxEWOVA|fo^3~BqY zOKifdj1m5+o-kQbeRv4n%kb^7A;{@3cOJ4pj6Pd%s>=j9)%gi5vJ zwh`6CiH+AJgiVOrLD#A>!r7H*V`^cot`Do_NOV>a`5CYMhcL0k%V7pPeKsiEbeX*e zq{9@D5iGg+URLg8RhI#X`uU?- zxKU0}<>N`X);}<>#~!5AF7eGfl(X~HJ)5sz?s|Sf&QF=Q(Y{DA=Qnv$eyPrnA893i zVFQmd;_Q9_s88R)jvRh~_%>l(c+X#eG4JTU4UBo0#H$S|<;MG04q4Aa?{;)?iz-x%|k23Qx%!LIB7V^`upy-FhLs>;Bn=|yE^m)IVaeG%U zk6X-AtVj{!pe89aNzMsX^T6)Wtfk-62#3<3K=8pb)|wsxV_nmD6y>jrVb^i(=Lzv% z=Mmp;FS35}GuCgeo-H?5Q|t)Uq5kfKbj8E?z!@N>GZWc!q~^y6p`Jy2-a@c^-i1l` z(h6@@^}WYSaQO!tCi(9m1s{`1S87)M8h#=C9Ka2W>-tm!V)o;B+$DRx#5(2zbgkQk zaV_B(vhgzTH#VeB>R5jm6H>Mih-cVcoRO@T!H5VAGm&Jt@^Gg>8KByQGLz&tI%s2x z9byGpeOoxM=|!G2nmremR<2I&6O1-G;{S*Y5J0a~6}aH=MavTocxWgdsb)zM-DGU3z2UOAIi>LfxP@wR0Jhkaos0S5{I*r+Vl4`?j36mQs%P#WvF1X`9 z(xUqQh?}0ar{?q&odi`RdYUP6oE0#p&x=Xtn<4IXKwO`cwrOpWUk8x%Np1p*2T(+` z{eRup|A($-Div3o_~U%t1P27f^#60v{6B)_|F3F8v)X?wudm5SkkFw7%zb_)Opn2# zl&xqNTcFBA02nyZv?>=3?ZF|&80u!Em+_ zQ!hgYDeg28BZ};{kn!+@{wDRh(1xsGtbjhBYP1E~g*{gClU0C*ahS1NlMk)V{G{Rf zR;CFQ$A%~=+Ym!e^QGbX`oXcR0lST5vdskB0)$7MOm))s!X-@Wf|VmuTP47PV+E5h z)(wPbW4UfYLzmgPn&n0^mzS0^-t2Zp3w)X(j#au_k=D-9OJ{?|!9h5(*JFxpaDC%3 zl9;J1vSjS)EkU*1UQ^cfH#eIMxvC`BfO09MJ`}x^*#=b{dG+L2Ok3!4yN$I^{6=HD z>S&UCdM1KUhX~1-M~P^hLuW%U=GJ`6o6byrvWVKfgTYDj89IA^9g-*(qB1+m(tW(IP>ESUd zvJ6$W*GR(PBPsS$@U~rvI8$}Y0Uo-#tKcx9I!oCV9GRt3gY0(7a3c*^!|P2_Epk&x z8~j&~%I=uQT$Th=I2S?R&FQd!y6&l`tuBg8>+R}{Z0~)-6sL>pE$^ zW+I6;6#lDcU(^!QojkLX`(XDD1(EA2tEpJ5HMrI~OTYmyK}jTxZ8qumdugOgQa+~G*mvA@x~Yr!$;#eoXXV%_796`074x`JW-v!F|*r*-q?esxJc@DTud z;1(bpP-_Va-KP#V@{CX3D>73&5K+`88dVRw2}ASlSHwJLlq;;eIOaM4}C() z-@N0$;V-?3&5C^X2&(uF#NBh#QTY9?_*}~~YN|0pgC>b}5sq~xDT+ZG=EbvHko^5? zYyruN!0NVg@rx?0m&E6~Ju~ilKF#us?iTU7#Nlpri$%C#dr&Kf*3Lj8m|h#x(S|1!ie{U17ws+Jv&2-?5lR+%iB0}c*($#SV|ww48J zasT-aH1qxWqG@nkxFkrHmXR@%3mq$Zg*f*=pOODSbln9C_K4;p)O?Xq3ic=0R~e)C zF`=b$IGvue9(+z3jK2R}rji5M?k*@u>r2qu4aWCoVsA$n0kbdOze8=u9FWe-NNDT#*emQ&|Ors49R<$uvF8Vi`FVMo-!Pk(V7X$2# z4<;NZkh^qQL=rVe_2DR}!0GW5A}WkaT+P1-BS zEQrf4KlQ0}E(D(m2g)uvPeN~tTfkPS3;KK#BjWI)Ho>FSfwz|$Js1WjT5c8xdCMK~ zty*1mr?8_H=PJ@$sH?MGTUj<2?trhEqpYD;I_)}hVko=vj8G)-D63GGm`lrz;1Mxv z*yE_cLo}88J4fXeA!k7A#f33anr|B+3m*~6I(NU0F&m;(A+>^C zOQ{4{beMhSflQY8%!GMku`V4OJ2{3H=I1Azd{Bk6B7glif*W6=_X75{9*P3(Vg}T- z04|sT<)f4)3PqpL6mFlFK6ykCbX3DHN|BoTfxLH<%&c3$3L!c{z#-?DF*$}wfAH&f zoU%N+IK~~7hc=N1PgHp=YSC}&{^+=kBZO^sXPCL}q;L3mu`r)V?`z1qPvnBwI5=TV zL9z3(A}%skPd3NkxzY#)4tQP2v4`|EH@ zQ?3(n+Q0fzS|#G$RpJPaWTD_k9ad;VqNRo5vikqI=1g^&p4Q>aUatKNgVOH0nk3&W zk}tF$Cy^prMm#zi=jn{Nfyilat-goztz2@Tmqom0s42CmJ2*q@kfY{o9(QKRve4VC zJm6{YVPynBlHuLhx4cw=$D!gGJGK_l_>Ffp#b`*)RK9gRO6&1J?wBMskyql z^)uG4-`iv%@D-cQzFIU&eBwOp&^TX_+69 zm6<0T+iA3{0*Fk{l6hcQ=TAX##e!8S*>35QJ-k!#L9$l_4AD1P&XQ9n7WBy{*ct}+_#%(cW4R{IET zFs>^NGjcu0kK!w?4UB~KMELyPS+0w+&E|6P?^`5~DnS@$s{mF?W6BCnV_9W3`6hy5*_%z=*%X4#DKFOP$eV(*T7== z53sIMEZntKuts(orO6}`UClvhO<Q@_BhS7 ze|z0{%=?~K=d&DHwk3!1ZOB3Xh791_MY6{{g{*;u`GbrJlmnXN zFOUa^3mgN&4m1O93zh@PL*Nd4E4VAt8yP^jOWG?Hkm+x*YtlQ_I}xDeuLe{LrVHMM z?GAsdx$Dvkb?dnc-&@^FaVxqj)0-L43S(h(Z`w@T$L;%JI=8gFZb*r#z)C*=$ZjWIPm-e_Y^7RNUz#Vvo^FIC;(r7I}zux(` zI_@Y2!Vv-_>{?&YkRsu#8pVlJh_#>nZ!PS})Ii-k`|jN>jyMdG7tX*6x2NZqU+?3c z;N{I1TwrJNj3bb#Z9YF152H^X52W;+J-J>f9YfpJeqt?)khvFCa;c4fLpi;ttrmue zi?_Xt5F02o#x|WPyjUj8nqdEa4fS>R2dH;@*QGry@Zu88Af^-!^)=-3X^6edm!4Ay z;kh^yZ0U)OoGk>Z8oDa^wbPi!w@nW7$yqsJrmaA73qGCQ{?Kbv;_M+W#?mqNl z_|t>vf#8F6!+C?hQr)udUi3=r;`Fiyg!)_TYW21T+yL!>cSCv;yaL|}?TYor2IK;j z`saf1gW^NTg2n>zL;3=LqP+gPwc2IxwF}q*?t%OVlm+|`>J!=>Ee-e|+$Z=e^)37E zO)tN{AILYzKX@L9&rm`rB>$h=A7BD`@Yev#Jcu%IbYK_fo;>)jlZ@kM?S;)8tBf5d znMSV~3s)R4EwCld5W#M=^BDw$V@PoD$#t@4g7KFW@M^z(akD{=#vs+82I6I zJP8&DQqCteDlYmd&adfZP=Y?$3L{6LOj)~#t`x{GS?++Xm6V2R-ZrU?iK5qI=h6d_ zwFPaV&0CK-<$^@HgW~7=$-1o+3%gdOwD=}PwwbaTo;==`ktuO9{Civ;HoS&;Z(qOF z7xGznFR#rQF7sWSsDv|)ojRRcFjqrI z9+c$U)6^!VLJv06Fr18$xw8djTPE48dM0$W(wV5Oie8-u>FPMwd0hKsG__1isSO)f zblnp*nD}*DCb_(o^oH$is8_|ez0N!b9q#iFZZ~W7MOGf(Wxm~Oc+IrB)0P`9*~ZwZ zddW>n2r24ASF)Q~6BEO%*%JGb$|lt1Ypie^HPH)aF~I|WRkpW{O`JaJgZ7~j$$_o1 z+$qug^OuJw`^H0TBkzx8EJ!|h@ zpC|_c?uB;~d#TuZ@I8~S*soLvR(k>m7rUuAdWd>9pD?f32ROS{(RRVL%-zu4+wQ2J zFb7k&@WZvib}Zdk-D9ul?umEk?u~cVx75*gL3V82c-=#<2=0-0mAhKedhk1@-sIk$ zcRIIt!?gi=Cf@9ymIo{SZrFAqH%zvDwT#;kJ2pIEJ&<|T;o2avsGERR9Jf7%1I9KZ9)zB0caZK8ca&>vKWrXT9`IPTPuU11c-C3R`#Bg* zk;?X({Hdno3$_M|$)yq!Ka=-Ysm4&x_G5E@i1rkVFx!1DX<}#U6$R|VjrN!YFuVh< zv70_b`|SOfmC!cX2d|R>ZMlb!{X%Usu)E4^Idf#Bi8i({(fSB0d6r08Z7tzSCcrb~yPR^qLDFQk5JR8IuyC=th$KD!fLP<_03^<86 z^#AEMjmp@M3wzJV04=(cTR?{oA9V$-U2p2`i-dAV%Q*T_Q zkSr$`4fyj}wV_X8$KJdZBR_iw-h56yxpUNvho>aI@}K2}681&y>DgzYRtSLxqtHhi z?b|n&b?dB6wl6;N2SM03fgGOr0yFi+JPnwTX?1t+SMg$A8L*Y-JfaR0T9%yJMNq6kts><+8wt zc|=KML20lk=s9sPlD@dn2n&yVB|thCGwUAxE8=Rq5n)D||FE4JnXUGlSrqb3CIZZ9 zf7@xN-c63z_1T2?zq@lh{|QWqKO-R=EZRw8ejr%JE{7JX|8iaUSt=C~o%*Hq*@AC}boW17Re7-wqmxEC^Q7(i{#~p$(O2Bn6b6x)? z6U{kQOjL@DJW|)3pwW^eNG=F?ObTps!OcG)Q(OQz=Y6n&D5uf+Dm|m&A~H4ZjIDkn4=u2(A6$8S1enO4-*-eQrD_ zKRqWynco7}%MiB}Ls6dFG3o`LaP$!nh%q-+^{ay0RA}cn|8|F%J$S=6@$L}9#6IS~$&=-Dz5DJ-^ zPxvZ?b2oI2ZFx^SyR(l>GCRYo*5TWSUKjl1F5I1}Am9TAgJMGpjpPQsZ0CqwymPo) zfueXBb18Arhwbxbde7VZ!#>KIXtOJ}#Ptj8e*;F{imt}s=Lj40Q*A5!cYqPKb5u2O zv@kHVHW4;4vNmuuaJI0sm9}tlRy1*Pv3C9sf~tQ`xHw`zowHP(F5QhiT`1Zn_;SWH zQqt6xA_?eVpgttvf{3KlzMM30W~(W>_D8qJa=+g*)ftM-ih9IHFNf_{=}!%8)tY}I zNN3WSxlg=z+>R%&H~9TNKznIvv_|u@0-`N=IXu(m23UE$Z4HNJ9yBT^wTsSl(+6O1g}>RaL2xQhrrK6Kpz}ME}8Z>@M?c zK>rxF`Fn*R)WA_MBmk=`!+^G|O}QM4;6_QjV>2r`lc9DLjaQ?6B^Ft(Kl6k&oWkRB zZ(J{9GxZcCvq!+n68VcVMAO`U2Lnyv&96_U#ydYtEWh1R)wNk2Z?e5Sw4-%E4gG#9kWOZS^d$rCNE@!qi z?e>|qhgbzwigt9i%`YR*T>b+%or`|N6kdLYyXI(LxYf}@{R5N^o5ypFTzpS?eTwYj z+*SK0$83ZjjU61fyXbHaoR>O%!?|G}8jCPUIK-WUdG+15gD{4xf^50k`mJ6daHs)tN!#FWMsJYJco`>S<_1Rn{gjwJLsBt<9Be@x@Sw`XYpV11;oHh>^)~%)tl895IPi;Rr$%CWpzfBk7pRf%t}!p)7GIv*E!5B~81(1A0aE zh~E$yh@+zne%m>_`2RAj0~s!7VO0MIX3YcEiG8si>l}@weh%NDsdlasmK|eUvM~zU z@hX~PyiS4135V&S&DOz<88rwe%5_*0M`0{?@kLW|o7Jtr)g+xJD%|HDQa^vgKycBl zH~WOj)YCsdeaP7G*fmui+AwfREc-QRfuuAn51H#YSsJxZ$wy4?9Wh8A9$rT%nFe=- zHU~+wZoBZw1o-~HTHTLjitSoIcy{^0GtYmA=l@*l{eR@+vQ-q6P}R`>**0X78U$E@ zRYN6fRv--~SNsVtSr2Pq^Jf+?J+y&VQ{UX2x};lwE@t+*sXetK(1aW;SJER)}awA%0T??v{z14OuV>h8xx6i%a>iAD z+ubmCbXSWjB2D|%kZ5CS{n(6NgK{&~dGXe7j6yGX^abNfgHlOkYLdvwp!6}|Hq)y8 z<_pk2Gskv3_C*+WA@R=M>0#oY0SmM8rDF{5=nC=c8fasxZ2|(dbQF5Y6(WIb)cZKd zj68TBe)r~LWWG8aOZMay;f7k0EG}N<)O(nK8;6{4Q_SI5L-i)cdHeTxinO8n++p_=mRl3G9kEBs08-%xxL+HejH zsDr`&u4sYHCI>JN9I>VOSe^m*4WHGvV)-C-efxFM=$7Mo>osD@G*oxT1qVt$C1G@J zoYuSMf|^zJL6m{VP#IeIE5FByi;aR5hR2II0xp#=kM|wco*cms($mS2#S;q%-!1at zk8Jqv3*TOz+Va3en*-Hu8+p04^Une;-5Qz&WUea=6@Ba`EEjkeBGrQ=S?&VHA8Suf z=+tc|zME;tdi~&AH;D9b{rHEH81@b@>eqkrxryJ%cXTvAr<2}DaQcv$llbZRy157FK6(ydu zm!yQGm!uSSZo*Dt37D)_R$vxL;cFG?v6-AE(3(L5y7(d zvG6fg$91_mpO_VwNaP*n)<<1#-ih#Ws?geBWt2}+z#BE%o1W&iU}Jy&Cd%|~Rf<+W z=5X^S3M*@V-C_1Xk!@Y@!(sa12Q=>Z#HlHSbf6Q6W1zj{andlfr$A&{|H-;83DTp+ zV*)?OQ>ev0OvgX)|DA-({f1F(zEg7^bnAZ`g3n`qeIl_Ycz+g7dg*NqRcUs|GGMD&jvNzofft zk?6ZT6h!Wu#+$AgF|wP~+FU-+)d5pb6`qO%;N`EoRRTH7)v`TGA$v+&OjpfyZDUW8 z#XPrkO52b1GHELEsC^MKndfs)A>2ukK^XdlXK?)5`J=knU-Ssv-|3=W6&G)#dNE)EDSu`qVMl*I2}Dr zz~NwAqyszu<0$PtkhT?j1bOtsm67R*qtxVM)fiE04ft&L3Ac*fyeAEiDdJyDNHvL0B*VMh{7r7~ zX8#DP@yQELCC{7wvNExmX9yb}Z3y>Z>Hfd;ge@(O0^%WnfTEFrfK>mxzeCl;(ZbY2 z*u=@%(Z$Hw#nHmnOy0oxe?$Xi?Tk%SEu1Wz?HvD4ou+o-gsOt}z2)rHDwBc&Dq^Tm zQP2b(2g0`jlG0+BPumce8+fz(NlIn zjyEC~bHLyG%H@_wre!R?|4Qg}v+Xtgmi@tflGFJ1UVA_eq#ey}>44ngQIEFq7qcG$ zi`|28&-AXX7@{>|NQZ9|U~t!_12_*sB-Ry1Mx}Nj(991XF^3cuUB*5}Vj;T3Hbp#k0rS zh8cR)*qi=69)_9PDv6;qp`6MjoW@z6@ALLhgo83_vlXSkpGv2iu9g+LgCoz|=*{29-&B=S2^R8uJG%u}Q^RXVK#np%AvsJ`mQH6xj z)KrJj8z{=R=#N9YCauaJganC6Za5-SBzSbA_Er<4YEDior~sl1+72Sw&lKtL1XI}t zJP`i*OTd)yCNkG|{CK;8k3f>Ljto|e(@fsiDX=R$m006P>~rFaYM&Q-*jq3!a=vav zzE?^P*yRT4X3|lGQqP)TQd9CKSK=Y7;aSu+`G!^QMw2a=N_XmFsEwskYgLjhQYPn@ zJ0H-J8jjj zsPp-s7Iz`QsYt=qpPI@UvAPB(6{@CGT-82CHTi zCj?p3SgIQPF4dAX6|~fjHKXrwN?hkJ4Z^zb2G^W)d}nm)X%Ev)lSFfzU8R}2QE7Cl=K=SC#dKO2 zo(r9?N=e}pzD$Y*hHG>JUfiWC{|*#DgKjKOUkxi+TE`IizI-lU#}z3eK$$f3#=ZUi z@wZ}tu%YLb!Gnr(WJml2=o^XY8w&mtitV=WbeF&=jX)@iV1(lv%e+LL=ti(2MKO4-wL#QtRADYeiG&dZ3eBe0Q$+aQ%-soYnM90jxP zb$L65#SOTQN5BZ21}FO-{$(xK$By{cRc~H10PRfPaeu6g9nx>O&-hAuKDb+VQP46D z3_W|-*F$RA6O`*03-sLw-qr@xhPl80`|1?{C(9_<92fu}Q)OSMe@JRU5sDl6XU0eh z6zpeS2MP-4f2I_T@wIr7TY!MtbAW*u{;MSTpY%5A1MRK6wDj6;=SNS^4Gk9w0fhnr z1`ZAf6^!&d4m=SkPzpBzI!fBq&=h1}vAKC!Q>%H2%5teA&T@%J@{fbf*7~{Gg`!RM zx@Pmz&ujY8@2rm$4`!@1tlpdNJ7Tx%&KtUup9bIB^}X-Q(_ciOUO1yjy5+HNqdMF- z3z&cUVVw5_DsNbz#%~&UN;yhLC-AdXq5KNBfmA)_`sMsM`s052NvUNCJ&Kn{L_lw> z5nnEhPEA(9IvXALiN^9p zz_TYIkBh5diS&eG)`@c}W6UUy!Pa3kbB{XY+;w?g@ZA)S4wpud3y3uuG9L&d+;(lSmu3bV5D z^a{rm;2y`8id4gQna_zc*>h&AR+gWeXv_ zpHhB7d2UgaTMc+s`6V@d8>QTivrM^zV!xtbD+PB_u#JMdD7c$~?Gy}9u!Dk~6x>6> zy%g-C;64iOr!G98DG#d3Lz?n1vK!^sl>QqE9--h-%JCS*9;et7MIb886#Jc~{GO7Z zq>*@vQvQbm;#d^ z0E03BN_mBXS9M{m@;5@x-zne-d`(sUVXzu{s8MbpN_yR3Bb7Jk-3=7HNwxlyV*gT= zw+uE8=Q*alO~Jn@`5lAJRNh4fpj=I{|4{Irro68z$o8fv|ILv!vu&K%? zl=3OXKBKhHRpkpR^CbmeQLvkWujw5!znLm^KxO!-3aTn1oT`Lys{e+a1QldqrT5nb5zyLVU21oWz3_>@(~eg0R@HhSVTdwrVb$rUaqMnnp&!< zLsb>o;cC@KZ--Mbf`XCA7}QakdWa@%)zm`?W6KGQG73i1%P|y;HPmu-9L`7eFij<< zs8*;dvcwQapIS*N6E$@b{Z1|dNhL<8A|ni`(X<(Y>BuGUc6d}^bX(iTuqN5Mi(T|~c&DX6Dl3HDcAs;UhJ`=Q#1 z1gJKtYO}#EQJ2x{W2urBinUU#O;wi@!$bP`sk%Z{R~l@y{H&%Pr>d(c(`w{E>hTmi zK}Qfyq~Ih?B}S*7tf_uvEhHjB>KaY$(9}+f2B_jLOohf}si$ZvaWyrhsomIu zQl+Y4`kidBYt#t!wFjY4iNUG8sv0%eJ!*`q=|je$ZqQWZZm+1P(ZeQ9JzZ7LFxbQD zne=>Cj&!OrNmIY2s%IPQF-kmFJx5c|Rn>1B?03p-RLys&;`1o>T?)=e#-x5vQ!h}} z?;GqH^+Hwsfx(`mj$cIYf2gWIGT4ji#j1LV!TzFNO2GYCQ-7kWmk}VBQ^P-1)hi74 zclAn5-Aq`zN>y>LyrEu0HT+CduPwq%*VDt#RTXF5zttP*;U;8!>dl(EMOFI^_P+WH zRlUVv|FuTvRtkQpsl?FK+bOt%K>Za3Td6cLG)O$4bUdpkHg8Qh7`!)3e%Jd**|CnMA(Oi0%$nMvg`Wu>}%jw-71GcHBYU(5O z=26N>Y)*Zgf+tkc6O-w?rsF#(=T6HI+D+`i`o;YY21H{}8_4qm=h)GJK$`|5ZQK)sNJVHT4rp z{*+Qa)6~!D_X|z^k`lk7U^fL{Q>BQHb2UJyHAXQ((?nI1453w%DNsl(EyWmeOF&+O z97fYf_}@-*3yC~O(V{H^H$Em8VpkO6E9!CLjI&C%1D2+IsMoOJ_A_XUD8gV*}R65O1=vYHN z>rk~$L%3fH80;?Pe0tY~IM>!9rD^LZ7Syy;G;KY>5~84+f-nUU3VJl{R8{LWgeSEq zm5xz|`lvS>s5cvN3Tvks+9vIERXf8FoLQ4OEs$E3sKcwtGQniaU?GjDCT+=S4)E{fwPc-c^ zP2&xc4yRpV2%l(IQf^Y;w5wF@YD&3=Qhr9kwVHMvDIPaa^7Weba~iK32&5aS?VITF zW_q_p)B5T67n*hpy}nfhl<1;BS}SeFp$U@&dpg?ji%PO%0QkrNA? zMqrqofi6EHzscWC177HXWI^E6z5r$!mz*}+GX~9TP6>2aQ0QT*qsQNYJvw~fz;qyC ztPO;xG&v=(xEP8c600*C3(UI~k&f`J>08@?4}o5ygHSLWs1J8VS^^z`;D$i2s%<0W zC#5n-gJ2t14S~}Fz2(WJsh!>c&(i5k1O|1P!Htzfx_g2lngkX-IaMt1$NX4zaXC%B zUQRa)f>FxX$v~MM3w0@5y^a2`pRhk+1p}!T#}%hB zp{%K|drhFXjXFTHHqzk_IkVqRlw#|GNOF_1pj$+c66GK=n4DFQuwa0|ji#L_DSL-w z-WZ6ji*zC!NVAv~EBe#_*}+)7|Fek3Q9)K7>_CS6!vEbK)*o0G4Pq6|U0u-tc9Yek zz(AJTvA)sY!>Lo%?oze8Rc*Vf4IpP*8;DtDk#$r|I1myD0wH!d*1ts6cA$R7vo=OL`-q_~ zNZEy~dth|)`Yib7ryA&NYaC|vDAL>Q54jAK6dhDJtXFpa`H@ak`bshom(e(9G0`go@^eA|31PTHJnG-5#H|KfJKF zU=x?OY3B3b@ffnsAMOkVIAAVi;xJXDq*$6AD(Y!bWV{&b#o5V~C@mb=$RT1dC*3qm zhl)cxdIPA@?iJ^%wiB_wG)Qyn&NOT!Vc?f!g`09}VOD*zw!R;oOD3509P~7*M@ndL zLiTu+4k5aRfp5^HoZlA=b#gwRhq1B<0xX;n;&uflH&i$|Mq}n z2jGE^UXewe-c-hvG`&t+Z+c9`dy!0$`-Q~J*h}{Ef{QSVIkRd1AF+}hS^Qr@AqxFy zEYcgW+3DQAtlm70)@epj3}3r5*3)G zYWE<24Mr2(hYeKal=L|F_7{;}zG1K@GLCNBC&{2DtBOg8d7?=7J?1otF9IHr^iSUOyj*0Zub03Y3j^a2$c|bfW z3)=Pqo96=CidaSz<#874p%~Xrv`y#SNT{=wJF*(NnKUzNb-~sYRPA0>+l3RMVs-uM z)if@-J+^ppWTNwIAuz{zd0EBtdR{h~KzVXS zoK+R{B&CwVe;}3O?C1)Uh@-_C&pHQ*$qbkgU*mCR9IpdS64WEo7Q}3JF`eWEU$#J$ z@+#sUXD7wWfYZ~h7bsK-JR@N6+MLP2A1u#^6>swgaa@nDsLw`k%@Rumfzyy&X<5xx zYSNqhPF}kXd4r{eD9B}Fge4_vN5u7kEL|DpNy~IJkPe7A?L7>+9?M(a$HERz85^6L zh3H@h_ppYMCGv$r)0>q9EIlqp(H`Rth@1{u*&_i@nRC6Fqps79Px6}HUjHV0xh)ra z*%qk}cLq*t^!LyjMLZuS5K$2?wIQoZRZDt)k$`jJ>nA6+azIE z3x@S%F?@|2)_pN@&#FPE@2*g=;23J_|DjN1W1!PoO|sskvze3hz15Zs()=B0oJIL0 zaPea~RdcMSY&g=%kQ63$vbtMnRcJ|5yV_zdf9s+h{+>W566nTAZztun{YxGeIyEyP z7FU0{hK&_jis{e|sdo)9I49eRSEtif6Q^Lh9Sg))kgB*-%~m?Z4J6DAzsJf`*960z zoHgbr**U1^5l!NNS^8gdZzpkN>bgx=gan(SH@I!+U6EdA%S7Wr?oV+rI^P194|A|9 zNDvjS8JsqEzg7mkF8MsWR@XAC%D|V9V0;cA1XpvfTid+-0x+Te(J3s-`AI-G?1wqY z1%jil(n4FbuiIXjEDE}PAr834PrjQ``z*#_$-dra>(1SD&^nZ*pB8(c7wGPZZSwGr zytolzO^N(?f;}^Gf?b{hO0dhys0O?23{osW+(PQ%LPP4}Dw~Ou$Wu(~axEUU%Q1vB zD^D+3Uy(kpEzwJB)g_IQp2|np5Q+5Iw4dI@UlIw1m-PlZ0~VK0pi8AY(us6QoZjC{ zQ`zE&D0^^GV zyfVRXpSK;;Nwb#~okyo-?wquW@5jPkgHw8zTNM}X2GS4e+*ZALYfu|n?}16@3AWkI z<2E$&_QL~A#fi6bC{*nMoGiToWLjQ>(y&r&JJouyo6O%741_wpcGHwh?70NOl)|WE zV=KHWyWU*>P!q)>!6-7b;&5Q&a#X}oat1nWR|vJWBm-BH*OMj3tooHMTLaA97>up+ z&S`pfNC~4(Kco22;mw%W;irXAkNb-fTvZa|*6?Mbv|4$&(#o5$F>O6Kv}!%sj(Izy z)kHR?xjpW!ZgEml?frZN2%?B!&&a2nP|579Jtc0xxi^cGznAEEg5CMN*t*`xM(-Tp z3HBV|amT9?>-CBPGuV|hMubD|JG9~!1~V}B{qe3^CGEy}-1U3K7sjm@_Hd~PmDOra zJejsma!oK4hd65GwV+B*+HEdjQz=^jVa$Ptud-RmM`t}g)(Q*_P9b+6t_^sFpXsF8 zoY8r-n=b6>4R+(aOTa#@SS_1EHXeuyy`uD=)&}r0iOo zeSEgP%Z>1Dxo-C|QLL?j=tWcJzqicbaRWam7K0z;91hsG8KnbOxTO3Tni(xRwkLR71Q3>l$o;^ zx)D^Ux`U_N)sL@83BrWr%}!`mgm{7BDWPa<lAt)E((V^`O}DFD31XZK+7czya>5;!N6|5RO9W zCJ={n;ugHJzc=85e!Tue0BEqgn}K`{!ORK@q@Fbkl2Lovy;1f zF!ym~gqu;_{!MEFD|`Jtq}bPomnHFzi_-HiOTj*%ZCe>{qZ&awB`h3& z#jbS+x`j3KnQVLGEYXmjf?C|%v$#_w8gmvu3&|F9Pss#2LbN*Sh_U)kizuQ2%Wold z`eT0bJ>rvNzf#<(EDwpsbMRNy9z?Apx<1&GcuXV1X|oSJt)@exnCiI2dc3EN)11mp z(g5QnE$bEY)wH%TI3hhs3=#)J{hgg&T*lAw9(b4Oj8rGqNxRK({1)(NUT>f)fC^kk zfbXATa6xvC`h&Y$u*Eut<&asx-y7Ow!zKCoLVkWW$XUX9-;tXm*A`gOm{kSO z)lXM<|FIWaG>hp-u`j~|SexB1Z`WPtIi5~{bQ{>!_rK|*wMM8Kq;tXeU ztj3F?B#6_USxBO`6dIJ+(Mj9UI%oq$=)h{rR?8S-@6AaV3EI||6%-k)UwOAblyJ9X z6JNV}b6T5Q;(9_bbzEBBo99#!j_VLIsBv ztu|!S;Ye%WnrJK->*MDv?JDR(1y31!bv)!$JR+&7ngjlZaFR6Y z^>4IPwJ2Y~MVLC{&raV7^csuft9|;hS~N&l`l9tQ{%CP(x`; z#J;n{GC8$VCAFtz1-bBeLC1D*@x#~4_9&VRqzW6y6q#+?w)UZo5p@D zpIWD<6VE3{xWysg%D_(<+1_C*S6vwKL~E_3)^niKHf`rSyT5X6`@5dM zN6go|1+lRs(_+B_wA@pQ+lAgra-GxU_D08rSrb}{Mu_0@$--}?n_3*`XH;as?oxux zkG$J{F~763TJY2C4ABYICZ{PRL29F*HATfZ8bDFgOR>y9r??PQ(Sv=0MzFNm-~$^fE|<@)}u{A z)Q z{+^^NatYYh_%`d|YXdR6(WXeaCQ-$$*`HJUNr$;OcG~zwKD_Owz{WPd;fc1Ah8H=z z`s0mG3;Da(box`B)!U0-r+gZNaH^AM2lwz~(rHo~Oz#$y@~I5U9Pm@`qb)yztdsgB z_33Sg#P&?P-f&=J`QfOvRn+qXU}mp<>y)M44e>t4*}OKpBV=C?Wms40EDVx#w6AZg zBGjR+X{%CnBO5|q6AnjWj$dN{%%uyP5TFf37Hh*H=vo?>x7%1iPsRm-Y~T{wSSu8d;DtCX#YjR9$$uiFtrc0kI>dD>qcVvo45>_MT(n8 zOmTy_(UiXm|s&rI!e?Td8%@y<6>`%?RgD%)*p zUyJ9e+8$E}xz`lW6wk(C)0rurE}r3ll&Gtk;-O-hDLyJbX3Ep$8K(Fn@nQz!GV=GD zx}b}uF5%egGPO5NS4>^S_H<1eXNnh!KQQGZ<)chpm-A4#XEYjkD>s27>?a>SrsOkfPr{>q?w{}-)H=}d7Ab)Lc}0rhbTcuBjiYmudQFq$*6o zk$cwE$57cx+4y$RIyfL{NBtaY>gC$2raq2>!}Rf{UZGDg^-6uBsZXLwJx!l%>Q#tf z=}?>z`V@+tMHA$5@yDikDFu&XmS>q#nKYU~Wjc&l8*}VUiD~By z?IPfP*JmM^;xEP9O!0Q{4pW~^bajl}mO+d0buR`-@3%vfP&$16}q z=AfycB=>rDb(FcA8ce;N$oOR4Z|ZB1rHh-yjTs=_J>*Up?VzC3ly}GjrXHY5F2RW} zl_N)&=2NVWf~6FkM8O&gx+v(TpqGMgQE)B=mr`&U1zU)HbcwfE<{DCSptH)lU!WkF z(R!|(E%TUirg)mZmSn*?Q~Ff;%oMLC791pwv`SiS>ZgdeXgW%z5H(_*&rK};hWMtb zcN2!fG;9$HdZ><5bwpj1D!N!)$zVoCl4|fxel%ng(#__C>4Yi+nii(?jD)(?-$c}Y z#3((#h|hYA02(13ZAyQYUN*%)l135}`%Jx$81uiyHyMmiH?;N{b5q}7G4qY4_`djo zsh>u5zeYq~n7FN_(cfe0n+P+fQ^r{u~|W__y`%nEHA6h?&2efxz_cip23jZ0hHW%T4`z z6kMQx-&FphUufz-z?h^i7u-mu0|%Jex>O{c_Sjeo6!NjP$%IekyJ;B_9&FG*TKxRP;QKmU|N2J6yB|XzyrE zOxAmPBe96Jv@|rzPqSCKNmbT1!wf`#bwQEKU$JjjkfMNmV2m`{l*WpiP5nyk&vYjV z_eA7atg_1DZ<~qMuA<;-9fO}~e^zxw?REO~rv7t%CYpZhthwAYs=UW)hdEjJcz9kB%iF@Q)1>PmFs!mqiB@%yQrS;b z_Y1B|5D2$eTE(rVc!PK&#eQk(w_%!oJ9Q#RL$FP{%arbwcJZ4SY*uVnX-Y>S+NJ5z z3{#p(ljROliLy};rc#@~GWD&b&!!hWbUf0TM|Yayc4~7Qsb-gpKQ;BcXfhp^R;%lB zYPdU{Mz*h0j6{t7j&&Ju)rGdWl;;q*+ljSqmzS7Q70y3>K-^;O{Hmhbs_39rq%T$# z44d*|xt^}3>N52mlxbMEKfI|5DJqI@X)x(06rroD1kA4QL|a6^hfpxo^RmjbbIR23 zrHZD|YfPUK=_;eQW$`nmWgULh8X_??w#&kOp-|bn0Nv1@k0mBk?wGR%%~Q~nby7@e zrF0x$N{dy6_+=Z35T+g4gxSB#)bHc=wthca9Qp$ply^@*QmF?`{UH)c59`0?+coUd zc2)c!mZ|@S_~0Y@qsZ=pp-^D0KUC$MhP0K8w1T46&hNDNn5jRmKSAf0?F#v6M!UAO z!1J?<7K1-h|1BLU2ydWWiB;AHsSH&9_}(Z}{~dAo)1@;^{r7}~CnEhE2W@SBFT0MvRMz|Ygt86Db(T8>X&(!~rp(KpDIR9e?c!{@9C7z++ zStN-#B%CDn#|z+|cqLA{%+#OLpU+^Vy`}U&d08sSX@9uW)L$U2DQ!h`3p&zmrVVy^g6ws;`boyWmA8JNat0OWN~prSx}%?+2{Q-;D7ef zKC1I?8BDDO_VGK5XYL}ez*XXAI=JdmZ=@sU$9epBl1#TtcMy5MM!`Q!{dEHQ4QX5k zT1VWp7R~MDI#d9ry1;3Skm%()LI2`o%^$$?NYj5XmwYQu4f3Y%Imkmw-P}M&&^^eIv^$D8NVy`?l zE9Nlh$O_TMr<(qmb=f)=acS7pKPO}T3ktr}zcOW2)=cRw>e9Q?e@yAW(ubyaC8{!5 z!)^+`Hsw=g6m@%u;+2@a8dDk$Y^<^#xb$?|mG6`9Li0QR*1BTB8W1bRl-|JB3_yWo zFs{`(+?ikKV@kJ>VU(70&38DLAsC`*NQP|6MW|mHilM27ZW;#t=9q?QS~=D`~eWZ%2dOD8>;rhLGm` zI^x$Tq2BrImbsfCfu;sof2{$&Nb#6ylqtY}c8k0<8G7^YUMI$(cg2Rm?_?9>OK<)A4I!`o>sm3(ZIFe|5v^*wX zHI72oX-qeb83e~n)Y^=rDX2y@%a|peVaikF!x>E9ziqmvF`L|?(~M(GV~+N$Y0O31 zHs%>MrZJydsU+$y(`2EGuDq+{k(oASxB-;`I07n?@AzREOC*2U~>m}vwuRuPc9 zOtGK1^-5!nDc($MVVDay-(XqgIdwGDL0Qrp)99dXcTy0bpo@aFgoUy`f`D(8Blcu( zfvWOeU7D5_k~U5e%S5hvmi#RmvvsBsw1(gm(^#*4q#7a9=r+Rpqx!{n7Mb$-M6eNk zqG|LPr<(Hj@KL^=(t4?HQ6pv=eKfvobI!oexm3N^8H0J>F3Q`CF*= zCR@!_LC;|$i8jP|y`1!H8fP2ln8vw;oNpW7F^%(#@0#-MgqKV^4Hbtv7AgjAm$VlXqS5%GM1Ct-=3+b-5n?Hh<- zifiZ@OwMZeJCbhVt|-)_2k#Td8JKXr0>XQIs2Yuw{;jHZ_bY>jahXBii8Ox7udB8u zPWlf?B@9c3AH>+}*YENd)VJ2mZ>U?)-q5^gQC*Amm4RC8O3vbh6QVqERyux^^uZ|x zjTM9HIG|5oKLLClC1kK8dEZ zVO(LLo};red{s7{jYmzn+rK_g%TH^NL()SUEm_U{R3x8*$E)oz{>`zBKeA|jqHI>C z0=UhtJbbRlpgqWzO`#kn`oLOmV zZfUGRBe^8buWBQqP{8ri>HN-3OEE}yQ%frOp2?Z#y_Q2_qmK*nuNlxs59%#x;#}eH z{niYft)L5V27R52Mx=+)GWd2#nwqC9?I3HL`%1RB)EVmr>sCm(LmvS4*avREW z2g-5_ljR=mPB^}v4&e57l0Vu$QQh2I*W6$c2i++f-vu7uh)tjPa6asz+PV$)%i2;N zKS#80W)c7Fn6Ig>wGDLwXHvAc)-9`PscCC&;b($_C?KsC4$G)w+FRV%ykL1l9S!TL zeg07L=Xo5#vD&_WqU@M5#;@<(*|s(gn^pNurFc z%u?bmNqal}U_2k#peEn0lZdtb7{W|H3^o|=nWCASAWFmhmYQ0c5iNE^s#`&> zwIbbBktx(rrKYvDei2IK;&y&-LA&KFb+|V2JTwYuvt`!bms-J(gnv`lGEnuWW~a(! zx2tq77yB!;tBJnFbYgmJY_NPgQ#^j23@j$+*xQ1-+GQW4k<>iZEm7#Y;svy!XZt$wgEXErnmFmZ|(kQJOA*6xS|HdfLyq-*O}I3K2mosKDJsseDUH?}o%vP2SFO#tkXN@Z3$^0Wc=}1%-hq@hd(z#h z<16Yj>=A+GfM_7{yW1kI9G%yEgS3)ecapV3Zt)i&-1UjNRS{20x+y5X6@^^Qg7%u0 zMavuc9L%*o`4)4&SyR4uI`bZCug#XFr38CdmY3KZ)Vv39tFYx}$tQu4aM`cYfHq|v za-fM78GnnHtSOXO>uR|{SQLyd?2GmF1}v^)%qT}tR;ZF6H|O-ucv#cllR zSdZ7|%>M;zVIQ9lj5=#Uc5soh_sif8peRRPFdF}Y zb|+W%Q+i>^fs6J|kT9_Nn*=vLuJX#o_2eVeyAb~k)>h_a+4H1*?r)yuIN{un`VckM zx}Oi?toNX_7;(Q3HH=eHN==nuao5xtv>lwb6Tcor5-Ph7JD1xY9D|FbcGSmP_(BPJ zp;t85FW}#l-&oHVu4n?*CN5Z5_Cp4$XW~~lR((U9g;R5F4Kc6d4z>;zZy|J`N?zKs z?ss`CGZ{r>`s>OP@0gK#0)h2p81MfI&)n3soF(_Bnb&N85^`F$h_hB`YirtS7n8Ph z6}Q&2+A^_kO-ZO<$l&~gPK?=;74N9Ju)e9Lp|zgKI?-r}PmSeF!YEj9k;w(!A2TPzw#J1xS81of|Ahqt!Ju)GuqTZ>3X{9saNj zUF0C&LA&pLGqg@+y4GP!4Gxah;<{04Q%KZIzBiDF2F3Dp@UM6-qeBac){~`PC#5<5 zIlddy{;G$CMcea`?o#c+ifk&wztm|v87LV6poO~(w>k#H>#;E^O7>g_Ee?h|xMim- zs%vYbMUV{K9s6y@T6`pGfvmLJE$WNvn(FMePE;Zjyqv{@KITAGocIkI=2U-ew)Ll8 z9avV^Qs2CQ-{IS6A7w6~FN#fXn`Ic?q)s;0ZO?(YR zrL#NSxLSOWU_;^_Ny`p{K3w9OahkJ{z#{ozcTXsu%W~yuj@yWT0xR)y{K`x}8A6~8 zscC9%itn6o*2x6MlHA9GzjL;)1&Q&gF3n55)q?H=sEJt?Ty4Tu>d{v z_F9`%QA^ToNb>7t{B4fMi-&dCE*`$!-s3pUY3FjJJrbr9m8Jg9&J>#*7_83fTjp6Z zO}*~ecE_`(B4cl=X+bHy^29_f?TJ3`QRo@_Cce_gKU-&A(dvBnHognY{;rW3O1MT+si?PZog_su zdDjr>HQudwCA8R-Vq^Z|vZC=){U(6%)L@}kE9UaJ|IUe z&ZPCYos$v8bhtIg+6&%bZPwlS4eS@Sw)XDN*3ztY4x4Ke*20N%BNLvGo#`H41A+WX8rCoap~ zTb5NfmgXkZ)D_I4g^&z1?o>rnZfC7ofLgb>tQn0~v8fJKpwftSI|9G{la)o>Z($h0 zXtVoJ7>qUqx?(jb9Xf)3+E0P1Ny09Zs6{xQWBTe^XaDWB-|UjY0g9p(e5F10$pMwt zN%(TB)>hZJtf8i@4tawUZC{K=H&UGY+*rS0K|?~0wwn3|@2L>aaAZk;#b}1MyY)8D zUs6Zv#M=F(j;y{ez*nm6eF-h`i~RHAIBWFt6Cd5duzklcZK|<2qO$1{}cN zpU5S0edDs`mNtuV+mgnqY3}~`4->BDFF<9YG4XEneCw2}=Q93u`~MU4!%RNdk@5z6 zg_h~)dTrm{?A6uw*XI}Vv#o2gf@(j3%0r+Mz9=vw)4@xqhz9aU2UW{H2ne?i2fJJP z56CAKw|eT1v*ughMZC_E-q8itj#?OrF zP5C$SBdYOpx~}8~Q+`axK#5mP3JPxs`LT+x7<+Om^~Q{?cl4-(@EZeoL28wg&l^ z2FnM%w1BnF>MeBeapgDP>&y(_skBcLqK&-~U*-*e+ zo@=+<4Jx*6w_GBLK6wX-$u0i@TmB<&d5)Dila_gG9(LA(mCzw`Xi8)+G~zt`tF)o2 zv7llzK}NU$)>aZwB?yyLxeMe~J3!eEDn>Po>f6DnoVXow@Y!57aR736K;Cx9uS5hE zY=^?iyPij)Z@}Y@@4#|+54zxej+4a}s4h;{x;R?q;_j2{Ei9&AEYS&k~!sp3LaT*R0vE(Va6V&WdGImJtXCoSd(_9=r9Jl{of zy|@HJ8u}?%6G15>qDr>9L$2_|k9gNiD}&uNCN32lY`QrHyG0$-CsqzX=>QB(uHbWC zL8+C=#f>gDicMG%fhjYwk%QDMF0*T`$HG*A#?GkJMI6*&_kd4;B#LAL6fn`#LZRD2 zp?EBx$aV|I@)laeR=b5cHrxtP&hX@FO)Qe@saA2TRrvgMt38}o+a@lzaXAOak$OF( zavO{ofRQ40VAKE{A`ZZzNtj{7J*XXGHz3f5E9|-mwe&8hQrHe3d2fkX3-$!+zpei0#(B5M1*uQ zCJlege%=99+hGc(IDdyrC$HWPM{I|wRKwxhVHy@WvT|aHGyq3cYn2o41?*zAPB6~1 zDxZNlW+J$yN?-tvj)Uvd3Epai*Lt}VW)a-83GRL{DWgvxfMa~xR;<{k4ZvJhtvf9k znY3V}Z^57z^sUu7PQ_*>6`ScRHmTyA2a!!22{*x)NM88MPQ=muk}34dPGs%;*o=P16Qzla5bBcbu5P4Sv_oHOW-lK6y9Tv@EN9m z$!cLYTfjuN5bIye@>x9_!j`ZKwv-*q8rf=0KM~W{VR|>FpNr|YV)|XInNwg7vd!CJ zskjQKo{rMfiW#sQP8L^-$AiY!*=Z(Gqcf{8b^=XS9y<}4p}=D&VNB$)c8p0_PUGZ_^ zW3<+mNb|9C)PcOpiH_75fSP2X(dUs$c`hRIEJQd`sf$;=&XpwpM9qW>G^ZN=eGmQ) zm`?*vW4aA$QH(8E*$;XH4e;DMIqK_B@o|QA3}^`Bxj3xfhMDX;hi2^O^VNCBz%?wvKN{5Xz>?H(hIfm^xZIRHuZcP)Hx%v6BgF&f<>#!%67tHOT_PhdQRwN+hECvov?HO8mdW^Qho9O zG_J%7nyM9SxEWzswjGYGL?pIU(#uxTLXSl)bO73X3eJ=jD|f;2Rb_TtQYXDxSx|5e z{T{aiR#mG`y{lK!a16llSW%fz!e*&;pE>|1*mCZ~q+$9Ld4)4fVHkmkxg3Y-r#MVk zz%gty)Uaz{8M_WT+4T@(KZkFz8{s^5Gm4EZa2e}|tJp8#I(94E%zg>CvD@K3b_YDk zeg%JGcf!l;F8BwUD(~R$2W%I7${v6(*nKEY?nl+~K|bUs;9wjB_ll?B>}06e_KWM0 zfeX+HKM_M>Hxhpgz9WXk2+sENAS(8Vry|Ke2@}O$F^cs20W$lT*oOr6Wt?yr^AOJL z{UV%C;rE<$1p2s_Jr%biT%@DdAo@;fob)(o7-?^u_%NJo5sZK0LvZo{tm#MP44)edyt8wBB^wk)f?-$w~IcMTyPpO-ofU*qVtx2GibHMx^`m7)$|}`20H>_%-}6Kq4`_g3v6nz%uOJ@&4!P_# zC}MAdkNp$Iv$tRhdk2nY@4^E15rlAXBJ3+9$K9|I`PZ2OgR=$6#gPuPEPPN8U2L6W zVTAcnjI zBP+K--&IhAW3=IM$k_!OvHWQd`cz7w*H}?L&GStIaC)`QGf(zuk3)q|+6HIb3}rsu z;!6W?Ce}k0`1E_>EPx&Gtp~;F23Bx3>i#8$dd_r%O6HUpg=K&byWreaWfOfmHBln_ zB%iVazFnQ;!wduPot3^ELW2|^MxhXNp%{vUA#kWr0@H+HuvqXxi!cIC7DmD)!XfYz z;ZV3rD1+;TF>tdm7H$^~gGYq%@T5@TDvXFGckrI(ynykT#UChkj(9F7v!^-tka_Ie zVi^vG4v&f75zj;ZPz3jj-$m}B!O0Newx13^!3lFdmN4KEG>c;RHb?xPJ8)FT1-1$~ z6y)77O@*)u=MMP_79q)e1^LW$92FrH*z57{9Q->4|GtfXmaFIc_;;cB1AE{VAaxg@ zX&0Qs1x*Pb;q!QaBBKn4@wr-n8H*j6sDm`d=!ioX2Jxa8m0)d;V5Aa94*X+V}yB7C(MVX!UC83B2+G( zn>eRsJa&3rmsY8ax15K&HPskl}X85HCTI;p#w_ z@CkmY_+y(D`fLYRJ5B5wE|sMbo3W*Ba1qJT0r(-;R@?d^59K$H`s4@gEI-05l(Z6` zRk0srl*-d(M_OM@v&9n8Bsy|nIE=-n#=p zBq$TwaTrg6iTGP3tbwUQ2M%Kw4&Pc=1xC=_zP5`$K|6>f!fNp{K72XQhTQB_9KNM6 zgcH5Y3407EyP;H4VSNMI42IFb$Zwp?YjLz$BDDm>f;}(azALuZpvFEAQsGh&+U5nebgClIiZ&pObBeA`!#cKvR8&S``Es3VCPT_16=tHk| z;i-0E&C*YK8pJDrFCYQf4p*-Ocy#7OtK8ER5<$!{N zzZ0%auBtzwD#8tTQDV?{GVQ7s+fb3#blm`4zYTtVvu%vr=8l0?>z$rji|two+n8O2RoD=Dq$<^kB!&$IPk1=I287n?!B3E{)`Z`lGRm6HeHRwB5`l=YYW zP(&421A7})<_!x)?9{!ieSr<>0 z>{NUn;`rq5!rtADf7|hIfRDirai={i&qMJ=s^YJ=K^^HNJK@(mxwN$X4D-qVKsv^v zRij7an~CITP%3@O=xy+u64~}NSRMh@^)zT+Ps2#=X;9;y291;&&Gj^lCNWEiBxeiC z&Y>vxGw4pOt5^D{h0p*z;yN7E(LRMc91sdpP)~@f22D_m=spUD;nN}FOqh&xFddO` zH2%&J=c3r1hbrQHScUq@Nr>D|u?~XbB2?HH!v?V)E)$o)wc=9f7aQRYvDu@#{@SIf zUpu~HjJ0#8-!Qn98w(yo8e9fd;=QQE>JAy6 zU^`6(`UI>!Kr+$^>AOpDUOc)P>d;JnbO0V}ob)g}z8R)ZdI%nGLLz)(hEysMV^w@e znZLama_IMWTVM!{C1&_Ny?$^2p4`gf1{7Cb#8s$Fu7-THS4$Cu5#ot3T099V#FJqr z_NQK4>n^sAK#1=bAHY72#`h26TL~@j(c(i$x(eh&wfM03Yfi*VT}&+%e}ievRnM(D z9vMW^#v!3eWNXj}!3%>>)&0$#JRm8;+ev#mDR=rSBqcXi4cz)+o&xF`YQ~ z+0$i9*{i*owOX+sZ9%5b7FJ3nl87_l$KrHp)Mj|gvWB1H?-RioAY z=&BOcO7Iyw;P2HrmA;$-c+K`vmJPr^`oF%VvP8!Il5t{MQJ?wXok$MlY&aW*B$CV4 zvK}0d9@b00HkmyJN8nh+aLoEJWdoAiMwCTo!cg&BNIYjlIoi&XaV!rn|K*c)XU*Sv@@LbaF%#2oR2!y55*gBKyQMrDAMi~e}VS* zZSaJ6JG_lz=D*0;KNjzVuf=Ul7Vlz)csDB+x3l5muh}T^UUsOsi;WZSV-v*(*b(A` zY@YZKYZV`6tHejyTJZ_iE&i7E;O}YpdxrQsJ`D!Y#@~v9;&CKu1Kvip>It0S2Aq%V zgREJN6=VM>rsLc#5r2z}KtpBaWi0=DoFsK9rJqFBp|Ww<*QdyuWfO5q{0~khjm-m} z__X*3Fjy9Te`t38ZjSLwu2rRzc}*n4q$|P+_0PO0?ZDQH66=@y}nt zn6ID|BcG$8_&JQ8ufm*Jxr@o3b7rT-yB>Ev{xfWMmX3xNpLj`jwD={W?<M_dbv-o43o`COzr(e&Lu(3&1z}g{heGX1_rD5&qdPl;h98`h`*W^H#r5 zia&4n3;Fo-?|y;GzSA!-{CT&ZeSts!>1Q9}&wKss9sGH}pS^)UAM~?V@aMn%>;;s8 zAEFHGXHOxQI*im4+nqMNvgjk}e)xEmSoBHjPWaT4nV&saDUG-aG|Fgkw*mNk<(*jB z7n`Bk;*ei%fgCchzq*Q>jhM1~3rxf6%iSuiu3d32bvU7Nr_XGTRdUZ3zEm@LGgM#& zBiss#Sz}`DaF1|hWvPOkk>w7sygQ3oe#(~x z^m1e&oP*V_abXx@#R^?$7Nu+v6N{?#VpeQdjnN^fF&49u=|*LxPp2lt5(5!XO3h&Q zp{_>Ds!>Bgg$}Nf6u4ee;YLY^UrGktA(^mE%7yJxKHMV}z?a_6zK@2OGmK+X*w&Gs@YI!4jU`YW!2Jr)+E)kW~q+#N(}jc)JufX|FG?-!9jTRlD7CRqq?Ljy9VZNxRtY1d)xt>WL}9FS zk}yeX7p6)l3o|9ZP%Etw7E2vMo75>Rmjc4cQkM{r)(WRe>x5pZTew&X3ztg~;aaIj zxJf!yxK-*Ewn}}%W6}oUMQNk(XX$j|Wyx}~oCmqWB<_kR1zDI1^Td~Ejb50EZ1k_h zjD>4qmi_i7ILdx|D=OyJ+pRD`d>IvRU3d(wTg!2a+{an6-*jnKKvWbonfdLSQ=2$9w6x)v^ma&vC_9a7FfBf1D1=giT~h^p+m*j zQ3((niSZf-;SKRk8-%Bj*^|*#O%v&u$`Q_b=l~nhgb(Gr*vM5QCl4^xXxSkH>`?ld zEbm}t_A38tljBy`aFUqPg$US1P%QljhDsO1ROu3!DgD^Bt*4@t{ipaZZi`jB;8u%o z*^bSl#JBkn7sGh*-{L#A(mI}Ou{?WN_=+A4m;K0x?<=TKS&h@nw|FoA?e_8sgp6Dv zi@D)jVzbSg6TETQd*mqdIa6ZQ@(719Gl_hd18g+1oROn_@`wR8Cf;Yh^ev&+2VRB#a?W#e#OXF>#$!HddM?i@PS3MskcC}V z!Qa{KmgL4Y{gS)^HlaG-mp{NNH$$FJr`SZ4OGi><(p6AJd>o^bIV&%twJO7xU#<1C zqRL{1whVlXQmr(HWZEt^Wfd>R3+!NrR~PsUU%@@>2mu~qQ>zPog^yzun1m)Po3@i3 zc@H~Egq3cq@Bl^&2iWuhHsc{Sv$}}VkFMqbR96q7pIOx~!E zF3d%$nk&@MuWelX7iMGh1yH0Haftr{dD2Tr{(nXCe;N7F-;nfQgUQlAV7l}=%$44R zdg-6gB>fAHmEMNqrGLXo(!00k`w){pfKAeeaHjMT{80KBE|ETg%calY z7U^@iQ~Cns?w9b4^cB1y?S@yRJ@B3^GeK5Zfvm9-S!d&AgH4r9RwL)J#d1DtmJ8Sl zxrlYh#jH;r!p@OP*mvbpcB4F$?UaYH`(+<{NgmGLl@DR>%Vq2nc{JNCj}-*DTo@*g z7e>hy0-ChKZ2X;zzl-FFdx?4 z3C2p80RI+06+eRlR)B`c=i(Po2%YTv;+HtVb6_nyAGPp9&<;40oht6eSRUNM7KmSC z&H0cg93k$(npK!A6iI+-8cY{-iAe%7>$zxmh^Va?>=VSS%?@G%hoSiTp`;WE6 zhq3ppB|eP3%XiW#!UArlDBMgThoaSj;SkOSBtV@VU7puW;Q*9*dEG1(Z6vU^mQH*%Xyc-2;cikiC|oIL}5A zd_{Zd&H)tALr_E)@(o!j3Ie%5MnleCsZ=P#rQnGLs7qjK zweHKKAEO^~cf!(YodSceX_jaOWj>uefw?~AG0=QUKS~a(*U0Gz;> zh@dyby=H4*7|OUQpvY4pU!Dd-LXUhjY?Q0vba@t>EzgGY z;*TB8)#-?Ecau0c`J&CILBL2r0 zG*Mys5=`6;^Hkb3ac+*YW5OnQNw$byD&$08B9+=ie+6+1+5^f3m&DCC#WwP`*aB<1 z>MCA#f@|QRJy)V$1)uozhgjo1tcgK&&Zw(kH96vzTcw(BhNZq7v_ZN0Ft4&yDp5*| z0k*8#tn`_8vtvh4*dj^;td(f6RN?}W-Y^P>F)IU6;9jcEPpY~tgQ~5%<5i=6c^Fnd zhUzQk^$jgCaQ2)t-59x}i-|pM=Zold;yGj%V#t z@R~gXpV_ApU4FVq+GmJ7d!{I~&!ipqY|+`CBl_FgF7-Ns!8zzGbBN~%;Wf833|}jC zYLF+~T^Yx#y^mMM@e1}Wb#1@m=e%N)Y^v5sNQ{%sXgzpQAo|OcS|c4rXW5)eesmKh zGLLyi^c3wh&nWs61;XDADyZ%VzuKbu3byF!OIy%+8l4kgClopt%9MH^Cb!#bGy-9d zr&hzPXTO5md|6<$0%4`^1FoVuyCQ!j%2>G5z9a*MnK{fQ3Uz{^P)n-CM5a+yZTrt^ zAJw>a!-_|{6(=>SSg(>(JcxY}qU|c=@FAH6kBq=D~}vt zyn%9xH;c(^7}Zr7gVcyA#5p#SP1-jSUcV_zHknayTC&i6;gv>dJ|LrUK(8z*<^8H# z?Ra3IEfia;;eoNs0Xx31AnBN@x^4C2m&?4FeK#C?HG0^U=w&~E{`NyzO&O47Z_2KE z-7POQt4Ovq{N-5sAb&DcTtb}tvBfk;$2DTu?HXEw8xgcOWijj_cP>J%1zgCeOpS`_ z45KNvWUI{Qh^F)^c5}TM!pT$Y`b}X`Y5_rChD*LspVOi%6%Q|;h)@5muV{F}2%7*vRQ1gO4Xmjvj zwa&8Fk?N%tnmrvtjXHUSvhkf)Ev9?)@sy2XMn$O-IUZ8V#u(z%%8Zbg7vE|1i?30S zH1NEQi2V*)+V2uheGen-58d9hBnCgDXs&SkRgq;k6NFWZo<7fgdy!~h%OTsywx$#P zjF&*5xI|jj;`GJ_a`sQ~+du!Y&z)>XpU+r{$ad<@hsgFua*#tWVAF|Ziuo)m*}!H4 zsge!Yw-yOH>~AC`Q7XSPy4!~7_mwDFb|}Xu7nJ)5tV)zTIgsPC>70<6PtcOvEY6rz zBW9-kdLKQr5^-9EXDK5)K^S35Qp`#RXq;zNi?h>C%Grj&8A()mFekVI&2xO^w8=Wf zL`v8NIiV-SY@k}4Bhn#_3hu4M6vDJl%Aadji#e)!Il+}gzdlA69py$?oL?{Anwt*s z?KXUSlb9z!TzJ!Tn6HKFucg9Us?e9ZaX2@EA>mvD2^p!n?r#CzIZsR7Q_fZl=0!l2 zs>S(R_>K1(&OWpQx%z=9Z}Ki@&yS?T)Vd-jfafK}1!`royyHUiJ${v*6-U1zNL9r} z?86#PN9fTu{PwuMN28F6REvvKezqeZ?m*117MHLrPYs`<86v3=lL`ycQ6j(z9amwF zYGo6CPs8Sgw&Td+T(;n0Uc*U+s>MQT?g|tZD>bI%rc6|C4VTo2Me1#FY6D%qR39#> zLJl>OFFLAWe7Hn|!{W7UB4i{^`#)u%A?Ua)uB!<*74qptm)p1 zt(o5W)*SC5YrgkVYoYftYl(M>b*1-8>ss$s)(u{582A8jl53EZ9f%SNiA9*N@Jm=c zMO)Vx<`KkR!t=$RU5HwpiJK{*jX#VtnLnGm0Bb&_3WH~1CS zyB%#>A3$rG*O2c>pE#qI#kyxS-Os$muSA_O-Bv)qrc`b+#7KOVJDle}MZ@{nz3Nv* z$5|;YW|&BvH<2UscP}g}uc%cm4k7*|?CVE=Ytf9$dr*{Hi*kBqFTz$lR7WlIH+(Jo z*=cf+u_g2EvXjD^hq)$+6^0IJ+E`omhX#D!8?tDLrVVHbPu$a2(5uU3cZ2H38CoLD zGUtloQre&@OY3cI6fxnw8Mb#hg5F!QcFkh_@0=yz8=dx|G}Pl|M?_U2I{S*UX0S`v3=*)E zQVZX3Kfl#j*Z0&;bK1mS+?bXrxt&copLfO0Y~W%m_qQ)p90p~3FD|QZMKQ}ye5J5` zWz>>GX%%-tj;|})`MOc74nrqjIlB3JqK7Z-DpcATlMQ7xXa?CvH#tB_UL16i1C`_@ zh7QWaPiE+?1L!0qp#U0ZIVjT`F@_$|VJEVB!|Ds-{B?V&XLNFtK3SHwMcT+NZq8D% zEMvbvv7EZjLbPDYTP<#(K2zVtEh(9@Q{EbJtKomLd<1-hsqaIm@52!FjXN1EOrD+jCI=VQ1Wq6}6$ppP8t zIfrVMqL&;-CG7@tG^^M8WrUb4OPxUfSpt+i=%RM9>rek#@+l9x@6Ec;h;4Xgc!Pro zqH?H-=kUm~mweYgg#?>KP%6U+Ui~8pd5W~XS*##(aitnu9~)0q93|2|k@kypK%^a! z4tmN%9;0|F&NyJnQ;(#8uRlf4|+cBnqCyTwR70?qCu@D#nkxQi7u)Z zchI#v)BeUhuGPu3m4D3Fax5CQnPE4H-&4WuI7y+l7`vU-({c3ayV8C>+`S$B)lr#q z7iCeee6b+`n?>}hyGH&%!#ZD+#H$;T#Kv64=n~f|?dcjK@3St8o7Rdm)n%tjoUA#s z+@!e2owa~04_C2Sim|-umlMhf)AmX%h9piS#1`Q-Vr@FWoxPXaRkmFiD^1(Ed~aac3JhfyZ>4e!rbgXYiDW85)4|j=57u_Hr&r$pSFgM`^S%do znrO_=O!*b}GF=%N-nSCRX2xILPO_3N!k3cbLH5m~YUa%ksW(4dE!MA8-+kmyzw0;s z&|D7PPw;rKfzR9;tglk^GY_qDVp4;ig|&Q{RW{G@u8UOao%67=3yLnx1!;$dRiI-;uXIVEB$ zU_A!E^d{XJ;(Thfw5baF)A2v-c_Ni?d!BGVoHRX8Htu=y|Fq{===L1*GCfaZ^_)K9 z_B`n}ph>EU>3Nf^o;NW)Po$dsb7W%HjV&BzR=erIYeM_;~cLSdG-GtA5%dpG09KZW+!9L##5%sMSEqu3$ zmcBbh7vCCjg0E7X?5h&z`PPX`eD{gVefNtSeGiC-eGiH4zQ@EXzQ@I9zK!B*-zM>s z?+Njn?@4jM_mt)LJ#EE(&suGL&sinD9ab0L3)W%2oz_6#%T|T&6>F^THEXi(4Qr9_ zP3uA5yVgH^?_2NtKCpiGee8+&KJ&EoeeN0N`^q!Qx65;??;Fnvk37Y`NZt)L{yfcohrp-5T_}%QiL{0eK%$$O=caN~slYI$ zmX7@8vuqe|ajcKBzfsZF(Lj81BK;TaCi1_Z@iusdO3`9AB>Vs9W1+S=Y>oUk#(Gqmuh zQ0i}vj{ZE9`}1+QzW{ywg;`u~Uzf}6t5&{H*Xo7#KG;boETV?3l^tDK{UkZrC^kC6 zR%)BdY&*SuSrTZeEWV5K*&2ByQN?BTYM*1ET^gB;?+OkA0YVL$G}f*+`uZV!y$XHv zR-m;eJoB0Gblh;p}GGMwDq^g z3H}mH^OxZ=e@9&I?}{t^<+#h=&hs-SL}72bmXyW7>{!q+2Pb2Ki!&D&uJ-2F~e6$MF&acJ?8SZYluJ3 zq7uECI+Ks8Lc$P@QoDw2A&O%Y(>=`EdNfD-gyLw~jPY;`1;_Fmu8w+u`XEXJL>#W4 zI9xw*xc(6|SQTjHABPV9@#y5AKwveI!0Kom>7Rt5beurPqy1BGmj4(vXk)2QozPru zCN_N~thO5FerLJd@9FY*yOjJIF3Gq8*G!2a2oD%k)H=4MTKkJ4NZPXdjyH#5o^Q%fc6{u`ss zf)ttZrtJE`F2EQZ2UhZ*ppJU5hw(1%KaB=^CJpvkw1#J)z<(}U`_D(Q{{nROUr2*} z5e@dm7~sDI75)Vnc8A|#Vbd9IZX{{YxGis$5YTs4Jc2+ zVs{EIkteAsn2Dz5i6qXJC#xx-Yvx(LtDaR;(1!&OV-!+XQ=nuC53>Lwq~`^hd0{i) zgR0RSO?T&L5zP=wah_}-#X0RLk(P}@*=W^pCYViO_OA*(jLENwyfm*`Jjr6M9mKgO z#Z!h^whf!ivK^%tIeY=|0B4^ z{}|T!Yp~hB(T!9(2%VH_fHCEh?wr-gQ;g7PgPfs+MhV;}PnD<9$Sg(=c{&^KV=2ny z8BFJ?IcpB@0UR!9dOoqb`3KS#yq`D++LCFeEIHGik2}nKy!QX6`FNJ+V;fFq17vSp zV3T-`c&W@3J^u$&v^{Hzb~KnG<^Ae^mZs=AnxgGAMK914?W8Gsou=qbnxeO8ir%3q zdY7i?Q<|dBXo|kTE&eaD+W#+9`M*Yu|KBuCyYZ_3`#+qj*W9UkNuFt@>UnvVnX0GU zsT%H1RbQH_S!Sy8;5%rl3KjKqb`zSb+3sBB7#Hkoi?vT^zdZDq{*Pwk~~L^VNZ;gbJQ62!-y;iaRhczM30!2 zvTPcnxpJN%QC6_^EYT@0&kMWje1No#Zw~IE2mp9@m zEop8f=R1vlelu)C4QVsfNd1PIp&V+K$WSw!d~9!y9Eo};`^FZk0o^EGQQH)K{8gSq z9j(hL&SP!3ROX~CsNdEoszRVOasqA9Ja7oQ1lpr}pcLsq83qPAVsM}nMgZ|3SHdz7`!KGkj;&xa(q~am4V}wz1>^B?@17$|S zh~N zD}~uKV(E}kzTB7&RG}R!UKmHclZ~S?;J!h;tLZWyA3W8#AsyN*-kX%`r~wb8LN((3 zbT}2Dts@ntje`DWWNlM36PgLfXktgPX>_BDsmPYi;)BdI`dK_!BR-^Fv9vDFjUlDN zOT}Uf;#jaiJ|!Mi!HsLgM;RMAi>B^Z!SOZy!r`M)H&*iM4qQ$^a0LOu)rbYIqh)#n zS_E!H`@k}k1eOziT!mhN+Xy$_jv;|NX{oNpq`*BmCa@M$X`HSI+>2`i_u;0%{a7A& z2&)1QA^@m77z!An=Ol5_nY{A9&5R4?PAq$VJLxb1JTumlDW1*nxI3Mt~N=2ZRe3 z%gYcZOuR#ZA@}%%47iFZa)}04F;QNwb$Vj7yh263Y9ac|E7|P~CE_r7HNC})ZlY9P z<0+!OzW@XD`$QLXlndpxS%J$`EqNVr=nUvYEZ1R_G6qmVvV7t#pJ0<`<>0p;>}4Rs zNuQV9wTbc+?=gA$C)HUp;5+D+B4KOML|H7zrS87Fo`!{Kq-}<;5G_Y$;FF-LOp`m? z(VRf~V@>Lm8A_*6dqI6Ni7(WMPaE(d^!IuW{6pJOGqr<@&$fwA8U>-sa2;i7@87DK z@-jp6xl%DJo`k>5Na_m%uLCe17g1*}!hChi-by|sc>S2*^;5(HpQAAF1pJ#wr3Zg7#=VmiscBYea75Uno*j)8(h3!XM8Bn z`x|6X3xm9*L0;G=21yyES-f#J>(EkOH+d$opxK>kxGW z5{^U<$B$l45dEAej&x!;-brGblY?`e+$;j=Ja;J1kvE#%;Y@jxAu^|D5lCUk1L$GM zBjTUsvdmz}xwJVoA&~z0;7zcrQuU6cZGnw|vo{1qlYCvNTw&Oqn-PB?TVtCPyV5}# zm|bii7)ph*?E}L~0aly}6+a=qQAUAPh|=j+4CV(=>1v4c^oRbf%nvjBMWhN|LFhZP z0aF8`x`DCH;@e3%{@j2X?GS65XvDuu$BR>OHltuA*!YxBDPE!`dL0*3?B1x3sbe+{ zbWJDz59+59TT+o7Y!&CNI`B`Cv76joiKuS4Y9H+>o5c65H)_U8MJ6zQ$R;p0iyt$f z!kd`CM*L(#hc-29JY{E-rySMi3$3f-fFntxwn}bMTUL&&-?MDfXj?ZrvsXzxiO&Vo z1yK>_Bl1Lz*h=SH#WQ>~d)KQhh;vF{JEd@(js&Kik>hkho>PuiPIr_zJ#m+)k15UooaPL~S+Hd=XiYXoQSWTsUqY|6H(_x(Ui)EQ~3yIx)|u3A_hCBieb(fqQW^-9OaxPj&Wv* zY0exm+nFmCIp>L6oD0Mw&V{1JxmY~q%okhf_yQewIt#>4&O+CB=w5i_&9q1&cor?> za>6$;d7BTjC*L#@~OVidzI~ifUUdBJJ{wnWwzpDL@ z&G&Lduw^YQr8hWU3FZH&uJWuiK^Hr*HQvFWvXt%Kgch~v=o3G(2*`HpsOKC)L6t5( zKG(#cY}*(GY5e)KSrj$mKMlMPlx@0mEkWgV1eMnlRNjE3b0gX~H)V;r+qy`yNLCpn znJ3q&QMKf~8PhtBai9V^wy1AJ`!{|Z zDx4^6(V2dI>31~!-shk0!Yzi>XGF45{Gx)RXDq#juGg}fl+7qi6fhnUzpB5-rR?>^ zu2ubi8yk*Pi{I84=M`0p-`A_Rt5*`IwkPXUHay)Z_O919i^@__&ofK)XBb11N))Qy zm=OHFYEipc?AI;bh~YKjfHrj$b(QErtbhg2CQDcvI`)RDpT*8McwAUO{Z^gT$>_b^^}9>M#> z{Cwg(im#l<@QYK8y-p44oQ=YAo)CU#vxqoPin#N%Xy$AYCC*FYQ0HaQ-FcO8+iT)T z=M6F3`G=U`yd@?(ZxcRySIltU7c-p?39o%D&UZc$mpY$`E1b{8Qs)bCr}L$_*ZE3p za{fj5?Q8M8vrD|}d?P+_c8h;HKZtLgpTzghFXBJW@0QouV+EbPR?MljnmYTf{GjDZ zt==a}i+#Q%_M)klUx`gF+-H__~X%-^K&XF6fK)uWFJPs}& z>;gO34NkB-62YFx38v93*c*AlJ{S`0i;7@>j1CUMgy0ZN4i3fC;0T-?9ElmhF*qwY z7PEuna9(gcE)Gt@qTpn#3Lb~m!Q)XCJOK{{PsFz1G`tc#8E*w=;N9Tq_#imb1>@B? z%&?((#8^D0&^gV6i~PTok1J*)fp=uJhqs0tyeVsx$6PbKE;lxa0j6rvWM`t8gzBi0 z<|#M^7-Qv5(igXz6x5J)&iq_!(rIO0+lyF&0ACFA zA)Su!p;tPp?a7-J6Zz|;f<(cy#aOi33Rht$J!eIf*1V4@vdyCvU4cXCS#zVUDrRmp zrEbSEc98T$Tm`4tTH)F#jy`XBWWc^}N;Q)gRXfZ9K#k7PL)8({?u0o68wHO#&ilM>l zF(P;q#srt)wBT}DJh#x|xfOGRt8hW^Hry1v6U&2lAHp}mN3bXOxB&IV7pxJH;3kn1d_v?0H;dN6r$lk^X>mkwtC$?z zCe98%BQ6VW7fXXX#OmM+;-TP+;?dwsVrOut_&oTs_*d{1aUl4r6%W2{H3`0@7U!)* z1K%d*$|pVlVWF~^?D>s@`{8*pLdDteV~=R2Vsr!q2c+g09ggq`N5$v}iiq&27#$&z zgIf6%`@x8O{3@TOYZ1{J-^(pjit-9`_oo86RWEicW)^#%<&)c_Wa*P7A_ktK-_!KlqKJYQc2!UR9%_ZyRp^Hz)EjMr0C(>+9#TTQud&9+*pM!rX(wupz?Ave@Mt3NFoI>)fl;>+?C zvr~^Iu0BHT9Z+wDpo7Ta}cRAt6* z>hY969k9wB-u#Z&n?7@v+}Wk<37!w}B);b=r%|ydgbqHTya!Zt3so(~dX-GItRNU3HwXsPnfkV$quN+G zO6fC0#hJ`W`IAOQaXoG7>YR)R$Gw#pqF>O@G)^iSpnxOb_}w~_vZa)Y1`ZbI z22y_B#24~+den>Xos`rL$eLCz&hj}$*N>P?`GxS9a2}TBSaGWbvrSeJ9}R3i zgc1VAey~IR;e-a$S{Q;Bp`mCQ8isbE3Um*RM8D8zRD{N2Mrb@<4o$>cp`-Ah&}8fj zO%csQCyIj5R8bb1Ci;g?5<^4NMMdZoF*-CuObneWjtQM9P7IwTW`t&o^F!x|>qB$I z6QQ}{mC!u#Zs~JduYC>3oWpm&>|}yT5RQoF0)$Dv6zl! zp(|Wb*;^7ZA)vxU`b8Ov<{J~STYm## zt6asu@S?jF(&0y}GUV#_sr7y~l9|X)QmEAt?n?|?7op564&mqov2cF7J!n^ljw)g# zRnsveiJ4{jnOBLr)Wd?$gXWxtVtgfdNUMDqc(PI%MYPK3AM#B%eS|iZbZDt@^d&L6 zksdpAO_tOuD_@R$%Oo^#boVp-=UX~SkNU{lhGO`ExKEYPsxflXtB-AHM8ygsO}&|j zC9KLQ@KjqZ(moD*ZM?#LPgsS-qFY6J2^sybk+`S)g&VAv1tzjRN70r#rng%7s;sn6 z0j1MoRM1KVDpXgryj~B0fTp1v(JgcnEuCc;AG#S+Lbu@b(5*Npv;yaaR^pP-ZMZVD z8aIUQ!K%<&+(F0t==fl$3Xg{F$CGs27Fw@n;Q|`Q8xU4W?U>-mN`2+dhfZ_lQKcnH zHXFy&3iNC=#YP39UUb7M`HrG^E3inuON+!$JNHQ{%~b#oqLul^HbgvUGR146zWX8! zo%sx0g9roqP={l|GZ&`;^^gJ``;7CW zWx;8%9!`TAPJ;}mq0E)ZE~+=DgzBkIAqPTGFAXu6h7Fj8T`+wT&*2Rkt2gkbI%o5(RwGl6u(V zm?R4L{Oq_s(IsbeChNcktG!BIGK@0`1d38ljuaUH@@8V?{Q&^6A~KYL+8n#uLOPt{ zpdVHT7juS;sxG37smK-scM%TnvC%5#JHet79Tpec+7_u(m6Pl7w8Uf@t$PfJ+rJ?U|cxX3P?uQ)UGJLZP=;Ns9;EDr6%)uH`_KM!CT9dD!KonfFVY+-#^;@NNrZ-gWG zkdB|w@!w(XRKJ?0=3*jU8AQU=zFG1U&)EbBcj5?htqR>0u&^F3PXP<-B6qv!Xtx{q zYPX6uW~ov$1+IK|zXDe-Em0B3_^ysXmX9~&r%a-t6x-xy3S1AzBl2_4LIT%eSd*1* zkXaGk&MT-*x^+}iKsp2d|F2wgPLn^`3pn?WM1({x_C z(JD*ZDXBzcImL!J?q~|)xPwLN>IDbuz!aCU3EFGp5ViRNJq-h!rr&Lbf#D-3pE}?D z`X7Cxs_wRWrM_)A#?4qasosvx>crLNnrc=qy$DtH_t}ERI+Ux7rMsQ~>@$Z|!IP4W zMAhN0u*2Qpgv${N_dxS-PqYpnj*@V1bPM-E?{Ghi2@l4^@DNN155ukD3fvtYjmN`d zu`xUWPlb;rc%O`y!pGvH@bUOGJQe>8pF}W!3hKhA3M+i3@Q2S9`QdX!VR)Ws6~4eV z&2Np4TDO3^(bSZlMZO}3ZTy=^s|=ipZ;iFn@AV>V1z}c~ zO3c-q*O^%?v8=?1q}A1R4L_pT7$f6sC84mo9VDU1C?>7LSQ}KRwL#@dGnuryH?FQ- zGZ@g>{>B#TU3d>M#HH;jgfc{d?HZF9zVVt$q7j%fxtt?FGsG0It@*?Z)DXN%1{^Od zMG85$>b82&nM_Cm;VTGqt|HL626^FYQ53!o-NQ>UFnm3Z3g3uh!Z%@Bcp1(KFUOMb zDqJ1D4L5{u$MWzU1V;B@b$BiA4_D!_@S}Jk{22ZbuEFl`CRdZOj<}`u(o(DJcUOP$ zANen}V_%J#3IIGro}MD1>^~pFK=Z_I1m)KUK|m(X2Z zk_FU!ltN@9aW46J`}Fg7``_Uu)6{hZG|RiIEHl zUh#%K``%CP17g?I2qol$(G$0qK@UF}p0V*GN=2ZxFNCaz|IUL34a-6%MuJJIlHw-OTVrB(Qx zQs!%9tpcjNlMJ~D-n8s>!zwvOwen-B54!+pF91_>`YtOere;x`$Z!IgVGWP2okXqZ zgu&H71nig3(!*TU6PBO(j%AeRu_wMAo!H|A70j29JoN6G|5E{i5ONKf$@U65h%y)) zOf%4V_G(0-L&*d((|JzaXUw2c)_*JwpD8Bo!r{GV|MLTzE8-FU0Ix7LuqRG=L`QDY zu%dC4{^Sln6x;{uZ&xY0{1YuaRu>FL^WK!h#Kk`TXefjHo=tYk5a$r#*nf4prc#^q zQYt%kiD>%?a$?-91oPwY#iV70#4fyuQPY5karzX%?C5^t`WY4vl%&M0xcCu&Y)no< zG8xuE?6^2L9V^izX?nZO2kMh5uRhW}QJ9Qq^%d7lI~o&dz#n?D_ye_f)0g&a->z6g z3pPXm4h26|7VL|>(axaEwMc)tLjdXQtK~ZpnnxuOtF#iA&hN{Ssc7eTy{uet^>0!h z-Nj>s+E1O^gS!|!#EZVC5BYD0U>-zr@TtlDuIKS8^XzLGzVbpYSSY7Y*C6)O1f{LU zc=sV?Jl*nUiJjEQqZ%vxLFI;};eZb{ofca3lNu$+e`Cvw*vA>q_Edr6@05h!Z7x^* z7P+7fy<+h~g6!AH$Nret7S4*i3LTSTQn9JVgH7)gvR%ZX`gs#m{AhIkxpsLpdjGM7 zHF=k+WxSUt^)Pd0yZ@&s`Ew>gJDCa8oL8|LFb-l@cAAi3*ZgYz>sSxT?2X*)-Bv!x zf>SdIX=}8AYxWYEw!D(4y9EWQX>o(}KJ+1`K1|#zRQ>(!G%MMrlE9iQ;S4gO{kMr* zRwGDLm#n;rK~*r#W{%jV5X{{eQ`5e-h{xtAkCgSmHJ%XFAiqwmN%@rp1w(pa z>9J&x2SovTh=mNz%^E(!ao*-O1u+fXWKpv!c!ZtS3BD(}Ua3Zk2Chj1h(L62n22f0 z4WEuZCW%&1%oAU{?96sG?gCEM26Q)r*pSqiPwI zFdMZM@ksV+>U;#Ehq4}sktpe`X-2jJ;561hg5gJ9kF`&vy4N#<+ljUt`AD>1!|;X_ zlJwZ?OT<#fTc_bqOc!T0+}Det;EMnc+jNw*^7J+sx=_5K3H z-I{AQ4B@hH#yQZ&aga+lJ|)p%C|e-v^`YT%l&ObI~iH zZOiLUGM6RZNv!?{?Z_xD=sb|41(YhsUkM>_q_4xQ2^+#^{jdymcc46Rhk`qC6S!q* zy37h*LFq#MJmk%Cv`}5p2vReS$uVMx3CH9nOvA!}%O6j;aYkt5z9F-8Bn8){d$mPp zY(3xdT2s=okHgn)C3saeUXo#_F<+IkebV7}GoF?yX(rs9Q!k=NW7XD(Dwa`J&-Fu4|>N7A{j^lrx)h^wEC(rEW2ti8oGh z#SPdcf2z0^c~X@mXj#+MRM*rdDp&G00Swg_p@0LGn`1}k=(2_btcw)wH}q{P^<2HN@{ zEHI~1h7><@^3wIRYh}C}NKMivk%3qxif|kY!G9HszRvB{5&jFDimcOT^6l^*48DX& z3IxJz2Lhw>X7OrC*M>apQECGMyQ;wC0Sma2b$vh+A5~M_h5>i$e=m3}ue29o(W^Q4 z_s#LM@n91{*=slc6q7P)dXKoaDlHtDGl#Nk)qOf!q6uz*n|ud3x=BI$Adq#OD-O!Iw$yP)<1T`#lZ@EM%l5X?O}u#2EY`- zfa&qZxRr+Hb!3fr`}+7Q3IBDpP`LVDyB@(A-$C9Wg?ZsHqHBPYGqvdaY@~QsA+b*U zpj84rRicXLtPF+)a%C~uzy|j);=;5oTidTqd#rjw`o)t)IZX61#EyyeTUOYgypc`s z^acgBAg(n`&kG6LtY|Y*r2mRo1k5??W4$jPq%R&!NGdNpCjPEb&~`XHIe{1Tpu`gf zS{p_9b7&6_c85d%$^j3TZh z8%VWmQN3Q(LxbE|hlc)9Li$FbX=rG1X1NEG(W|b+X&BZ0Co$!ftrlk4zb;>p$_9vn zwyYo!zNLZ_l&PcQCDPBZQzXZLif+N1-+Ik$MIO@tor*Sq63B?GpH)-(3*=OMSNJ^+ zD0_AqNn?d_@!vCY?)iyGK$%%$@w^pq7pxqiKv1Wv$x6)N zO-zypniw`(h&eDDEip|qE-0>9W}fYbyRjp;h_v_&rqOk4Ij3r8N3Cz$z%=pzsZq`{ z6Ud-#D==1C^Q;%US`=%eFGwkdVhK;I&&!>ZFaBzATqx)F2k}g+Rk<}~DI5pr>3k4k z|}WtZVe7-;4;cp<_=+ zo-(f&)c&7ew{>79^a^ohFYzB=05oae;4Om1%79&wQzuFH?I%2_+_AWnbHAnU#X4CF z{tSMu+*2wX%^G=+!1W^;RFuH5$FFRi)hI5`Kkr@tJk3!{IhVF|DeVzyjig@~e#ie7 z%h1T3CaV;}D1HubNVS?330$uooNB)GmcI1cP}#got8A=`+qBXzxRPKr8zmms7PKy| ziBVC|PPuZbzffY_>4CjK4wO1sI}q71c%W0_&nS&Mwoa{+1pCc3ZOmyDbkuBCYKrBAqdSP>VvmzeO(Q z*I+04-x7E8C%Vh6N7J6|9C_(Gn93AvD)L$}I(1l+!ZTnuE`Al?Mr9j- z9?EnX&G-@(UehruQl7LnSO99o!LM2lMr>t*Tf2uM_GP8VBX5C~z;veNG1dEB?UKGr z_&q|tO<~n}ePrxSJRt5qUA7^>sEwIrO-PA)E_nBEgk@qg`p9<{s`j`~BE)VJ+)S+> zThd3A;3)OKSgyaVlu+fi)m$pL^8>lBhtTFaolZz6%RmGSDNloAnj;2OAP9?%vpV*xbA$(-Pgu8a)Mv5 ztcjESP1h@(ELZHcNo~u~uoSlUDZcCSO}J!qPrUhc$pAD%|0W=55d~kPwA5vi3#Yqn zZ>|TddU-2m^#teFqDit-)CnD}bE+~|^$2L(@Y(gs;LQN4pxPnNb0TP1c(u4+fn!cC z#mxi)YD9m4M%5Y7^u))z+Z4>P@rp2!N>Vf9viG_HDeH>PG_{3^LIzmojat;M+R=PW zDp_3lF}4jT z(IUxYmx?B1T|=!Bq>JoS4~34{W6u3D4WoKewi9ouOhtx|WO-;u4qeqBT^+@Az7jim zGyM|Db|NCL@v54t^>j!yj9S4bP-auZD z!ZqA%CXZo86rvH#NG6k*k-RnYTA6QKuWL>ZT_6clKTn=LZRRlOG&>D#z8F*&0Yg)L zh7m>Ep~-Xw4iz_Mtz~@$FAX=Cl^ChlyPdk1W$M+qx{+ zS`@#!khi}-Y|Jz1^IwlVbyeJ6Ao6dmOF@NtfPj}01cAPR(xXhZ8Nr6F-dcS$4D|k$ z__qPOGn+EIRRp3Brj?guIc7Lo%MxUlDP}UOBnwUMl~~;RBsD>S;nT{2=1KL%s;GEH zr;3g}!?q0rPQ|?1dOV~Ek=BPi|KM|#vrx?}-Xvj@gI-+MTR+$IjLjvb&Va3xcnB0D zGY7Y4x4WumCS3)pkT=$-i+76*9e+EwY8VtAk&+@F!=9RJMW+%Gp)huBy zV`Ezvs!w+6AF_=qcay>vD+kYpuB_`XZtftRVHjF&*`k)CIUSwg-X7#aUbneMWKhyB zvTaryAcf$6iGmhbo37kj;T+v@Hj&{@D>H)AS2OsoVXq4}6wO~-mC3?TRn89EEy#G`hho5Tk8Z^Ud_fcJnGD+YV_-y5fTF zr}5Gl*4n7)b0%&V5+f#x`s&+F&ovr3zE}2xx3|a&0ay&Cw}tdCf(s2ub3!jD;d9T4 z4T@VG_EBP2gn7A37tnucalosxB|8N0QU^TC>wSGZa3a>g`SH_Kj$(A&)CMy}2XdS%IM$BqhW=yNO2ysgNkSSgnii1e z&y6)~>tW;yW&!+>LHE$%qea*_3C4c>@$2^%++2Bs;`b+7sE$Fl2Vw0bJE(2Lx`(Y^ zc>IJy;JSzAUV=L?bt6b4O!u{Gc(;_uOjxWt`sl;H(Hi##K>8g;xA<4Ap@R@TkY^>g zyJ}TskFlaKpbR-9uqR)ZP%p&cqIQ@;&9P{eX!1A4S8coFMfe@9rd9a1&qx_8p$ z#9ObHeGK^8TPM`_5r|u8?;{_+!Ip-N z(8gugydUVIzehmGR4-ff`#R-7#PlpOlX}6ahM!&=>P%`PAzZzx%31Dhkj+-p&f1N; z5~eS$o?E1?*IORvAzjrdgQ6Bvy+=i3Jw1i%RoqI*b8-c0{AdIz&c7M;n^f_k>70-t zCdR#U26lcR&V5l++Jl`Z&3~L?W2;ZaPS!Oru-u!f*N`8NqLiD*BM4L$uO%E^@&4T& zRo2@w{~gwHX!BK0gSjL+V=Vd$q-JPeL4Mz*$ZD3RFFbHyUI?i%Z6uEh2k9SdmfD$5 zu~FyN@L6U)AN9EY(qmD(X`vfkQk!H+SXq zyf^{Xe=i~Y1PUPhXmq8YK5^y^`XgViXsgw4r=DrEo()!sNcs(`c>9WLw!5%bKiC-D zw=96N@yTSX1u#T6Di1w4%PT&uXj1R4IiTB})28a~Ob;=5=B4ltEq0m>dzhFpjq!@~ zOJczAvWtG9Otuy$B##i9PF|8F<)Kpu{2=;o9?*Lx=xjkz6SN?p;a4 zI{|85Xe=nQRl*+ zF79xdH#6+czY|bZgxO@4q~7lgjmUU}{Yafkp#yY3vsw#Z#)hPxV6tQ|4N>g0T5 zVqD0_xV-#&CIX9glV;Fo*R0enL9fhepqO;8*tL+l>3{pMR1!A@9q9dqCy!BI#itnVn8}XdHityC>MC?r~Wg5 zqTZjgu=l8)cG7t)Px8N}`X;|PDIgEdhl#s0u!C1o#qKpsum$Dd$5x|Y{nD|cepI{_ zfuh7LD%?@?q&e7^s2Hb%rU}aN^pE=)3%1l6wUL5;k(e)95KGz220;v9w$l(R88F_P zxLpFP)K5DghS^qn5eIA#lehpyk>U{$coo8+BAgtXedQ=RFdq5!fPe5>ftt-Z-B8{t zV8oTEWuQWO)tu@l*?!RuNnZ*dL+ey4UynE(gCa0wGVR5c+*+{R)v@NN!M9#^P0w4AU86_rz0@P! zWy_q;Vd4oFp*47rr74XAMAz>cwXwx}UO`AGu5UF0^B!VyXB1d8tRnpbR5uy={<~pO zIR*N`sHhQcfrfus?zP3Ko*0;vwF9Y9whipg0uBmWfx72jSOlx&4#Qtj=3ev-_==Pc zCD%s|e!^o3=Wp^REm_Ul!-Ml_=Je6Cqd1fJq=6h#*|x7mF{nQc>a(6VNB(6RX-Xxr zn#Z0tB{FVMlQc_mMlawvoAc-v0kxRjltsv)nowH04o{Z*r<;ScFE zS~C*bTg)(JLSOjdgnwtYTM)_9N z&762?My?Zx+;}X0W*CGt%GFBo*+(2!NqfUFvi$GMD=Tp((CS?>r@^@+K==|`5(yKX zQ3qk3NV^fS<(1ZE{Sz96b>2NBq239fQ83q6?b@;(G|gJ#zv8x+d8d|UMV6H*a8s`9 zq&T0wB?cu`s%7MP;c|B3%nbS3RpU}E( z<51iTIPm6#B7;j&`b4Z4bZdPVayaWJlP|z z`V=F!NMUE`+BV^T8G&azEdfhCkKcyZ1{1s2`bNJo960|?%?&WOuMhM-n0O^NZKZ3s z>osXuEeVhKvKCNODDH^n+*eOo^ zi&?YOhN9RVqIRI)EYmH?{rKBW&M$H{N^y?vo(7Uqun6JNHCE9t$Q1QwxOuqGN#ekE zgWYp~aF|Og=?$|AZP!@RgiBDyFJWU?aWMH!8yvMSQt3c5G5JmZN$Lva3+-e04p4)g zyT(LGg&3Kfj0~47sz%9h4_F`*l=2xFlkQtfbsK)2sK=0&sJ58MSZ2j(+n_*ODV!)? zrpq*qlR43`q)=VCGBX%83eLzSO%5!^08Tm$#4Kl2#}Wpw1r4Q~1E7ftrSLS2aBtKV z!JQy)R1~>QNGQ*#02H0)RKsYQQ~;#vWyQ$-c4eWUAd|4f5dl9yLu9k|pDL)lnG z(YIf`orNebcaZG8d1Ol@w(gy#EskhHgWuCD{`fU@S3^1Q-nrhOIZi%}>*rnH4xyCI z^L@g4du<#2fp?oUnO6@$0{Sq2$H_Dy^D>deh3#xwbyhC*v{Jbc*M)uWD&{C0Mn?#g zo08{Qi>sm0Bu>guZS5Ivv9+<&{|7(YceSOrX=b|Z96 z62m;@IxInQjk8rFgAc`Kq2UAav^1uvK_mJp1>eEoQDJ-5&Gw(Gi|Dk!#u<}A7KMBW zN~4&MrS!U^{GnM8AuV^PU9Q}lG0=3?F8S9KclB2|HyQG56?bWt3EKJ|7wto-X)KNn z$TrG*PS*{Z0m(6XJmmNeW?MXUu#UZXJ)os7R{yp;OmeRCaOf zJW^u5yi!H4isz7Y{V4!#oJ{+*f&;F8K|zzB^)a*I-sx8Ktg}%zWv=(q<#Q5W4a14J zvU2&Ns$r8vb&SU)UHW-wQ_Zyygo-rv5#E16 zSe$GN!>6%7e#8q}<_kl&lTa}c!A$nf%qUY!CD#rzm7K8OdUU9jOAch|h!Vbl;ZOEM zWunR-^qeTWmvw>jmvs==f56dfWe)N3B{+HI1fXlZ-4T4g@Vwxg2a_WNq91~F=yrs9 z3l!*@S4agvtvvcUVQ18R6C^Rx5_Ug_g}`g&R5(!9AZ0jG!&*_&^5xZ*=?Qr zy~+4$-*skxA^kBpovgM1GR8Z!TN(d-;nq>~6fjsy4+9SaX6QTTClukaG4O-%=!Wve z*m{a*`jK7%LNBgl75f{&*HYd3UFZ}!gaX;}Xc^^k(-h~^r^*%*il_oXuVhz5D2cmyp_tsKk7o4^k}+O|x4#Ngt4t6m z$lp2^?5HyMVoxtG_hxKskMvLXQDCX6?u()-@niGFni_MCYGv{HXL(`Vw(4281cPj- zQ4-+KFgV8#vOW(C`-7&FEZ$qF$aWeNom?o{N z@zgSka2^R(Tu*lQn{<{(c;48r$}u6+Xby~-JYuGs)J|KPHN4W1M%=@G^0(05scGFY z-Oo20o7=DKz_~XRd^VK*R-@?-8#7!Zl@mk9R2HzJgI{crMw`+vcz>HPs6Km&e^ZOPCTA#oO@$wf z5%CgP{A#|d6v#&34&c?;{LwUD1tlRD<%|jhfTzhzNqMn%ClONv zh@pXMO7k|>Es{#jUTOd}gNHe2Xc2L>SSCWy+CZz88dA}U)?Q=`E06}DmiTez)9mNt ze-4=ZV<-3?0Tv#Xr#qP)ui2R&?UGxPA^vjA8uua zhD#RyZ;yPgXgZ+t?Mvk12EMNAv%Kp`yM+~aNuWRFnF+7z{C$p`9$Lc*f5N9Y)UkoB zC@)EdZ&h}!(?Xqlrw#VcxITNBdBJ$$`iguVRgQ&2Q7q%BI`6ld{K=`p>6=0q%XBd1 znZ9Fi`+Z^X`b~>6yVaVjUyS#f6D@21=rD2<_i5Nkny&NXsWQryWY8(JuGU+pP08Kc zH;AR0rk9UVLii`|kl@b)7{<3&=v?lFWljLfDHL9o2ZgbUrmnmuiflb7_)9SI3*@li z9Lk4?V|6HPHPE=S)EM^{nG>;I6juN3ooG7@)PTJf*y0^&MueC#%fG6l2271TC#^WF zAzN;fnu8lGbCG)(}f!h(g+-xI4J(3$NS8E6S2f;C3(b5b_bAKyx<;%49f-VPb z9C8g^*9<|E-knwwx|OMTU52s%L;k(5UjK!@x_rcS)-SjVX~nHXWL^jY?ZCcW4GS*O zIFIda;(nwfpNar=f_C$Zc73ZMTy?8pL}!e7d64>g#ITk}YPSOQyMl7Iv9oyPz~m&) zXoBQ>|0RjosEcutpF(e;VKx`4+)U^(y1`F6_Ztt%9+|#jB<*xRjOUSQcaE58vmf4( zbtEcbMukw5ZkWkCi?gWYJ-=><@ckNgP0As!ZU*Tfg+^Gnq<=0Scyy%sNT)Mte!T*W zvv(y@OdAMZLQ#{%$xh=OTyF<5c=ELJZoVD&4L^oCLd=+%iFcE~d6O*dJUlkTIZNdz zgwSz5iLRFA$J?s~N*EfWfYbs64<6_7aEFUM)_G>if;7#3)3~;7zFd99?0)8-Qf=s5 zaHLntR!JSJc1nKQ>hw!WWLaYA+)S)nD#HJQnw%M%$GqntPLrvB|DwGUDIex-M<)tq zY5!F_62^>!HK6~-%pWLg#KRbNdXJ(Nk}$-phnRf_gQs$?106%5bDgrt*xAL&RvxG; z-lL(I$}B>!?iT@wHuqsQT_Ge~xuii+0i@4;o{svBDe~PT%kN3F1FoqU`>TuQSgrfv z$GtIK;vSkiVCnAQx=meDNH&4QRr z(|`zB+An?MpOXftZ;)X)MAQ9eo60DrErH0Ak5{?#A2l{Vn_`J-tcR1jZ1p&8$L-;# z6j+_o#zjP-hc$drLxXJ5Uo4vvqHODu{DV(w>k`Pwth%=)kpZEt94EmK3=~x?;xXK; zmrO#=`qVNMSALP$@ZQ>I3D~@jxdI#xC^E)ADH6VBiG+?eQ`N&yK-itT1XGCbR;Qmq$8hn9np8C`DT~{y=RG`!N_e&fR$3 z`hj-VqW(7GKwW6NZh0e9I+`eZW{9>Sfj6#5Cv zLkW8VpBaI}3ZSU@$kRLGMhB`Zh(ct{_dJ)ezFL}FQ=R*Q8X+Ir41{Jyce0$2eI}~+ zww!2v#_nDj48xA=CZF*PL$~WqUZFjKLiNjUXfcP%ttbf%GhRtOL1k+~4N(Ls{fp#> zI7zN4f(F!IpZqvS`sTd16k^?d(|c$~+J24GN3LeFf`%54yitBpDW5!!!e~gKy*kQi^F_p;8fui-40EKOTi^S!|XDyRKfjPlB37X>#2go)=jP zc5czx>Nma)`}_&{44CqyNOC=4^Q3Z=qAq##wk%TV70^{dxP&#_m7iOJyLviq8u(N8 zqh>?)Th*{~The=>>`|wxADoWi3frQ275IbX5v!_2MxrR2R48FgI+}}GDQ=Pa&Yyu2 z?=Ajm`DdJ8idlIxq&WPo*3G24TWY!#YJf$eu3{BHo9eSN;=In$9fdi(XeCud~scB z+&H&}LqS=61tC%E$h&^>4AO(VGsx36&`4RMg!4Dh{s>S){B$n)ud1AoR!L*A zGR2C+1sa+M9n=NeY(i%|d8n|ZJ7Y}x=bJ^1z>p1W5F6>`QdsQ=3ocOim?bf6t=fzZS;3Tb*<>_R`pXWls;r zRE7M%JUdN?A`Jt0CTqqwL{JwbFOcnnhNM@b!L6*%vH{0dGB%A zYDsv1x)VoTKyxUl7FauBHYTzQBXYpMju?~}exSjQ%zOnh;FJXnDj&hFsnt7*QrJ1w zrA?D-rgtIYW~8Z?XD>x|{H*@90$X74n~%rh_|nQ+58zN^G;kou`)Z3~o+T@K^WDE8 zTG8Bw`tgKO9_h1_+=iFuSVWCS{Gk|x%S(nlEk)i>^8$bmDBr8*=GO2aOw)N7X=D+m zbK+&ZNN*$XfsA->+HCcQD)ugWs~ zn3mlOrER#6{2*z-S|lQ+B8q)0${(_mcmdVDHx4v;#4oi0pGsl2#Qy9=e zFxD3AIxg|oI#;f`ra*^j*$5B5ECc=hSzx1y_miTNJ2avQU$w-E69mvIx6#QWA$@hX zj3VJe0V{9HZs8-DVZRIpo28$4a-^Q^A^En;(vdtY<fOHzE5gjS^TOG`8 zQD*XFrZKQkeID>{(!1C|Ti%7!dGi%p+r^6h{3@6>H0MzP_cvVPDfd=&_)xu}W6^*= z?o~lE0&cC2fM-y@x-SeoEiEGYvLF7UBLn>={kDO|cEFVKVK?j`$Lb!oEFQKBW-3n& z0i31_9hN?po|cmBERdy8wyvpO+USG*A+k7kE(<$_??H6XzC`w-)q+YWEl1UoFmmkR zQe~sH&W&tamK@f&kN=AyiY1c|-=}SdMGItEKsLWS@1C=sa4^z;tFmLP_Dm6YwiBhg zhz4m~cymj;F-r<$EcQf2d0Ka)+8jH_J6FJd(8BVO96zkPxB+e1|E}@e%^^LzLjMq( z&1GKBRKeMYdP_G!1iU3YeUsZOt`1=Sut$RTx8J;eczB<*HA)>+bK`BnGwF>~t;2vkINK3Xl<2ACEdn+Yz zZ$wJ6VBoO7enCV3617WL)A;Rz+z$Th7e3stUw~h~emS~WGC4Y#JJ^`nGC3H#S~JQslqnaV^t7=<79r-sZUkO)T>pEYy}YOk@c$qXMm2<(3&s&*@C3CI z-$<~YemKN8ym>IstJ*qTl0;<%0jJf0(-Xq$Cru;wqu)>dB`W2*Z4Sc&^Gd;Qgv`-X z;J{Mv4Jo48W*2TY%DjpyA-&86tBBfZQiE5TH|M6y;a-wKO;cKu6-8m0%@WyTZW$NM zJGy7Mvf?(vm`xkPl};9)!9@FC9!t)Y9kpUAKzV)@l}&u4QEJ5_Cml$4_^G1Z)*dYOzND)CSgD$ zs3aGcuV31K&o<7__jn}RfRG&j*s0*qE}vSQOf|NmYda@>gmSu@g7~ZWKdX-d;zEdJ z11{L&a=S`$vNeK4Pt63&^UWWM-LflDM5A+|{@j7$=2|UnOaAV}Z;bF#bKMCcZ6z6p zN=eeMGD5rtEgXK9g*rzF;=tD3Ud&?T$&kK3r*t8Laj5vJ1aDq^Rk#?JVwvtJhT>GiW}J?huRc1}8B^gGHQ-_>2$?!6@7Lom;SVjd*|QoBtxG?bPcx10 zhU2mMHKIDF4hW{4F6S%omfK1s06%X#f`03Qq1pnLE|Q>t?eEtYXWpN z%VBiR0!Hox0bX86bGQEuT`F|LA#O)m>|idta^+Tt1u8|hA>Wh#Wjqw~nwMQNg$981 zTG7wG4ER%Q#0ctux#3kKp~8{LopSnzBWWT-;75Pl6MFzK>n1y+xP+T`LkL}V#D!L1 zi>nX=L*e$IHXt^J=GPec=YM+@H4s3H8e^HT_r`>u^)nV*xgquHzaj1G3xf^B^#AvF zeh_G7$)acG{L}yf<&c1;(GJUXHF%7!7}*Bt)eg(A2i85I3*lb@`;BYvfh^YSa>uiQ zqZxP8|3gK-&jWtTG>nWNi=a0SNx<(SK0kuSH@M#$ab5)ERB(x5XE=)SearJeY{>TY z3LC|O=HHS$l=Yv+H=}6PcVCx~=yyL?@YiP)V_2@#@sTkzSnT;Yy9I0wl zNJ8L)u@vF@%zmP%+A~OuwDb<1i>#Q<|3g051lHkdMWRsr#0F@U2Z;G0$@_ZUN`e>V zDp(%|5VwC3_`nzzU)W@d=8oG5V zD|D(hKo37MC*EL1C*XxqQQu`am7hF_&wCtt0f_{d}( z5$937z9If!WfVe_EYtg^ilM~6esTT(DC7SX!yav4H}qwK@0*)&X-+g$F=%j9XciNL zKd>S!AmE@@cBu2kTE&8t>m)dh)3tnEHg+vFB`cCOEl6})H7z!N1qhR3&7d|_H9ET2 zx*zppt*1};n|a^b390^`79OnYc50kF0#wt7I~{!=U%k7Jj7&X0ODMlA_x>|xKL`f} zcPZ50B3-x}F4%QtSA44$q!)T*<%L=ZWx_1m!j&z)yx_)@55iM^QQ@Bb? z7l6H?4-MZuKHhXWV)X9N9q=419}p2?)=UpTUi#n6?-?5><`i9BT_(g;YE$befcPv)XYhq&Uw%U;G@p!|Ei8jaEp zu_xJr2fIJ(f{R|GGbwFZXZU#9Smx)Qr4qrVc#QsSIm2Wy9{2QQPQhm{pM5}E4aN;U zps`gVHb^IfdggIWcf8Pi;l_l{Z0)r?qObDfXBxU^=Ni~CP{3OMaI<#yY%Z2nL>whe zDZXC%M{;63VS|;X$x(3k7^^$l+Qw_GEIE()_^vAHtyXMhx_7vu$BvyDv3bSGYEImq zR$F8YZ)Pd5mTCn^EtaONGr_I=)iTS7bQ$NhhUrL) zd7jt$`7;&SxlJXvS4{&fEOX@QE?DQ6H`s$K{$o8h2eEiGnrCL6Tj?cwdI4>#tFk*G zbrsh^a{j03NzBfiJgcX~zVVW}exVLDj;daxKqV#sIccH6F0s_mNS_ghY|*DNT>8~W zgubl1nx~lS62NG^?K_o+oE+*F(Qo4taFK_>KTFFhDAC9cvfOFbJG26i? z?LDTZ7b~mww2%5S&>@)wv{guBP%#I}oey|lZp>RQA@exiaBC%tBPkrjDN;K3+7j>!&p1TM!xo%p&#RX&c* zKtj95Aepo*Xg3BNSSc+=v-f7iOPHQ*Rmpg}y+w(Lr1 zwArI;)KusFVbG>@lTIj@MtA~yCJ7+XuTKUy7y990XGg0276n@tmK{@pY;8fNNilwr zuTEKmJ&g|oz0ry)>Hpm^a_J)L>C+hR#;mJ^Oq$0b5i>P=Y6{2V|M)sF^+EqNa-_Nf zao`z$y)HLPP7Wt1pv#vOCvS$x1BKIrAa7<4zD>;2zEw#=(i7&Cu1&mgC2%=kq~6+4 zs^r86%#%!C6`Nw-N(mpLD7k8w0{#hhEnYMQ>tU~o^2p){~yq4OM``}^kl^#yYSQ*8d zBLypOt~!TUigM5lp&WEXP!F|(h(6Z0nG@EUxr{8LZ19By>)?e5~~r$y~d5;HSGb5k2lR*gEw}4fquk~8jCx; z5hc`b1!l8~J3*ioF`kIYLm3lIl8N!T(CvRX`=;>9<7~^YqKa+v#I|kQw#|xd+qNpU zZQDu3s2G!;>6yMgPxtosou~8oUwiMhe``yW_TC!79lja5sTq0>!kF)3-{M$joj#WT z*m(G1t2=G_-mo411Apq5i3Y6tG1Km*G$`+AOK91}0Q0V{;u8hzGX^ZXBq*;e=vD6E zFT_)F^WIBjSasGmU!32IT( zW1irbUP5KOsZnS#3`H@Gh8P3HT)Clp-8#z7lE!{Xxy)qxx`A9&6otJB=n&Y{6LNIg z+tuRgAqI)$b2~=Z)lb!>!&fbpr-VRt{vw!k>=b&oP8LG8&VV-V*u`BH>OFx1%-VYW zZrcHyT{jjAL)+G{-bf3CvuNNAWs6;!RgUSO(`qG|UJDw6S`>{zm6Nhs=@|Ppj3Lvvz$@+* zF6SoS>El3xsHCiE!iQ*JgE_QHEj=#vv=h_g6#Ap!Pl)xoS<5f zl0xnNDt(P>Gv|PekjTS~<_a-@F$ii8S@p2EP_7Y&OYq83s8L3-G4wF{)tZRuf)XUO zKJDaeoM+Jt%w$^Kgu)S{^hc|S&JT@(Upv7H1j?dPh-l&I-KvFC!StDJ=U5$zbcT7_ zR$>>TBY(iZh6Epx4I2YEr~O~+tk|p_ zq0>!_jqr98FKj@6?(i{s18#IyyRfc~dagg<|2<*HHUDy32LS?l`2hsP_n#!}f(A|| z|4=jjPvV}fY$LZIkK)_z0>BQIK~TN{-|3K1-2rl-fc~Mwh{zP^Z|AmQf+E_{lpRku zjDH9rLn18m4*W@R(5AB{sS$NQ(baX5b8?im@VIh)%?CW0A14g50DUJROrPBg7_}ggQx)mW;4rS=UZ1KqV>8N$d22$EE{aB*k@syP#DwH!Br#fJQiH7Zd6U+<4-;b18cQnNJlPKB zu~va-Rbiaw-Q29U_lT`(A#Yq{tfFC*y%xg$Ca|`}I%>Fgogrh&wYj{~X|q;v>uQFp*`o zy%nz8afRg=F%fO9i#XkXzBRpMi^X~*$=c5C-j%HpveXPvg+o_Hxnf7)4 z6j9|V2y43hS1lP=m$Z_}XQ8<~Bz7ihTS>K20^>dubC$xG)#6pNeq^YfTLj+^WRc+& zceFtBRZ3X+3`NRg#$r59IM*DvcoeDN0j#JoG_HXUVU1n*0q72$?v1&6APHC+Sucm( zIciSA#)v+KsY!}G(up@$wxc!Dq=)FSTnOnYpb^-95@I5)m&63h-v1HuG6G1>7jgid z=h>ZWC^WM49`6WTfNG9VCNvUv`ovfdNO28T;PsZkn&wQWC$9@*SI*^Gsq2}}VEhCg zjoJ^6!*{E2_YsFy=>obYfkU9-7#(ls_#$+CB2Ev1?fYG2VHln!O3U zR_W%4?Q2c$OF)Hv_FITMm|X|4-Gu5Xc*PInrI;#c*z}sPV@gxd!#P@yq}61K{=*L8 z?Ps^UEQp@J5dZEr>nq}Z9DJQ%v~FMQIRKf;*CtnH`mu}UYKUT@12 z=L<}~XsS@+hBq;|Cr|3)1`Oe(K+;zqlqm>ez#F_c7jL`y+r72R7jQCCke(G5@;PxI zu*Y>rMSCZerb~>wlHc(5Uw3NE&aE1W6W|wx3BZfryd5B-C36^ zhgu8!-1m}A;mtNNzF3ALd41=>)j}F&e8P%Kc=0U;NrS8tsU?cYI;k&hj({RS8A8|eQ?<`r}_F|aZ)wEiyvGC7t99#{Y&WM_{ZKU%iD zxIXz5g@j6=Anx=y&EBlhYfWpvh4LMUPx6A&8G48$(PQUC;`pVV4=8FbW-e?lYL3Wx z!SGMeOqTgt$)_=$Z(q2%U^Cw;q9(SNgI9W~CS(^&hv4QU79HEyQOYC1Kvb=Cn!1}Z zD|#aC^tio4!*m(*Vzxo`9ejnZ#bfI3`eW*P1Y?^1y3X3FDq32P8NcZboaW&4`n-Psa{IjI>*#q~Svm*0 z8n8ohN1q;a;L=5!IuXA2nDZ83)#W)bKt1Zo>{>myr@e$kG(tJ=OLI-{ zWf~r96}DOEJ^m_7pP2Up%&}A$rq$drA2`~7k1^f zIeXO3H!hZ$V3177k;^Q82vTQP8O3UffD!(_QV!|0a+iXsdH55+<1!F%JlAP4rdswQtb2FHb3rjat+xuGM*i!WO=iQf>jT066N#5~KGfA|0l|Uhb;-+XAvQ5kOWI#qmX$g-G)!}GaKuRB> zWqqIsF_WB)dsR#N?^Oz+n;4F*97gMr(JQ|4ldUf%OxtEQz0paf1d|Ex2J6=DZHCoT z%d)3AjjiKXxE~5r%x6=^)s4aUo{C>8W<|@?^p~$n@PNpTBZ#sH9LzrIgc5t=|4V@xZOv zPM_-w1d*Cpnqq|TXmNFN^xKuJRx9u;cis5b&fr;VP`sKYm)fD)YmaG>b6&oJ&5#XU zxV5qiNVXJ5tyyhehWiyQxr!Z!DdjmUDcA0-OANYO9=+eKJhYfXiM7viTx5=_UVSawJYtK^m!cIDOzwnLb`&^@lU{A($=X7{t?^65^{-lm03=eF3qORABI zV4GwkqvKEcM0>l$reS(`8?o>d;VT>fPKZq?NX?_E%D}7y=J(ks2-P^Uk@yyOGW5F4 zTc2W0Cx86Gyb_tdFho33Ijv&%->4XLB$X6vXa*gDsw5DG6G+y zeIt(mn~0r>UIpQBtP$e(b>0w_q&pk;ByR6G2fkqpzQJd_5rGE!P}u*o==dcD3{`14XrpOcPokdo)XyO@SL$+SJQ zw6DRq#Pds}DK{94C)RudJl7n3q`XhUlU`+298!rSmTd`3Pl+SxamT%EHj)BDOqiz zG5fL%u1=l*+@MxkMtyA(lcZ*m23H%(jB7-;en74Zf$qj;|BpJ~I;=EUkDm{8h0NYAOchDsK4~%;Qt2tUj;-UtUJ_0SxpN z+2*rTn17$~4s@Lml=IB$=oh-Yoh4$IvhyJ~k~5tzcx6EQr&V-l*WY$qeDp+oD3#qm zc_3eCNR;-_avt$94r+)(UVkvESF&pKUFbJ^8o$J3S}%E}xpI!D4np6WemgXBk-YxxS!95Nlx5_^^V+Djhss16%r4q&HAMbx&jve+K72@ zT4|Tp6f3W&)QL{k!PV*whB$e9=~uuiqjum>w~W=T z0`4fIA!W8gLr8OrcXG0Bi#c0*>-~KXClDAm==A}F7n$rSS|FFzp=V0!nPi*NCs*`L z2`o4Hm~NgJI8Pt2VN>|=1^n-c5H$aSi^_La==h(e|I!AACf5JTg0dC*Wc&G1vS@-O zs{#fIrWAI5KM~6f3^~N%G(+);zKQ!DQccq_g zT%D}ELHuIES*Nep69smGfrXLzp>)@#Uk1+f+53?;F^g|Zm9R%DRE|7G(P3!bV14oU+PEjyufBU6_IsIkbeO#{Y0Yl>!X5>kks2k&J-VLYnV+rjy6+WQ<4w+o&Ejj)-WDMVs{YOHFw4I&(e?csMMrwc_Wu)iK zveJb!J78B}Ffk(SAZ`o}+nq{Cp3GgUMZa;WsHpl`%;T2C{RaLf7D=yzib!{p@cPIn zapo<))337|_*9>Z9~7IH)u+kFn!)`t33j=(#M5uDb zL|tJ+ce0^sNvDE`bp$s`ymUp1EoJ~fX|=vn-ehQtuQ5w@DA3#x3U6Sozl*y6QH1Qo z`ugi)WzhuWVj#aZpRL%>(R_{`<)!K1P@^2D=NFIK$b@m1r0*6Qf&b*)AY*6j@?T$3#mRn3$YK~H zJ=lV9382iXnt*D)2v(@^@+G39P$DF3*fQaRT|bC5D6s+Dtq$j1lZWgkre^7LX+3^D z<x2>t2ulE=9Uzo}Z7y|+RL(E9(7{e5JbsnRp;Zww{ z6V`a8^#O)I!j>f8CcUghUh@elz%{_@VLbn(XYwS2F*B>gTJ;PQ<9Zia9 zhXb2TIG1eRtn9X-|Yq#AdbX3|KwnXFU7HVf5+aDcn)8|qd+Y4HVUcLZ4jyP09u zln86;sQcT)SVo;&sfEB`hnZ8ZNio+1O4ClRpO5XKPTBIo9`CCJtolQc@siPfeWykCo*BwyBA}~(J zyM`*LSs`IxkP}L**r8k{Zw53!iQD3sXGbk=;Y(C)(l5ZB?=X-@ui_p;!3|z`w9wB^ zAx1|CVm8z^(L*@Yl5K-$wdh4 zTR{~-3gtcxdZl4|*K|I0igY5$x`)NfW?K(BGtNmS5vVj|kAVAZAw4IFdW+71HS6v6 z_xyV}@E}V3mUe-FBECa0*MAZY{uPFk6Sidg=`lif_VojZkWv1^4MXuT;^;391_gon zAtB8GDvj9Ni)n_f?t5Ww3Ziz%fN{g2xXxZLJ|uds?$19!`QgoIWwo;yFGA+w3=y$H zaC?}H?Wf@N?cWW4bZS|YO56}MrAYx;D6>zr<;kyCp#dj~8vQaoOc~*7edw$aFBFWw zXXJ6k@{wHgNe7lfb`6tG z>3Rw01;!oI)U||)iV%Rw*}pB2V?^I04v&g7Fdoeg4YSA`VTM>rnWi>H-4Ya+Q`@Be z7pCO;>|ZTRQ##%Q(!no4hWUcNU(AWfTEG~Rqd5^mZ150y%~8+}spjdQKiz{W$`IFy zmHT-w&)M@@ii?2lUMLgs3ff#-9G#W!5-%jQGxbS^_gd`k^Y-<}E?6e7rTTr{RsNAdV7@O|auaUI`e$EU-{o1nzIljZ zxUVldl+#dQQj%(!Q4U;Nyoh#qjg0ZIJ)Wf#_G!8N!5oP^w^WmYzp^UE(1QNB44JgO zMtcs~%N!hDQFJ0Nsuh#;=J!jkY2_^<3uKPy4M0BV+m{8s@-4x%6e31kBZ|#_12Kwj*{GyFpmqd$h z2$wqEW|FpdgwaFvRuUXvrS5?DgXqaOZknx4Q*=u1UkLo8u@u(il>y}Y>nnbf<$t$* zO3BE;*4D(4M9JB}`JYvoWJMV}WCj!*GqyEm>kJwHyE!yylq6atz@R^9nP5d(SQ1*1 zGKakrQl&$CJ$h}VGRY?l8?AO67NNr~#7!Z>j4arR?t9KjwAt|X$H&PD9nj2enLpN5 zRbE7g?V9UE7vvK$w|*IVdy=Z_kKjCZRIuqV_XQcpDGVTU z;aH0M)BFIT-p(LKx|KyN-f|4tgRI*}RefFrO<`!M2!MS1RY`A-m_a*DO!(b~+&$?w z-L}tlf-4W60gQ-*MG8g+rcM4LlEzaJqw6FP@bw5Ao1+vax9#ZD_iCnbFFHA)mY({< zc9%>*_VL##tsGZZ-nkCJa{$GA1pV`IuF(>Jz2B@Bk8V`zua+!w@-o0vfAcY(weIX3 zZP}as)vw&H@Du&t&C2KAU9|1HU)dAtJOBM}2Hby|)j!L6-_6M=YABzcIrNXcKj{LA zsU)3C5XFQEp~eRkCNXpog}|cbf9lNAKgjb`t7w?SS5PNPx|$|SqR|Q>DGd?N4UTj2 zQ6*n*T7=Z!mGuv0IlbnLd`!E|blsom&FFz({6Za2h*~OyZ4vGS5Yg?sM(uZD3^NcK zD)N^8K}?Nn4f@t;BhE1XzU_AqOE3{#MLhElTK~k~&v8E~NQ*TT5ON=8;*ko>al@II zLY;D4qp}J^lZ-Lr$O-8cloi@CEw}b^9h=KLN>^@Tvwjs6*)XuJtMiPH$!hRS?kqCS z4H;G0sP7_75vM!6d+|0+v1prTT)-Mh=Xo(V8gsnPkP@k3j!uO(by#+`d3i(^+OCih z<~bmBBh^}O%4$CPTV<4@CRLNz96s+dY3RD(Jdu)_O_?@7Lcoy1vp_m5KIf>~sRA1g zHK9dABbQmzX21~1qP1{fb!^0>#hjgd5p}XYJ)Q?-{d&?6BKRr~35UE{3@2cNOQV)T z1v@(S&?QO=``6^Y{wVh{+4;EH^0Y?fQS!<+{Zj~0enDyV!B#fffoMM61J;2P`W*SV z#IHNPzv3)roVoZOb2`qHDUeI+GvTMS$JTaH=mB~rC66P_avDu}d(R3u2Q91^S-4w& zfcB9!FRR3*?f`klhd&)`17JyVJu+`=l8;T+JW`O+v!xb`f`_9de&3(d2LTV(Pw9 zY|lYk69zhY{>u>nspN*~H5+w>3l^$Op0yCe{7IpOm?C2{U;Xr}$cAiE2yhzF?6a^UoiB0kiRf1?rk2pz#n!1vg|6i|< z*IY%Iq8w<_4M<49Ned#fVOcCB!{Y0gy;E%5NuE*8Nn>wf`wSd@U3Mt+T>1D96=9xM zVJRh5{B#%!>%YLX$fCn%Q&sOxtiw;XFZJqUh#Jj~pc{b6NkGmEaCq}}aX zK-+g-*@bA1>&0?QdQX0Tmj+{ub^qLe1N(%t-KfBJTrTZjRs>RY1aymBrN;V3?FEpv z#lB+;3&|lu+d43)gMLWBJ{0Ns$XgQ4vKQ2o(KYW3;r2uxC!xHfajFowyzEr?ss*;S zMYN?Cxz1j%$i*n{!Baka$G%td62c^K>0^}ZFkcY}5I2=gl1$kTSr=fIr%iPd*woa1;NTbTy-mD?8efh8pI))Ef9;7XO!2T-^ z{v*MO9y0Hd{iauj?^yWXjf2W|RwlM24F4w%CMRggAq$|4dEe+8Ti(AV54FH#NR%Qb2Iz0x%U&)3pL~dhVDj)&vz^?gYJs4 zh)p;VPOQRr^n5E8mKL`Kadz5V#oe~f7pAS%7KFxGYfao9~ZnKEush(>Qnb15!?Zcxq2|A5hHGcH03$d-4L2a+KZZ% z?FWz9rXm!@kp@Svio^5-RMJYxWqn3!dA>>A|NTq!O&j^3PXu5oi`NDbregbJi*OrK zJe=FIJ+`4Uq0nW;8r=4Gk2H?rvEzH|snDUmXBF6-$P|A@^mRIykr^+PHX6S_ z;5nvrIV8~7bv4Qoo~t)!TaMM6pOQ~J$(AefOlYZ=OBeDUX%=hOXa-YyZVElDY8Yrw zsa3~`0IQ9~Abp5LsPaFNug+b+yZ!W5()$B3e|AYH-Xlvd#J(yPO}{WgoxePSWAHoH zqrV^X8W0h?)gvebx55x25M_kbXnb;NQ5~lv0%g*G=Bl6Qas!OFM#dR7T4EnvlPy7e zEf1&v>l6E7M*q256tCH-$d^j|z!ZsDv=~(;PL_-dIgLECbdgg7rQyG7ivKW9PwoEh z`R^{AUN|5iy8k9g|8L{`r)jEPJ7KG#@>qSSOXwOcg(tQ;7^hZLAGRuFl@P9$XAXv2 zaWvpk?ICp(eOFSgj6BpXYLI^xS(t<7AC3I{$z;PhfR4erkk4e3R+7#L>b(#81+Dk8 z+L!g@N(%T*5pUM?w!Pss!}ZkZbzSrIl1ujqy^H;90s=}gj3_8E)FRZSrKLrMh8&ic z)(hiK+DE<uR7mQeftIR9>>NsuZn_g~{o;qvIN{J40)RSIM_h2kr%?-5VgW{0 zQ>iL1qny1ZtZ6L8ymf0rswUpbyep`(z$gZ3=3p7`rLp|xm*{R0;ya$nlF z9finTx^IP+7UjP9z5o5uQnN3o3CM*z5$Hr(O|Kji)sU7lEQgV1AyXM)+!!4-sPqCl z-V3J0WYVsx7DHA718@phHR<|Y##NFA5ou6snQ*oja9MSH(QcqqU3EmgRlOdQ%tSL| zPu-PLc@Fn9oqWMEkg*$+<1=kP0Ucw8w^iGu!A5rXa#6LKWCY#@_dRd@=eHF!)M&n` zdZli10vbq$dJ^v{3#CJ8!3qyy z_D0JS*p*Bs;iFcpoc;x@92C?vpF<-Bd;QFjbkVi%ylZ1lzp!RcBy0JG$KDfX#wraZ zRe`&*FSyhIbVXTv<;KUl;@Lk0-%xfS);@5|XZbhJiqk`T?vbq>irt{S(m-Zw@gB}} z<#T;V%MGhd3kP+THp@77|3EB`pl6iWx&j_=lL0Zf%0_uwQ_560iyNbkadC1b^$PRc z0aQ`G9txH>06%{hnEr6ZeB9oKTt1>%$RWt=c_;;t?UMpG3$%=ONm@|6ymmB znX9c9340%)WX``ou{tcD*#zb4gUA1AL9^n2?xP64vJ`MD58L`ZSck%_9 z=DwC3KCFAFei(BDBbR$%&8ZX6DJIGrdRNXob(67+%PzUfU|gKY+z$zNkB7b$js~(T zQxg%JFz0pJ9{BFB{)j>;gY=QKX}3BuhN7N%oX9Qk55SK5_7==FOokbOFj#}6rTDEBDbm{06T@OYmCSP*b#;@i( z?i-#+rMud4ZNNa5S%HVX53``m=xRjgX?vykd%M88AiKf)99(|}LTvE^sH>bS_Is(A zQSJr*hPA-CZK!Jq)|+{$gp6@)%6jDC)ST&XbAK1j-s6}~GmvalNzo4w_HWsyl!Se! z2>7`N68FHzGfaXW5AOv6?q|?-;WgglJ0cIU&{Mq|_Z=aUVZ9%>3&>;1yZB47fe z$20PO2u7~)M+)W*2@^DNGNO3?`rwMaC`5~1BEx0LLpI;+MQe2ezEY3Vsz|U5ebbPl zD!WuP=uR&l!AR$iC{`TX!?wE|pX+JiA958MQa6Ye_0Yxwj{O^abzOTm^!sU+6XT()2js$(K-go}>|4o*S zg-B?Se9I!x-xSI8KcPn{3tQuVQlyfO-GVp@Pa=E0kv7)&%I}Ace3DB=C5l|itrEiE z*(MQ(_2lNaeX{Lk(&qR?0=yRq1QLD%gqe2&yt!?zdU*hXqV-D-2b0&Ji@ zP)mJwK!6GEQ+X06%Xx(^z-d=)iAh>Fk1%?r|C5D-Ionlwof^J;rD9ETMJIoiHtFT7tWBvV z61^vEr78`&hoEVT{2HYO$jaxL7xCDMY~#Uiuo^R`j~UenfdN#ED);V=C60BcfsDqL zB0QCK+b?qYNmaP)lbAKe6hp>LW}emoAn3mJ#g4YAKQ|!op#DakyTvf_Jo)26^@oUgA&YBlcV9Pg z5k?O+a!YB|uo0j1GaMkvXk9VQp{}>tf@jxV)p%v?pWz$9Tv+ci>YZzVqFO_1UrucG za{vBGz2ELHg~38UY>+ho7AgU!gj2#XX+Nr;#OlCE+1~Znba&xu+QexDX?vT?`P^oF z_xSb|EmO8K`D+Q6{VYRIr-;ug$k)inCG80UAx4B9Z%;TV5m8oz9d}PU2y^%WGBpwh zwa6Y^1M)CT2Klbgjc}Xj5&Twd5bT(O>}VD2?Btf*P_ajNwCgpF@d+=SPF@|KE5Ao_ z?zFrPDDei%2g*MLBRP=KCoZiQd<+#1QPXAZM|mx+EyuSqEw9W=IGZ ziWj9%Aa#)PcWiU-J_4srhM zfo1Hl6}}k#7)eOZRbWnO-d?~1twIWTceiqmUwLP!@(8+emAZ2D_xV4DFTQ_&FipqK zZDf8s9Q;opAeR4$(|s39tz7K?>2|7Cb~sCzzAhJW2KoU1p!vO(xT(!}s z4-o`W_(?kZ%YfrTVi)fsdA^%H^uv)ep_c-WnVxJ3er#`#9k0**C$i&Rng0k_K|YU$ zD;TRR%DX<*$qdK#-n{g5BVfUI_eh)08Dxg`-euTMCvL=&Eu{96PG4lnPLgbGB7fO?Vxw)hpFQ z&v4ce@CV)%r-5pac=d&BREQvUbWwsaPFL?&Jd( zfRxnMoGRBg4UAWeb5;Erdpm3t|tao4}^%STkjR+QG5yKh?jUc2iW&s9><5GWa3KPy9cK{yf)awxrtA(jpq z>j3L+2745y|8xdDlfrNn6nrmB}d8 z2*K=BS03AV?=)HZHZDft@%*DW6qu00uw_K(4iA){5*hM%D%$Y`cN~#yTM2s_j;WtpG`TjB7(uWVM;TiqhO_0- z5%@Baq1AlkPL$@A7U0wN9?mT|mh0x|E@11lK9)Ms^BF56CQtb?J60jI5(gb%FnL0^ z9AH}aJ9P%g>0`?plKc7-zC#;5v)uav)0*J-3~=iXDs&F&)Y`A$-(bJTudkp|#WLO# zzGr>44D{+6E=Y_THL-E!2CB?Tp(6e^VFf1@sEjIBqj$c2(1q z-b#vDJ=vF%h)Koku=XuvRr7?p*b$3cAnvar%ZQQ|wpRoU7un*vJC*-jo?tvpx-<*h zPBYfNzKSm0Pn}DMP|9ZB?q65#WFs@P@;rc9={`*|G@=@oRD9zSTTLj(h-S{IGBTRZ z(IK?TV)pmYknTw5aHHReZp}5xAeV-h$Lh8HJ<$~dC&L}%=HZfCoDI|2nkum|C75)HbhH*2!JO@SSCRfxb3>F)XYdJw=a_N`vkyw7 zaoF9re6%`YIWVYAMtjH;bR)PmfGk1mxQ2kdtT`xywTV@?ur_QR)LTMBKlIi5>ix&R z*zOFM#ni$S1(uImx(=iJU{F)?8praN&E||zx&UFjF5&&a8m7w#_SdQ zTZ7mV(|V0TN}1Ez8r$yMkYA*f+R~w5HD!f1T2GciCSXgd9k*hDH$^+;d%V{kS(>!j ztNSQCUD$T}9asiCIZHkJD%QNO%MLjE7_Q)c$~w7i0vz0=wjC}T|4Q~f&S(WQx^dp? z&QZx}brYZ4E5RRUXM})y%;ZjZs%+>`(3t8}vR=Q~E<1p3xK%s;wtQjPn8|rUvvH%k zv8iP2S6=BRqx#_(NNu3BRLS9gprEs%tdEL5qrMbZ_z{IW=~e8MVy9wFlrUqzncx$x zLTf3`-W)E;(pm$ZX22tB)1XURN$MXx5u=Bk(?BXT-vZc1<*G%iuedYVZ9=gHBZ^IP z2C$li&bNl4cS#OGt?bWcUF(qAE!|@WXz9HDkuY%u$p*W-%qSu+?QGX;BF9w`R=OR4 zSw@{0biJ63`^X(mKjkkCuU@q_%nYK1+CqXe3=QWkl6fG`?$si5%zJk=m`I^uLeEd(ML_~pAo zd4^IIR!gvv8oO(yf}=V3fs=M}YpsJf;XQ7XbZAKOqR=2?edsJH2GTwf5@Y70 zMlAmbBnuAdIR#>DKDyDO8xD`RC98Z}i`3B^xu`+b!KmC1T%OkpbGu`$LA)J*MC{A1 znW;3QQxfLWk{E8N>4rf!)YB_bLrAK|tqPoMyT1<0f}ZuFULiDCW57ZOfPERiK|S)Y z-XgEj>|HutDiKb_Xo<5OOWL}k$fi4_HW(Sp`S=XxM+%mCnh&d`i-^UGd@NYH{o>iL z#6MR+>!X!^{z1O*p4Q1+HF&hl8tZ6xP`~|C79Ga?IQqFzbIeM2%QbDkAj)>Y*4xmU zbo_0H_#rc3MX*3T`Thj{m=2X;7Y!>kupc-ad_Ba*CNnEI6f)p2byt0@($$Q;RX`5G zH25h(LkmGW6GHk$_JaxEeo<U%mJPV*gIJiD&8CH3dYy-X;^~L&Q0o&=TEt1(%WL zZ>$A87`Ts0mT09mM-2=;3FDEOcj-GrkF!_Lal$^BlPr+*H%k3tCR3EL1J zwiBwBR4Ak@(g<}ED@zjv5^f=ezbD$?2Os?e75MJ&Kf$l})H-8#+@0R1&mUt?uh>U5 zb(NmLjza#Ddd%Pyphr6$@R7~Xr}gdu)9S`r;=s=oM`kC4PtA&cI)CmPj44icet{L{Xg3in$z6elZIMGP9{4{ zkaYFMDTpX=g(%_+DCSd)kyTlPw`2&!9VA%C1Xl;ALJ9&_>mnn{R=LouBYzeA>U!Gt zy6#H#n)V>K@=bf=PCeOv6Fc@gzRyYLxK8&lGreEc+i-&>9M)3N=qDvNo)9_|u6=Mn zDza9mBv)bx&XF$e7yG^f4P8z#p5Ut?tqO&@(8ZW{n7>v~nH5hHI!?esJC$J4kX3=I zJV`Pl&x*EZjTxT|t*St-B5XR>xCX77s0v_DagGT%5QkT?=RVU0Czp@(ken_Rg@MGY z*%c-&G^SOq6CJ~1v_j1)GLD+$1Sbb*$e9KPC?!QDwu$$ZOVW}xD)d=M(h@S|?>b}U zON^OT;OFoDBuzB_O~I}-hLTI?%#l2{fTmTv zb7q{8x35nyOK4N=BbCe%IbMUNRk_1qT%l~09&;MguHAJc^=P_B)y_WzH@20xFHS&D z@Q4qnOzKRVltR(2J}frYE!o8)r6a$G24A7_AWc`JTrE3XGS)5JZ#f1CfLQjlmxsbdq+;TpmOIOav675?h26Z z5a0WN!_S@KQ@ju^@hRTaCEzE%ga?Qvy(dl1pm0|lo*4V)?=q0~klX{nW0f{)lix%B z>`=K2j$Mzw>jHPrz(eOK-id+mQMzl6<&M3J^f5_(N*>!m=cwGdF@DM4WF`0|yrc%4 zB)?8Xz-UP-xM@ZGwML=DyC#g#|ES(ACW3ix{GLN7@GpzRW!0ko4)syR81SZ zD%`Xua2UI)+=MhRr*ENjmQ$CsL2oOnx-oiDxNB+fj9nD(;+kBFnA(9~Q(4(DzEQZ# zY50t7iuJvXucuD>Li5V2`Y}RKG<&L;Di)$OFp+vFP^M6+uwut<}K|%gki#Hp2n$J%$P%5h8&1l^FX7C^=Er zm$^C!5UtP8CP||VBA2T(BHB=!$B=0mjs=J>?5h(s6mW#l(D;2Xa}hw;?!TK?^wR(n zZYkg&)x&^NenB@*Q&Ej6roImxrMjns6ch9p>@PkNA!zW~v(GEC23G4_0Eq|hW^AIS z?MMs7yns@D7W`yn796gV1O#4s=0~U~;FaJ-)Cs8ORn-w4(!ouBj)dbIE4g>e=NWT~ zoznF&YIDG9p}c%81>n0=z2^goSfGMee0mI{x*2%X;S`QRze#}(rwIJv;O^3K*NzvE z4jLg;zn>X2{Y+19YV~0!ZoCnIBCQ^j^LZA$e9eP)-M~{A!oCe1_s+LOpkk*v1Z3@v zJW?ewaI`d9)NGg~`-fu7D+h%++nxxzjM)+%4C~w$QTfqhltDt!?_60B_SpS^RLiHk zD2;k13SN*1Ttzo4qn4(TC-!fUbXE}gv`8i;^@Z*p)w`%H3i60}7w`ki9WQXan8Yud z*8Xf5SM)y4)ltAJaJ!v}d7g+U+c3oUa*qTx>DEWs*`_K{S8*`Gq!r2u1*%!N;I|N& zl{-ho_NeRs80E+0>F@P63GU2s=+eiFd||21+tI*g$F6A@C!l$No-Bza8`46AR(_$F zt%{KM09)zIT{aUrYSPb(b3BSw4l&O++(9I|vNr+msvzeE^rkzAlsIh@EqHY6#3PUn zMa-gj5-p)lKdFc;TvkxQ>tA1KEPyt-Yp;S9|NcG(gywJu4O-0mvv$Gx%UZ9*G!WohcbN{M zPb5BH!sQFai+Ls9OL@PI{l9ZaQO^uJSvk`-#{p-)_@WyuN_4O&sNi1%#QQL_D}s9Z z9_;(x0S~|}D(xY+6A!2~H^Gur*BXAGk6RHZer>yu>Oujt<;5QEZ9Rp-o=7uZG8D&2*1)MFYmu8i< z5M>N)>uJb_nHX}yO34D#mip@ad9s?}Kt?Xtk}bmy#Mc6i!k0+{vn?(!lrs{V9WKIu zU%C4bWmwAWHx_h$B^p$I>FH9?hrZH&?u4E?FH#~#F8$sO8bnGfsP~$snfYIwy>(Dr z(bpv!+}+&??ykYz-Jx-JcXtTx5G1$-cWWBA#@z`JBoN$(-}`3joA=jyGgbRmpQ>B8 z`qr&eYs=Ytt($zajmxpR{lqlt#G z<=-H0jL8&W%wwN5p)P5Cfma&;DCrOQ{}l4pmL@keyV9ws!yDU|*aC}QPI`n*s<$)S zmd)>P-{>Q`9+9^#?rmI}r{^{_TQ-!~5V8x-Gc~z#>~e4_dOH3g#)_f5;|S2#aBRfM zbMyO(xdW6+pVAxA5++wze1io5{)=!rPXHJ4o#j&H*ziZ zeo#GBdK;`SV;k7nSEB?Eu=Bhk1g~ASW4A%mJKD-+t^8YC<{DBd3D=y50W<^~kFT`R zJg3R&>+o-S2S4|`e9cvVb5*bU5Z_=(g{nFmG@f)e0BOfMDz!tlc~)`*Go2DlP>Zd_ zHW)HW9cqwe()GQX&2T`ixpC%Ie4nk{zZxg@So7l`4womRuy*l#6p*tmfUy>OP_)IJ zQHuBgpxmnmd`TJsO6wlBv;DSes?V>ZxL!LG8^ju&nigR_WeNXXzN@lh-A!@?~ z7v#=!v!^RO`fJXd#6O{N&yYuYy|K`6);u~zS9s^w+#TdSz9kTr2hpM9dlAlVb3DFM z;~szq_17LI5AC`Oy`Ylh8As4jEEf3VQ^dm)=B3z`hH7F|>zp@ss)mGbEy{isEzzDp zX(XD61ZmB46TmTemKb{r%VCs8uroGYvDz4$2z2O(P4wm2oEixra@u?UUHVX$kkqut z!;{oTF+H0!Vx9N-=*6qcPZWbyT}z-gX}}_#;yh1oSq_)nW#qW7$1fCb7d~!qW;Tj< z>P3BQ`6t)Xs&YMrSDo$f!bVhg{-{qxg+oUk10rLUPYKS(<*uKjgPDC`Ad@Xnx!T{} z+!MSk?Xe@JrQ%qNfu=-sxo2aa=g^TAoy)bi%Va`~K@_T1n2~?TdF5ks*^> ze5jCYaU_3B7aeb449TApY6n9zAnQwe{Ride_Uz9g!Ti-`27_`s7#NV-?DGVA;? z_4mkXsxD<@f#?hs)m}=G%O=)g#9tB+Nj<`bh!kJ^LsXXy3hWhEa*VSzkkf${FzMiv zxkCqDU$p_c69YI&8Nq%IS3Md^BU#e)RID!1)8pluwX`@jt$R|arJbH;<~<`(Ssj@P zd)G;SuQ*oNa>a&l}VSMPwyQMEl$}jMnd5|nf zMl(PW^i?xJ9%QWOcr^nQK;D`GDj+n?S6Pq>msv(j!Moy;N#$ck z+)m|VL7Q&-)R57;s0vC?%S90EO|Fyt>sbVgNeBEwa>WS5g|kRx%d21)<%O4!S$84H zR`v{#CHTc9>11xdeDFbHq3Nw;seMAB#jQnY1-|goiLJB>uU{0HGqIlAFB9CH zSZMB10CrET=k+TD-y|BFdW3_K5{)fx0i{{^y^AN7(yY9GkzmF|W6N8+(iQyP)XImGW@r>6B%hyp66JwN@9@J zt$V2n{@eVCj&wiIb0XL|G06JXzf=_e4Sb>^9nA9_4>n1Bx4QLKr#KBt%$3k_(C)EJ zo0rPN`MX}}zhI;b-^*)sruI-m#4C)a=fMX5eNC5$E`%`tyd|Vge4DD);1_HYRwii> z5AmDhnx5=E*;nd?b7Gx$lA*w4my~{ozpUM+wt~jW4o!@>P9gm|X^h*TNKgSYR;EiS z>KNI!ciz<@5O;5ytW&D;b-SLKl5uC9vZ?CC!@w#bFcNQ4$F+7v^6Pci+&&6eqlf-E zrd=}lnkRXStQ(|kiF%B{JcG@dGzz-*_)L+QExeg`Rf_kVi!ISe%fF7UP7eFP@|01) zz45Z&yJ&nD79x>+Vv&9#m3|VDeu9vGDn4Np8A=GAQcF6}Axn>SQ>C2{Q~t~xelK2^ z>fN~<_6mQ*5u=INT5g-N<_~PUIdfW0x=vJXr~#ev)`%pHMG$jzVz(WCA5p?hD-%gp z^x+{k*q6HK*Vz+Pm3f;lnT#wEOv<=xJPF7;(Jv@%k9F#XcfjHQNXSz%52f)7t=U?? zJWiOV0z~&VK8U$B5|n;18XH$CxVVoDv0I7X=z*9^)?Vls3NY_2nDcn&Ny)?AYL6o& za=pkw-S`Q8?nQF%gqMkjCs(Y43JQ8i^CgM+y(P@GWo84k=*wvvw^|o^0$vL~S`P}+ z|4BN%1UvZ&<(yt$l`rwoT*0Y~yYQEUXD?o|Uo4p^QgPm_^|{`cZxKFRH2$KI@to^e zxJ-QUp2H7Tn0)g#7!J2lC&hfp&Ty|9qnTW1@VoEqInY85-}qUzMM^F+$!>+y9Z7sO zOG%nYpyBpey1TT$chu3pU3VOX%{_G`VFJ|w8n)a8o5FcD|9$cOvCwgrAr7W1NFH}} z#Od9*kD4x-G#b(>Xl~EI4K&R=>DD_3nwsdq-kQ(t6hJ`XBi~vKki&-e60k)Ks&1m* zPmV7b)j9%Jtx{%PI!3&_X5b5s00OC}bQd)GbJ7pi0{RP4shrZ@UMITc2OhX!s7>W| zI1b{5W(3l*i~~Qm1%xgvump1cm`iFKf-=uV1zUc#U_~UWb?$PKJ;eg$R%GRln<+aG zb1P$A#W|i~;8wLRc80ANmLT(wQ+ila8+JNp-5md+ez{{sc#fikKvn>-t44xKkvchsZrQmX!Qr9W`%(u?;b{ka-zk$#}v(dX1o zFe}*3Q&(;1=C4OlJm^B}WU-qZ1e7Osv4UyJ!=Zt+y=a40#`rR9@_zymTG`~;?Uvr) znU~g-qbIXjR^DRk=L(4Zs256azm&-2nQ3zv{)EF6Dz6J)QcPY7k0G@-Ot$t3BUQW_ zZUePNoyz&agVQ!n=oM9#Bw`DHd+nYg&(6JI2sJy_wkpof`%2C_l^(MhRYYS5w_o5a za|){*V+rpJdqMzF{P+fuHVjz-e!ob?ZZQx1=z){BvSC^N#6B=U#yh&)>YDZPKX&CS zCy*s8x!gS|d!By5cEP>(0=KZ`ynn!wyg9{A0H0=3i3{hw@vaQ8qaV}L&(=}f&#CMS zKVmR*`JHyu#w`T&U$%qIahGn9?_E9d-933=RN+GH!u_aH3`NG8VJ2J|8W&|WRcI>8%RCVwVU+JP|?i+u~1bZj+tt?c%e1iSh$yXBcTh9N0d^H-) zEm7$X!CSF4`g0oCB;kE|kSoS;SBZrBmtms^eN+k(OOz9wp9g`mJB!Z-Zm@MUqtT4e zq|2C>&sU>C;f>8$*UJ)Xj`Ims!NxpPGh=rO8SjH(@p@qQ8C#zp%dP2gA?o>0s!gCV zvcI57o*@|yCRKk8z2S@tj_{N@e%%unFbGUN##xx3x z*e6CLAB!7Y`z`zMYFuDqMq|~t8Y<9_LQuutoyEf^rQXb1R1ib*pKhwLjr|j7;svDqzE*Re(3qR!cKdswmMDy z;71iuWF7y}iJDu1MZmQqhUB3EOLF<6(rM;LBx*HS#zM6&93YS*=80Op!c4HZ`=dkI za*h&?dAW{Y@#F`MpDip6@nTt)rD}6{(%j_2cMsM0aCJW^*j53XDiMzGOn#eA5r%MW zz7yF%4_K0)I+x#Kbov8_mV9>3@ajhtwnJRFx94CZYp$hvs0|Z&8(&?xjn<`0$T!Gf z_H?m(qDf-HcpJjiuBy2)U|4lDRIdcIV!|FO0N6e3FsfH^*ph<^YW#@XVOAx(tzW~! z9`g0*e42B@!@P5R>3n?kLc-cJBI$gDV~ckq%OGH!$>Y_oCQ`l)DvV&z`TUYC-n9e} zAa0W;Ne-S3$z%7B*Gdl(4PMjv!0CpEMZeS2`Aifgg&jZZV+)PViVddu3nP*);r-a% z3&21u9LXx#ee#DvEbP!J-Nop}fI(X#QoX|NMSz)|Hc-2&-Ao9JKG4P%Lj5Z_2zBLz zEp+Quyi4y*hFIwEN&ISiPls4&s`6vE>JAyPP;<6)_w*JMu~4?5WS9OH7O_ysxpY_U zh7^&Mm8@hp8)A4h$$0Dgv z&4OY{cV}Dq%~nIC5na26Gzf&6y7m8|M)Rdi((pIH6&{d)of9)w3aQ{C*xw) zmMPU7>u}v`@Alcwq}f-xWTu*pv2W^TL6BMK(cOM=mG&LFC%H^^h8SvRaq-#q8I|q@ z%zv(}t-|fQZQO+ElU#bKQtSUpOP>TN)Tq7R@TwA@PJ4Rme zJCa2QM>JettF*seYqZ~2dp!K6M-~Uy z6oi3h<&6mxWQ}Go&>389`#@jSfWV8oG3melN4^~Xf%M;J;QyII@V6jybpAX8ANZtd zkpBPb_;T}f^00NZWEC?vw{&;^kG6@Mv%~+CIFU9p|I{||{jWpupV|fnL{j)cUn`V= z+Unmq({brh;Pignf78H_{Rx7NLn+Z!k>D6eopvhRQYv!$oA{3G+;cUuEgM-8*mHkf zbc=R7@jPus#s1Zo3-4xnre{ZR$Cac1!}k?)KuMR~1E4)NiRvzYH9{ZWwCcHmNb^r6vVZIL30}k z`k*gS_|Lht(jcfZ0_k_*kwYnzc*jo4Gt5b&ngLIZ^(NYAPn?5n)hksHtVp>ct3l>j z2M=qe#$}7l6IWpk*!z2ut?b=2 z3pXkT*I%1Pkb1^k7{Qp!n^aLOe@dj=yvY@CHXmQL7V0>}T;)sNvEbDs}P3@m&$z+<{FWOG{3#= zh%?TuvP6HLQ~5`1R2eQZNaG9_18YKlMLbw!J0p}7)6SXr_wY_30P_v&R9cp$kh~=@ z!o?5Z_~%IcO1hGf>lUi3yi44@9herlXM+C3GaM9f^9Opu>jb7`V-SmQ^$=smG6_Ac z0=ieDYn^zei)O;S%hAXt|<43~GU_A9w-P zIH{OCvYLO`XQ2A`>%Js`S*)+W1OX%UpW(D_!#!`Wv(KePdG%>J-_rB6h0J;sVpA!W zgcgzSdnrv)Zo;IPMNoW1bK07tC<4QXPZkX8-*?uGt2^>_FQYr_9$ zkml^B>h>w@ z?v11S<%r-&6Ke1<*={mN;f53X=zg?W+P1sj^R4fUyWfUMfBy$`V`w!Bk^+oSQ_7$@ zkuMWoqxt|txYonZhIag>rB+%ACqW0KsI@3okkh6!g6Eh4gw{mj+<{mcm$qz!JRHC<>hT{d$qnc&Srm*_i@!`eA4dpz5v9Wyhy5rbP9EF^nOw z=^r4ivZqlrrq$*jIc?}ZyktxCXZg!ZP1)R}fr*;f z8Bu|yy16;{ET6_-&3rBHHuEoH+{Rmk_cRu|5=^bYl*uy89(|8aS(3ZytUPb^8(N4E zB#owNt~cp~GT#<~H$RH??jx8R;(N@&D4)L5_7lT@YD!D04*81A*-bf5HYA&+I)Ds3 zV{9|GDa>Sx=Z{LHf1v6;kShiF@Z(T`m3*X>n7|J|H{+Im6-k;ZYx9Hp5Tk;o{11eEv zz*w?b0zCd*F_g~Z6)f4vBikhqy523avZx~)EiL<>8Tz%fA<$Dq$;BxbjWa;tY!rt2 z%?Ha}gd?qk2^8%MfduKqM!d4ZlitEzrJD)JMz(gM;W=dHPt9s|kX&lJ&Bx5quJsh= zq_NNG;m~AHnV_A9k=W8?_EgBJ#no!*0almmmm-JmibTZrIOIwc!@xd76sqCpVpf+= zVe_tFQ_(aW$<61^Ew}}V@o0*8NAz`9r_N4|<+TkJijH$=GAoEZ*ItAH{H<-OAT3N8 z-VQ7bUDKJO6@TfTF|0j}%-e#vJW2cTSGYipbvU_6G#*q$V5GRVlyNDpHNzE@#D7x$ zONRl&bat6tRgx2^sjyXq%U;ePDl~9Q0$`niFyf>gm_0Y1RQy&XSf0q&jY_;Z=Hio% zC)sr9;iS*9Y6t104k6F2Fadi}tNEp<%!<1cdPPkO>M}NKko8oGs%}^_X~m1}{bn<6 zZ#4m+uhC_Tpoj+(HU1kY;6hu3D0hscp#lYmzd`)gSLao&gWd};(iL1*wqa{ z+NT+;S3uH_TOiARyW)IfXYfb*7qQl<$4J=q$2qm;nWTGvRj))KC4RMQEzq<0UsoX` zE+s-98<(PrTGnFn4Ex&7@UN20Hq8QUE&DvS-q^LZvl}@GGMDY^!1{M?yZcP+@qojs zD+CUElV8XWVKT$D69%4LHAJU#*2T6Qx%$l9Gj?|D1V6iXcO3GSuP3akcC=Ar11jrF zff6^l%5k}6;p~X%z3Qc~VcZ*+`zcB7ntc;kUHN;m7uEDBP}R7ii1XV@JGiyd2+SA( zZY&}sDj>X)nqNL#u1m2E8P=Z8;~4T<;t*sU`NHaN1b%um8ZaD@NQg?tmp?4Q0X4IQ^P=-507pln;y{I^brCUZOrkyVo zNf$%BC$!J-?_w&CL>+RgHXp-%%H9>n>`DGt=(9r7X-Js#KGoQ{o#F~|dsuicf5x){ zljpkPu^v*kB@p>!Lpf60dDIPHPLZ@3IX(0J#<^3p+4`Uc`L z#^XATVGx@m_`QDi9pTAx_w>E~MHJyLs#hV~eIypwoWjBVspq_hS3C`r9RG$nt7XLr^pZ?!D#(z8H|H8&;Ap{@I zrKRJ|obgFTQfg$mv2P)bw%OFs-=%7yrJ5EQGcn6UALW7hQ6LFPr0>!FexRQy^xcojSwK5zvL<=HU0u9Mr$|K6uI9 z8weV%SxDXoq$qz~!A(1Rzx_L(x;Gz0@V1k87Oo(^rtcZEkRAfa@LqgJqVaE|jaFlm zug)M?P~BW(;$@5;@kQ$Qu!Qc+oP5=R6^0R@h1Y+`c(Yws{6a&Mg{6BpudgD!A0hF0 z*^;7*S5hb@^H-L5wL@gC3yg+6e<`ka*D1;M>}_7Wa5J&nn|%o|FapCRzKB_J#4@QFpAP0tOoTy$79Mq40tgN71-n zu{dw3@ZJeNhC*I5#_xFX^5eVDDKGNcYU9X*SV^=!v>G3}HjEh_^ z@Npn*2ty6Al+A#XomSp}aF-6r+&M!{g_Vc}dP7Pe3Qr1%8R+wU1H~(4x`F?dd*yz~ zd^D+ZkmBFwcUoyoGY}oizpWp!>@4is*c}u`3EtrviqwB%QU1zWZjnjU`DE+O;{{Cn2x$k)2i6Uov8;)sL{t-IEE zhU#Tz6U}6{w!f~6!LmwQ)l#k88S#WQ%iMGDI$Lo#@hjZ*^$m941_&c+ZOAciTk8=e zO9ovktIAVzv*tVET<8dIA^|l4btDj@>3D@*2elGZf5paX>+KwB_17Bf2Yo0H^+Uef zOi;A6HB+o&{sRe6LPRXd2~O9c$mq)hFGA;NKYq`DXYFDqM!=x`^^N+_o;lGY0x;P2 z%{D*x4&x7DY=JPC`fvVIOOjJaOFcjhl*$kh*7~MS(=3eXfHV?j0J#*3&uryrtIF)> z^H%=Ny|(Mj{`KrCc4}5O!3MVuSvv5{04k_tlMrC%PQ4qCF_DF%zpG0jGh1~E*AgV6 zLQ32VZ|*O(s={Wu3@Z`ZmDF|@DoX`S*sVw5#d-;(hQ6&Qbj*XYzNsL+N;7S#k;7|$ z*4HgGz?PaXsZO;FG5j0OW^8muxPf z75syHj*{Qokz*!Mc{xyd1gj|t1r~GxUUo^q2czF`I~!lx>j>FDLx%kR6nP0Jnsus> zit^MkFDYiUJN>?)m0zh8nza>pXvlizWF{&PU{VK$@Rk0Om1#Cpj#DQXVH``*V+xF- z9KGZ1ZXBg_Loq4)S;~wahfx57c zkVrU32+vMoBGpJVf-%|(T`r4*ZtA`VeaxsTC}Wo&BAmTVyz1((|5chdf;~=Wu#r#?%kFOs z_i&|3UOu#(ob(+y`ghIxGO% zxlo5Qa<3QDdtMw@3y2yXMYtcTg~N)-KB?7*&!W4&9QwNW0D1JVaLkBMv>Zh-%6UJ2 zTxvpI=^=x)dUp#9lols)Gs;~CAj#srB~Ztf$*HFrH2c(qtAa~;8l^j8G<`leK>3zw z9v?dW!>TfUU2a(JD^bZ<(~|v8LQfZcOmFdrlO+DV%!7p_Cn zte3cos{M13LPd60B;ezj4UA^?S+7#h{@RbEc(=vL<4`=1)Rm8U3#V9N`(1`d%_6}i zFs=!mOeI_$J2E1V^bxU)a8J7*+U40)+FErWb)dszAc;D0WO9j9&MTLfH}W;~x3)@)kMP1+Tkk?(?FJxcY; zPT~9J#s=e>QYxWj5lwbPLkvXKattrw8sG%ebb{7UOrL(_rDgOFp63!zk+XUjgIu4j zBIvwXP_OZqjfp?FpM9@`-rU}jl<87(^lnWT)f98>pCnOeiLvFGhg{E)Ur6T9k+dAj z{xWP%Bf>zw+b9`A0-<|L8_S~*&aU8=y}yUXqL7PRNEtJsU}M5bFw=jti+|GZy6mqk zrZ${5D;eUU{FlCHqiJ?qR2oab!9s{H(mQ@;+Bi?6rb7q|Yo>xYaW7@?cM)2K%HA<% z>W!Z9_5*Ef4M&H$3d3Vem%6yAOvGLC3LcU9-=TNN15sS=+c-s2rse7Km(q(fE#U_R= zDii9zC=2C>RWn31+S58DN&28;X1DDRg;;=6ab2J6u*`H2&abwdJW}j*G=_BW9{1x< z7OOZtcp9jEqD1UwPak{%4Cx2@5&Kwb9F_yr&ELRHA$!EpEm*!R zq$H9qxQwnfn(?j#-*m)??Htho!RPy;x=dQN{z4rcrPr9jc$qOMhW$%>VJTRk0M5IT$j`j>Q$Tk|$jWnflKHQ1czAK~UAL)Wp==!BrulEHB~-vP z2Nre}*59XnBcK(}YZTYmq$AzIhAcN%0=%6?(2-({v#V=l2Zxa%h|;x{r5Y2++=QALH+FP2$?X(ie|J zzjskwlbJ7z_YnKyc`bbXMGV_ACey4g3DhEzxrWiGAxcofLLQUcnJu!G#%!n8>Ih8{s zlcs)Zari)wasu!X!Gaq9)pHf@rRt8}$uBpr5DaQ=`f%U$h+vp?KYDRs3;Gd{H;lFb ziXVnSw%MmK>S3x^#uEk9_oT8I9~~!qUK6HWf9-mt4-P0G+L;5*x)jp})yJE}@M4ss zE-sz=5Ej?L{sK@NEZ+rF2$jqw2Y^Ulzs5GS*u1{Pl*#A|!G%-4P$e**b9?RU*$k-# z24r?)|4Nb_^C=!)-6W=z4FgBro7yuLX^yJ=NDNww#vwn{RVlqPHb5I$YTCtnQq^xbh^9x@C$T8Khoq#{I!`RG z0$MKIFhS6nE0gbsJhVy!+?_=Nz0;j%%ZRpq(ns_oJ#u4=HZVkzQ7dqoEw`ra$!EIk zK9({|$liY+I#)?&o+4w5NAFnxZP&oTt=Vtn8!H0-SB}o{64EH9H8@`m|A40Ej504@ z8#eh&3yH>`Q~<-DZZM@4l40xzkrXB)?RQ;>J+W(iOz_f3bj*`EwNrFf|-VI+q zQ|aluCdLkRL1&DrMP`T=EJO8{fr>8 zu_)&W>mI%7f5etQX^@KqLfjp3CakJ~Bg1;XxuK?&`x1cq{#-t&6xpOmMCuW$x${g~ zIFoqey=eR3IayN)a!hz`xJ9S2b?PK@$Jughh)e}g+A#l!rJ?Pt6)GSrJ2A;W5Dr0$ zZShFcOHn0Azt4S4<+4Kv9iFDG>+Mhi)4C^~51G&;o7$&s=CioUomj+Rd=*(^`@tcq zt(SyqGHfz)+1dA!aeXgR{3;&e23vXrTOm7wS)dacF6k>{spYHT84Qlbu#{4$F#w(g z4i_N<0Er2PPw7th))ymA;^t*k4EzGzT3-o7{mYPLhL$mq+~U^f#Y>FziB>Ux9G!D} z#_OnBsBb8s$!7#HKeDO~tNB*EyYC?3pSXrW+lI@RC=dy(Zy1Dg{PTKwebD>q6o9*dexs%t;xdkxmWgKNe!`ctfsJn_32`J> zcNRFEmv~hlqHR-r$g|6O;%ve)^IiU+E6TLy*vws#9Jl3Z-L}d1rizUep7$ZJ9^d(? zUqE--{0}I0sD+O-nRUST=GR87&1`@f`PUK&AFQvd%iDx(>PLOv-Dwwe>V>#T`Hru0 z(v4f$mWc8;NC&}coM`b#kE~k1i!s_b?KW}KX2-Vz{8Wr@ht=+?(GR@gn_=5Q zWw8&Q*P)NSNqH{KNkr_G2e1xc@T_-{NsAs9UbtL_ z9`@k4?qU4wAyLk6iT}~XEN!cmbR1$dUSw!|e4pUbhqsBvtGXp5*`x6x__iD*`K6?` zS6l&J&p*SC=aGFWdacSH*K?v|R_~S~p3o$`s4@d>&V$FV{u&p@Gb|8)=w60=x*M%! zl;X|I#V?)|Qs)|C%m?4la3!&8=8i%-5Pl_^@UB=YGo|4B8iF2Lt_`saAJnG!uIB)J z2MX=Lg({-=0C5La^&Sf!f!CnjDl?dg?MGPA&>7}5_Z94t>T-~DB~om@hN?R*w*>}a z!{98A`p1uUkIydQ_3$4d5~U{u^I$S@m>4AKZ-;?iiGo-NC%k63N=s%alSI5d{KO?p$2~bqZl$) z(>?8yL(_=5c@}pjQFQIE!?JP0$s|~3wLq?2c6~l0sNz9n$;6sJWPw&C3Cq-@!D9Wq zrw?3jg~dC~xBf{%VGRTL5lsa$_+Ykx@9^BnZO&wws2jrf%J^Ue--6vWh*LeJhmfr{ zVFa8l$@~;SOs<4vMui)`LO7I%kV{upEbe56g~RU{q!;iV!nt1^vn&nQMaFhe7Hpou zq%R`)?&u@y1E$gJD!9R}=i!idQaey0q})!twYzF|yN}^)gRtYY**^sM&%MM=TOG+1 z9r)nW&gU#=D2!R_iwP6c=Wu=HZj%QjgV0I2u37L0Ro$U_Nhm|fYg}H<*8Cw`KfhrS zS(Eo5%a~@b=jBlb^g3K*w`oUMhbl`S95GW6wObMuoAkT`nWA?TS%f5G6oY<*Z+6 z=!GbkE=!tlCn^30V}@oSb=@a@jP^d2|)p%eI}!d2}qI%yS7kL}jo}><#ld z5V5qv$5Zr^#E3T)BYHIp7qw6>+`h~N~4AHfL||>_>WW@HXZXoZJ-OKZ(UkpM3aE?xCGnRV1Zes)=oorS4Yy7C^tuu6_2V{ z(jgm&L_?o1xLbv6jtI;JXgQL|M0|5FK(**tKIoZ&Vi&B_J?3l}?|rt_NT=0!;2yda zusIuw)tu$^)6V9x&v;ksoNu;exEJH64saNVO8ZLpRb#}ensRSFT3*3L=tLKO zjZbe#a0lOJDAQctAc@|QzZ_{Q6m3sMUDIus>d&!kIVsuvU#g#^!P-E@6UzOi@!V~{2q5M2McG-eX2{O1bqTON$`t;gHugIq z+WUHrg>)GtA&_~q&$5_?fu50O?AFuts(2qGWVH+qu%`CrmP7Y|5uqGfu-h z(F7=ZG{@1l6aU05*T&GW#9ZY{Lx(MPSf)LAisgoXT`imZknMUkl5^otwAk*THew(P zt&UjjDr2-Kx3x)UBQo^JVe7`8l9%(zap*&+E)UX&nO&0K=TkK%&g$H+_~8d9B_{<^ z5%>JcbQ+N>%$BL2HDZwEHPobBxdn6}7hqMvzh*zghESo>5-2FUMZVe>$d0le68}8L z9uK8GF>oBxXU2iIyr3eW;N%_+GmlK=3=hzdTimypnsgne$6u*+=(AKTAn0ANK`oqR z25%soUJ_s;yY!k61;jaR&I0HbQy|&Nlih(O-*}FPTjKHRGwAbS2YrM z=-$XeuiHkIxMo6X=1_vPNNuMQ@(onC35}tg>6nG*#4{PuPS}9YB^DZd5enS4_d#_A zi|LA~OV5-9wN49%8-m8YR_#md2GFVS1;|m_^-QzBVJrn90HGDr$`6?N}qP(z;#nJxP?)sowTS`^ioQPQGxj?5N&pCI2b;^HR~LHkA5k3|?`kod5aL0s>TtzB21ezyozr{%O+ zroA(yDavt);}#lJ7*jJul(^y;8+OLDW#J{+MA!F#=csCyWlSVwwi&vlh_;pOl$q$J zIHHbhCCpb>?&kw>W^{j>qTE@>dUu6=ts%oleqF(QUBP%=!FuIIf8`BIx$35;W!vr7 z7%V!rL;~(MtZiTg;6ZGb71kWO+wfjFM zl~_#$;2TG!xr$(Y9(vBvJsQ+8HN)q79!Qa~f>JeVQ(OUGHA(?jL>@*KmGbx*1gYf7 z9)&2C0HRYYmJ$dd%qY3XCG9YO1B`Xb7*4ZNpy?N?E_4hT$SL9vB)4si+-NOR>zx0e z7hOOpB1;YFg>(u?aq)|F@yo%#qr#7G$bCfS%}KX6QxA<*`7{Wz7dObFj9zvGutaEc z=xhcT5oeXI^q+(AjDk)qX)v%ItI0BK8{rI1tT|=UKVCYmRQZt?c=C}>_auP5N0iA` zy&BQ*X}a_YnL8io@1#(&HP@|}kqve<2_*;LBeN(85zF{hj{ zruvEDr>L0`vPtL%h4cplx=NaVGc%|SCUB3HB#`a=3Mt9?VM9xoix5Xk;F!i89?<*k z3mj;jPNvQ$JrD80f<9@uJ;>KVneA=@%Wqi;i4bKU&ztViVLh46G<5TwV*+k+?uH66$mPZ zT@JW1g!iJ3D#RPYp1D8@N%i6l5a!c8sFS+x0l5Xpr&~xk%u~G36jUgs8v_dmBoHFKqaiz=0i0L4V#m@Y?FPC zhpvS|Ts9VC;sLJc|E#yI4YKw`GLnjw+L~7+pT|azg;`)i@-k!kP*GNJiEC08%+Sd* z8m4zFn`wkA39R+6EqMKMXOM(~g@FlCdm?JShvy%1tWNGg8U~t-Xi%1;;Z*QI9b( zZ-lCTet90^$`5zX$d_6}hLU-c0`(B6 z^cTbJGS=dOE^jjK^`#nSN{W}q0t-`vld;9k)*kkpWu`xEf!=PWDdsQ$u?2Tm9JJx) zH{mWAb?7^H9x(4_!mF#ThtD@W^bIa1A(D#}k0vJ>U1)wX!2Tt#ftV4wEB2-;Jia!N z)Iiir>4renZ>Gs#=6F?buSRg!aCz&((smmllG*V?@um#Nw?(<5k*Pp1i~k9kY71A? zi{RppFFvjlC}pRzTuLC=G$QSo13VCd#1c~WedLP!eXlxj;5yId)V+GZ_r=8gV?_Jn zOz<1yy2LdsYdY<$l#pH8keI3J0|#8-%Lr;Vy^1=f>Up?=!^l5lbI+ghaJ%z-#Piq* zv4W$4E^91f0DE$S)jR#(ef1_p@)_uET@^)V+R`2IHZv+kbTK;IJhM!j$j%ZJeJS|1 zcs4`knXofSXs=j0A%?br1ps3J)65{TYoWy~|L_k4^i(gZM97v9O}0ew_*PvxSYDFf z;#k@#=mNz!h)0*v5a>p}`WWR_rgaqsFb#A(x0LWMPXbi-;K()B^8YRRrL? z;kUL|as5+!+$FkSl~J%>$2LUt(YyVq+OUqy!fPTfbWhVHKawZh;lCJv;XDd4|U{!Koqzz(6gW#P~BZ|>% z5%$NRwKwI5J5id33%*lbhKc`*_joe4!9>eVVHyNro@xq7b;fEe@$j~1$oJ23FIx!G zjN`<~x>J~>a!-r16dqLa%LJI`k6WI}*J~sMIui)wo2H&=y7kt$CZ;adEYjY2)C~LN z=uXnqal|v1F?gArG7CuV@oCWS;i1s`X2XN!)ZM-#Z&GB_kv7ZTdl=TVeX9T}P{YYK z8I!AFYa1UAhkm^_R`_zWH)8q9yI%2fyR>&D-QHVM*tJ zprvWAW+kWZS@G8MX6aK0Fp#!hr_V12WtN<>zH}#tmLjj&Fknf6AkTK9-)>vNh4Wkc zmcHvMK!i&U>c<{YqZCSmCWUI|!U3tOWM%_n};55Z+J+ z3xstxY!MXioHKT|IcDzb z5H=qIE|7Ramf^rk*JLbMW|I~~9$^+sQBm?R^oGeBhjH>Il6vMp`1lt>P4a(m_Dh4oE9-r`TA6#22>$EI-bvQ*f6K~NChFQ_ zi=qIwZdUcyR_5J8Gli6MBq^gZUFKM9e-#kgu1*K80k8rE)@9C1J3{|jN3u)om|v5E zhC;_sCU~eM>uM_+~RZo4IAbzYXdCI8>A&2n{Er zBSq--!5O*+SZ%6IXpc6brv8SZKh(%mwcC5HKb;LKB%xwI=^z^s+x4DC*PjZ+ z#viMD5p-R2hV+e|+1R^<-pC)n`uDknJqF4Za^pmYP@CKv*@NDH+0TzSw@Y9v=pW50 z?sa26TFb>Z9*uTh)Y`RH#ZbF$+CZi0`Zme{!45e~ckXwVTxOjl7UH%ubayiL-``A! z4!B@8q_&d2r>Sf1#o0q6d>>V{*lY zPyJa|ryz=mnGLaP5gXD`X>}5llPy9m8Z^Cszf^&fD=6>fKTaQ$g>xE2Cbbt& z8Ys(Oq=nvGw|osRgrCs$W(bKsBEeJh7t!4kaD$1 zGc4M=oxu*fua9;7yeimMeT5@Ap*GrXrUE-I6v#)=%ke8P&&bV^5^0qDLiULDp^tsw z?7asbyo>I>^(VS_+(C>8fw1cREJ-AkJi{KnA7PiXW0 zjnOcLS{?ApPB#AL-e=KU+Q(Tz42m0*0Va-khhmXzX%x%`%V7i(ML8D7A!c3`I+r+f z=N+$wbC#*Dorvfi#?n1*2B0yB&Ox=J3Xlgbd_;QGUSp&RL5C7V_nim%?R&%Qgy4OO zbmQl>x-H4ner?x>KG%)9w*q%cb=-dQwlKVW`fo3-JL(ry&*4piCw8v#jyoJG{4tky1%!jF$12vP}X_E|PN{Af}-twbnM3 z8&9PEl5c0#TfX+cAb)enmGfV9IB&L?zf86qWt|;->3BeQ`?cpAz!Uk+_agND!4N|g zVJzwnDm1`}hDf6&F6xf{6R5onA*Cb8DHpm^pWTO9n}*Pr57ot2l!FvK<{=~qxk>NG zL+Qpxx@XyQ7lAPuwxdaS#>vn33qEY&4oa^(e*28(MO zXW4*}myD31OcN|ESI!z^AJf|!ymZWEd`<2OlW~+87qAvf)lv5wvE=Z@@nvUMm-yh) zz-pb?UP4Q`u?TnJf+(wV^CW}3X1YKQ*d`_?gXP|$c_l0z4z(d3 zAeGEZXq##~M`$+;jDQmfCSNU(GSQG7-qhYs^0c zg*Kn-a6KW9?(Y$08_8q^)K8L2Y@L;s--sGrwLr9d_FBgAoMoM9=<&Y+x-hahE^Y!P zt&@gbHwk-I5vPPgvK>1@9U;HuN#^44gy>QWVT}Pn93@BiOVyb_WSG?@&tQ6qH?pW( zy-lV*;sfqCzru){)rDTdLL;TYD+d~BwFjQAeNkyGRBwP~$~|D2sa_d#cE3GUQc&yh z2+iT{kWQJy8)`fR2hN&vj1Zxb;J)}z0Wq(DF!tb3dr0~;M~y*IRQ~>GmGxj?CclbZ z^5@cs>{1WxWR-Uuc!}nVR^kccHlQ5uL(=n>O_cZ>Xw>8zX;k%VYRoRR8*CmD!?Un` z@D5p8bD`Jf>sMW?4ToB#s#qJKKU;MgteQ#4#7cX53t_1HyY8OP9bx?2(MXTxe_F!$ z4YdZdRo_AV3zJZRle#G#B>}2SXPdtU^Yu#CVi#2hfjX)T36tIpH)JTBd-(J2HXrP$ z6AxHQA(F$wjVhUR(vzahtZg#DW1SKS`{vIfZ0?!tFF@IJmr67dwI{a6PM$3h_v>K(vj;&5H~brwd`uQ z-0qoaq(#YT(^A7K6-A!HV75eI z$&pY3vw^QJwc|+jd5v#lYT0#zL^#?lH}d_V!Jp~pEQ8Vk4mIsJeHYew`$|wqt?T(a z{I?^~LxU}TzYxYKRqj}6B|hHng*BciFZIHwdgmCnIg7_F-s8%fJ*~Ms zwPkbHOoz5n!~K`=@IB%Z)T(R*z=l;t^h1yU8t+nORJXS|Sh!FC; zaD$4x^I^MtmZ5A=ibgJ%x#f^f-mGt_%-~baEk_S$h#bs;?`ieQCc5>l7~j^ zz0MKo#mEy6bk3+MRd|Oaji)nDa-9>vWc%wgQ=Z*GuRnh>yX9^W^3Z-8 z;1{aIf~Y6`_6_EWLVRc9i;Ie<4@ZJR<`%(V>sEia2Qg9kguZB7ml=+2E}oA35Ds1U z27j?r;6An(AU%hj+#bZVLGGmAF2c0Q?wcS%-P-j=Zh|vISAXn`!d>x^x){&hgsKgX zU1iBU!sBwU5R7KM7XJwg<`7MB36*Zx&`i;Q@f-O^#iI#N%%-;eU?$q~)6 z{Li(X04zg?&$817Jw3^{qUdz|&~Pv^QoAu&0_xJF;S-97?P>=!&Y2 zGB8WdDl0v>CY63a-!a{oB#iT;q82B!7|sJtl~rx>>gC|rcnJ4} z=TcfCWK?bC`>fFX+aH7T-=1uo%w26w?Oc>iU0h6^{`Y;Cs{gmCAcC*7zm7H>MP(iZ z6_tc`0y3fkw1q8|rBaMTV!;l4U46Qf!-jR0IuhS6d-MnZYCx~ij+u#pf@PIAO^%i92QV`)NyJLltG|+2s~p(82lum6g7rD zx@1fhUiI}co9eEWVOum*9>Z(%7Ry;NJt;$T*-eW-V0~6Sb>(TQ=(jaa1(KT_H8!P~ zvxtrRMQV*yc~k{S4OXsw1su|BICPnV3F)*G46C~20$nXZ*^U&OR)i*2W*%gVdzO92?{o#XC< zof#CWqxvC%$CyRfgPaXi8=EJr0|O}z{-`HT$U7BTs3yJ3nznt!6!Uzh!vdzIR&27B zMVivwG~1chf&YB1&T+D<446igrgSS)UEzS|Z>NT_sFlP;A=!hKuIt&t+yL4W;5ROClf?|+|j~1F*{4uzJoU_pE z4Po*TH1qyz2g=vd1an2;lz5q&ew*q8jMpJ7@JGYJFY9JGHW_F4-|g$$Jh9KiRDY36 z;W3Vb#D{^T6UY(7f9^9Em7BdbZ@ir9%7<#^6F7Z8Kh+9nwQM9?3qFT zNM0RyO0t@^c>tO7{1h@ZuS#mM&Ixc}tKSpKp~*7qjB z0sO}gu75jUoLxjrolTuA4Q(vF{!hYWs@l3Ei=y}y(XX`1TGAAv^&l}w%%U_B1!953 zPfM}*&YHchhHLphN~&Hc2u^WIA1uJ%*E`{!mS>!`f;z*JSG> zd`YNViS>qE+XQYCMOzg_Us+tNnxKJ@q<7hs805$o`XPURFN4+cv}E>0YkJv3xjvIL z+eEuw?qsp8iFR#D}FGkU3eJ z1>9h%5w@>6(ud++34n+(L^1a0n7pY~5+mafk28cvdJOjG2jYyCj5HcNqC0OJJBB%3 zPQjbh4)Z?@3M4H8uSAtP#k!d(V=$f*hB%r!mrODmx{@6J_B*~pBLj{)15G5hb=pH< zow+DFXJYZ>=|G2y@7Vu6>oJ6q7Iwe0-tIf=IsPBAUip9Ie4>i2GL|}u9~}_-66sB$ zmSp_kGP{6@FJY0Qbvc+emYxlRTACaewT=tyIn&r|`+KgR?JDMQLh5VBSBZXHMjJs) zl-cRzd58OyZf*;GC5Et4>k8zyHs}`Nzzy$^FQE29$LvXvDO>)auB#ya zZG;6_n(=uFcFutf1JE1m3wG70iUgxR2cWErJk70ny6=P19Gp{AtUf<$BJ9L+CPUM|i~QbNDz9H>7;` z!v|Deu#D#!YnUZ5>4fvF=3OT(IABy2%irs}PSn(|?}SotwE&c@IXs8Zyb^mEocLFM z7dVZWiz>!PSxhOjVeTeF%PO@P+CTc(@w)dH*`7^(A#*Aq9fZl*VH9z11B_Mb(Zyuj zmNp1g7_N>7$T2$(!$t*zF()97mZ>gI8}(3wh$4Z{2Beylq&BUVMQe?lT|LDYnj`Xd ziFM4IZ#M5SiYBKdL?M<6!eROv!h=myc(eH-;_T|2hD(!_Y03M8gldSeMV%#$iA44@ z@AnTV>D_8w2~5WdJ^NAMwq&lGuKmV!tfdAWbF*eAl8l6t^Hq@sTqkx{IkHj-DCm3V z%%U$k*QRq9E!Y`pb^r~#Md8f2#rFk_0lno;&k)P2?jCxxWd7<;1F$9wI0Lk7gs@?o zXon1&12&#uG{CH_2eg0pQXu~v7=1k=#rS|0hMnNZq|FWgq>%1Ia66$s zvbxaboCdPGCH>lu06t1(tZNn0^NG03jv@_Cw;LQ8WEVcIaP?b*8$u4URhIXS6#V_nzxHN0zJc&{>L5ou0D& zT(jFZrJ9~3Qg_JBW(cxTn)@-TW3R$JZ^f}hn}$6s6h=ZMu#<&=f)*iWRJa8MT44ZK zUSgdjOhs=S5)w3~S)yKP>^776@7@F%Dkb$rOZFG{*VD%le;fbTRP-i69-fxsNroIO zOC@6d9ak7Mmv4rdiq)KDl9|aZKl$^#PAU1xJ&Lao9T@(mcN+TCwJI6?p{kYwWnYPx zfrXXR`+ooa@G=T9TYrh!^awf+^ObCRiz+pcv#-YB5&2Zs8Gh#JkuXv|#g?bn@jS{E zb%e_O)V)04NEfeI`4xCE!!k!^LdBFPG0WT_yzF=H8S?cL(>>1g|bT>v!Qq6?s_umMV^|_y zO^2m*kMnx|aw^WDgVf|Gy{D!}NWO&USm2tgISu`p$)N0#POSmZ4<{!znq;&LY3m3$ za()VeEgEjUMW-Hh!XuAhMFU3`KLmrJB%PypJvN*J>*sxh?1T%SK`*p>d9TCX0p`QP zZr_0hs7Ggu;oypnKM%2i1JcvswZR#tKyo)3g@qX!CZ5W0{UfQMSM7Udfrq~kh^dmE zj+}6+7%V+MXUqt)bTRH_ErHDXcNLtRmWxehT-6v7`;Nb<8ygMDb%jxbyp;CvTn6Cb zL6wl5NZTB%L8(OWQhr8XG7B32PuahNiHBcIg1 zm@XUFo10rO9PQKUvm6Jq_RoIp!zrBkL`k z3-f#ohNDebW^#m^dtjfjNR*KWN)a!>9WKf&=wD?`Kg{kQ6CbpvUR%!sR7T46sM|JqMrPNftRJn#DX{nbUPd$9(!? z9b_fBPC_nty8)GZ0hPR=wzJqJtMjx8)X%GlY736v)kTeAG6Cs3oCOF7T%qT0M9`c9e!yO9o? zr_uq9wB@1yR-bp?^u?Qvzo{u zB8YpL&N`KE0vK?gt~LFJUM%ZvS9K*v+#P8<-Nl(G4t4}zHMT#LHTopwf=EJe>>boq zA<@}xwFZJe95H7?ueU>g9dGwuo|SLE@B8L$df?Azf9RJKi->_ywZR0GF}hYJdN<;k zfV5Oi#V~J#Wn-u~e!#NM;j->yyD-dFVp=uXS%}@EVxfJfD&{L~8UEJ%a#7U!busrM z4u!t)@EK*r%^K7t;o~|@XQEY`sJ9!?ks}YNNY}6AE|D{Jh<*J%-%r@~4_Y2?s?tMbo+potvTls{T%-3;*;pWSn2CBWO<}146Iva=u9!`5ZYvb2pZ!t5o~^8w;do$ zM0#KnbH7B4f-zv@bJG$ewF{jl7)HMjk~;`!NyI`7WJxkE?8lsnQCxq2$T9Bf6Q1AG zU!p2?k#UfzAT>~8S3#I;NYWbz1UNTc1tOOgCwpC(129)8#~hgi=z^P5YZo_Wt@Zh^ zz(1Gy$LZ=gT3e8%DQ3%z=4_*L_=car*|L?vvgBN;_*Uzx_2lko|cZO$pl5PK8 zsITzJ*{+`RHILEF+$n`vK6s{Pws40l!sgc!gI4NkvEP|vLWrN*6NprlHL0-mEeQKx z6uh_;d5&>HqE#VbI`qo>1nLFhdE8+jf%EV5f?~pgX#pcwe>PSDHWr}Ors;N}fIyn% z)`rFWJUi_Tt*aTB6$L%A{QJ1Uh33Yec%{bX`=iZUNm-02{NZ7pft-~*{%%eNfaDBI zj#@cKHCadiesl$ZYlZH8uOc6@G5t`3*EE&zm>0Ir>N+6`l4=j)fKSjAXg}&V25P~5 zIL;Vky)ck1#38bUv3zJZh$^a~04&754r*;-T+`oe8=-49N~X(s|DXT8nZQO7S#tee z)x~_T>i+E^MA+WMRMO7b#n8s)|12IFRMzZ}84+-cTkNpXlDu;hyyJgq^aJ}!B!qye zLb1>)fk3?OpSOnoHU_V5%JcbwKLCv1U40Gz=f-TL5Q+k|Chp)tXL39P^KC7fUjL^7 zHlA5dTmU#o1|2gjDeRjC1P@)}Q^IAd)v(Ey|CJQheL@nQct?^-GgkrtJ_Xxb1=6G$ z1_m{-Tg|dsKDv~;Fgv zSKTdmk75z0IQ_CJr2H_=h3*8i<>q%(Y4?7~=Qt>FPv>w4zs1B>IAJf+V)kN7A-i_D zQ6Dll=7*_|nyys9z>6G=K*PtxTXw*xAYR6{^8#r^-#2EHd%@i#HT6Q9$5d<>1C`rZ zjo2U_!wVockN(w?WoMT5BLHN1N^+MsnB;p3l*niKJ*xv$rx1ig2ObSMobQDRRohwv za-`4@SkA$wDH_kp5k0&}BDxKPqUeH1C%T=yBX!;p8&F>R#8qY8Ts0(tZCsCiTnEW+ z%rb5c+N6y*Yuk)`;lB5Qx1J5M(Hkj1*-P;S@}Hsnk9JPSats?i?6-Cf;g28U|88IW zPbe#Yx5cI;B9>17(I9*N-=}}I$^#nE9ykl0yy|Z$3syAJ)?(dn7#QjKQE$-x23u4H>d+8t&5?Wi)aMC3^qcpU*qJ2-MoEat7I!o8wzzR}$lIG0@1x0e?=SDqJN`@mhg9=PAE54oJmSD6(Q9kh|oPm{49bp zKxX^WhAv9{z5`r_~SczY-?$>pW%H^uq~*ybI8_Vyd?y z2x;`GZ}b^0?5@@rMGC66P)k-jj#e|--5zjP*H(N>n(n&se$G(V*d*H?;PyE^Vy{3l z{{YECfPgV`0!q{+9z-NgtW{nzt2=}XkeFlvd(vFl)A14zz#h}+NL5b(+M#Av8*Gs> za8^#v#FksxD`I8hNUMu@H{>=iWJ)qVq#;fpmDP|l8<%lZ9^yGXx7egdST}`N8oaHF zFm;oUfK6%PGbR|LuchO*HF?5riST*i1Iz476 z7ag2)O!KUXf=q1tj;Ei5>I`ybosO7BIk+t2tfwZKJXbjQb=OBh*yvOJc(t!KE)ajh z+RO5%!Dh?+FiGBZmnOf|A{cTuWuFo^zn6$TPs_t(ma=@#0$j~eX|7?QIG{mwXAKul zc-pMDcR$SPgX^+Fuw1?v-UZS3wmvK!K@RpK--yU6zqaV8TrJ)ufRYZ7I4b}(JUz{X&~ zN_51cN99;hX`Mh)@fUn&HIScH`nGj_0~1TBeU#i5S}0zuS}|xPA2~a}-W+-|FmULlQ|J%VAZ^Lx-!{?ioSAt<_kh>> z=$IOP3x}-coi?{NH*4&P=CK#DKO}sb4Fi3H*-|)GDA~0(6C+eH(k&COqw<_ z0n%t55y#K|?@|V$hOS3^{t|uFuJ2&-XV0`=Eho%~8B3m0p}qIB>#~s|sZrnZt6X}) z>hwz@IZ_~@N8wOC%PHA4AgJh0L3Yt$ zkO!x2WJvSN!wFF%B^az-YSH+2%;L0LFzXb6A6hlUtC7(|xpYZLLeCD4U|TH^ToGqV z!ov@o^Xcefr^pt4!l4t9jN3Ft4c2C%5CZmzW>dS9KqneEyjU8l6_VBNQk-KS_nWzn zuIIP-wlMv3fh+3;pjbx2>RlOKY2~eqmWiQzqKbkKw>b~AR9M2%0up5hYIK@bd$1EE zX4zz&C%1oqLgBpd&iCK#)ACO>Lq@DsQ)jj^XR_USN=i>on#m@myyo!!)!P=8g+}9p zhqaU4lQ%&~j8WA|*8iwj9TPTPu1`RYytyAKc#Dcof640AB-yBKEkL+BEHrO)^vN#e zGkFVy<5W^osUHo(KynJ=7>ZDguP=Z-DvEG!b}%h`vdb89y1P`==9!t2YX2p*Wr4oj zYA{93`!PlL4EIFSG=9_)rrG?JI47a|cwOVtK#e{skObYVXw}5Yz<6F&RB|D$JI3QX zKdecmT4mMhS+&;6CI$I^47&H~NgIuBp0$x`sI2Dm6D&xe?>x-C>ir{2Q`TDJQ}q{Q zOZ-!F1gi=2tQl=`{2ud`)Tk||nqvaSgwTT0980z%M$t({X0p4zu|ti!kxxdVxSm;g zjc3HS;xz```1e`kOvS8k-YLEGCR;Mi`Tb7a!MyX4z~-bJhu(xv>Z6IZQ9K=6I4##^Afc>HBI+3%ZuJnh%R8kWag8CE+aQ-w zbXqB$Yra9OEevMXEvhlIecG~Iw{9aZ7bD%ZLhWYOk0!pZ$r`Y~W}P#cobYGpS`>F* zvtS#v*}k@S>odAyY-`eTn52=;V7fJz0gko-(cFGtTHbRo$dPlKpc>_xDa=3a#~3-k!$Pb zAVA81lL(zxCN=u{Ls}!8o*G75 zN01NT1^@fJkg)_|k$M2Qj9e9dQb+?=1!C@fqgx-}U(vJtVfvFXTaeFMJI;Da09?*^ z<)b9y+%8>c;iH7FxUsz7=@XiwJJIT|_{%4WDR1GgMg=icB8)*pR;_Q}&Icw8MJ6lM zX{Lxee86(Cq7dc0=}%DRJ;BIP=qT_zH@?Eg(78XZ4eKGOP12BZ$`fg>YTe@iaq z<%|=dn*cP69uCG*9ocmsm|dSZ5Im8IScCfG zTb_OhSbB8nX)RGPicvAbSrVj&LzcXjU0GE7v?^3{Z>*uQ_!Rl^~>7e z)#IC35+4<-#FM(50CzasIpFAO)Y!&y5eW{6cJN?-TD2n%uRZbLvmQ> zzNHRpWO?!R?qvh{bkA@5F-Vb@WltpPgPz_?R`VKZz~QB{&x$h=G4odSL)RRg_4D)Z zIMbzsZX*ymHG~DS)3DQbWky%QcvtV+wck4{>3igpAN^;~*$1Ca|zgf)xHdgUJ2CxP7GiPjd)Gyt$+F38; zayj-&A!U@+K%%u+V=MM$vKWpA#D+{ZDcp2{6Rp+Zv*9+Q2CLm$is)-UzcU3hE@6I! zoN)xR?vofoW}jToNj5X zs$+&i1!VODbLFA2=hlMG+?{lw-{v@5bf8&XoO_8EM3CCu6wOr^Q!15S8&%LEWqP6p zPMdesp4Db;X9r`ROJ&ubo$2oD-H*EEX;0X8+cwLdw`w>|as0;BY*cp{%by7XKC6zI z+a#u@(R7d^g-ihfot|B$vN6YoS%?g)EHArky>ku+H`h#cqp})l{lx48qY-y2CZCUFEIY~8 zhlmbJWUmmyc34-_f^G?UKExu}F~@}t9TPi-F6lVF4$NU|E{AmKhz1$|p)xM|5Y7IK z3h*TC7{2KN!*8Gko3+?V@($V*P(k3}@Rqn<(NJ}Oy!Ie_b$`Em5|8rQoB0BDuTpey zX^>(Ir2X7oPFYHz=|WY7%93KTd;KAo!QJfX@7wOa@^YAUbE12;p@~&Z9d^)lsyRMj zkeqlL<=D>640eiprx^|U?C#CF@2?t)RQcA~0mB+>#X%|Z)$wrf_3m_kmzr5CW!*@% zF}9K!NblFYj+EUIc5w)-HsYnXC}pOb3@*SxGUSam;I-wYZQ{6ST_+sFU2$aGp)JO& z20F`q&b{GKW=d2ZkeP-j=C>G&>hJ%i$xjA+-Ez5Dxs+WTYuf?*dojhx& zkXPlk8aKOZb~N23Vbzo5YqTK%CJy5RU2XB)7+~l#)Sds_++Dhf6+h(8)~R&w4(kJ@ z*%__-it^~xabiI4?hOWs(_(@+?nHLYM7q@o#H}1hnM~PqK4S5l6=40`89={_37nsT z>QQ4VKl7-1I>xTi%1@+kD-D#bXkiM%m*mLHu6$)MUQhMYJ^IOqnS_oX!Q&V%)Mz{8 zIrjZsqfZ{@9fhCy+W9>YbJ`gwC{k2NPk3dS0cK_LEgvnFsIQ~G0tk8qm+5g{2>+_V z{E(N+vFY_$eK+i&O~;fv=|btO;J(FX2aktYNol=oMnWw)mTtk`oYr~)DnPuB8NV^} zs+Nl}JQY;54)o&kG6J^|z5^0K|0f@dnf}LuukVQQDn!g^->Zn>C4egUz zo`@rT>;uvMA<(_K+<&=UbBJo^drQmIn0Z41XxqhFuep{tIE62ug zPf47;-060GM^&q_4H27QzQLoy)YahJv%8ialFG;r;8!V=nyzLfTq?UBgr&p^luD^- z*3uIM*Pm00mz*#;!B;#X zaUq4dU8FLQQD$})9PB)CQANfl&84=6TWF8c)x@}Z-cnaB*8dtvVi!`$C97zu9qlw=FpM zY{1BUo4Q7W^NsO~rmF2a3Vs8g+l}@Bv1eTP68XKENA9nda&SC^F~c=fPBfFb=c=iY zGI29YgUI}QStn=8cXUhm*gLysko6c#jV0wB3h0flkUf5%=ffwfi=wljz2gtP^LcSe z_4ShwuKqwcRZ*Y^t{SPhlBATxC;ET>T>qgBwxEreu`r>(7_or$Q2 zsj=(#-t+&OcO=-#&nch`eI-Y_rh}5*G9aK9^1KNAL}bAtUXjwlS*t_!>4B!XfTec1 zm<6Ag-?wID9K_)Jl|8IJ=zL^ibAy^z|K;poe3a$wLq3yWAt#S?Vlm%_&mfDBeUy^7I3Xa|jiTC<* z=F_aX^bFcU)_5wslY-h*#6!V%r0F3@*W+(~NHc*kk_ZpfMW9wN$%Ji`88?Smjg*U; z`Hme%6~1&Hjd@V?E(Y-+)zO&<4;#}kS132lyZy(ll;ax1ObG{ zV8TV303THn3uB!uIZ@+T8rHg%y&f5g+!rd6s0|(NJ>Wn2m0(y?b^xtPTYQBsm57^3 z_@qB8m1VzzvX#bx)suT?Jj(5LmQjm0^=Vd^mw1a#{oTVkB^uVRIpT3Fr02dlHkzmF z*$tT%ovh*H*e9CS8T}Z5k~DEhJpY#L?Y96G(eIw@EacCvX=iTjC&q=eDK@fte-b2vO{oM_gWE#`;pS%*bc61#8% zMy3fZ3u0p2EM`isHIS_YmlJ)d*lN!Btd;lYqD$gL2VqFVdX58^)*{fQ?jW@rXvI)# z#3WX7R^A%uN#EqZf>Hkj^tbEyB+Yj)w|#ev0{?#lTEX($mF8co`$E+fr|&yhUUe)> z6VF}I&0!;PNT?YjX_}sG)S5g5OwaJhKgK|qlDdz}vk_J-L@#)H?^0zoI-(`}nP1!Z z6cR>uO%XQaeo*jpO>g%nZSGU})&Aaa+syAJ#$*wO<8Fo!&Q2*yH=+>w0K{%}rw#&h zP0n>V;t*~_pU5_+q@`L4begUiMF>O62tOL>!>}k-Tx+gNM4%*Hd#|9}-l~`Q)Y=Rg zJ)pBJ>hV<=!ebU*QH)CjH58}bYc~%yLXp*oGD~}NHWV^#HjN#m_jfl-jSXFeWFwHl_)eKyC-+YhaCE@Qh%+JV!!V;fVZ`BZ)*UyI$S5m*$v+`x_nF6-F2wVb!tRV?3D)B_*?hZPwv0c$+nvqSumE+60x<9JUL2a-&J zzpkCO^kYOHaaQX%t}fW&HaY47uQWRlA~s47iZWg7ZMs%g4o%56c)zZAR(+;==SiVw z==Gh_|I`zDBi)%lKYB@P)V~na`0H4{Wu-UR*8()OX}T@i#Snj_m-*gajHpx?rd{K* z253$zWbiH&T|Sq1uxRew1S~$q2vY1&C4q6@Q>Pz`4TJs`*Sp^-3NiFe)j_lhha!*w z)-Xs6A-~*5v5Ht$(S!mZ*&0gO8ekb_97Si8MMo@etYNZ=b;p*XZn7zS$ZMn*>WL~< zLPFZ2BUtsqLB_%P`&>D#?L`T_{*AC6W90M zxy?0*fkoGA4BCOxOTunWzXGs-!Dm+?*#IVVAq1CouaH7&fx{)6QD%6&93Z)n*Dv>c z)566ZY)J~qK5yxl{}N94{$Gr-{;5O;Ts2rP-;nsiH=g+afF=I-U#pU-$$wyql6GeH z|0+n8>YfhR>X=`=mW7G?u6Z1C$V~>gU0lM`b$a~l*1nN}(RLsSJ7dHdNOw%{wNu|N0UAPSCJrNP=LT?W z=GPzwcc}kl4ohd$3ZZd=lE5`S?e+jcM$}-;i+4>C;tK;h$C%<`45OfOC6 z3(t+P1dXD05DJLr+AQAt02&O^JWb{tOVq3;A&7#S*LJHtU--ktM}SaT=|O0_HhVFg zUKqC@jLj5(Sf4tY{^(IQL8>*04?R)d|#3J2S z@ti@pn4-9|oWf?a#$^j_{<7>eyN%ZIo~)Yj9VFRKQ4OnvC{v`kRh(zWb%6Jy!v|(Dt_>3l=X67}SP+wZ2$|{oMNP35nbAFT2tva?R zfLc~-p|`WRIYxR#T>ScTYFy4WUG%Fg3Ldh*t&n|Z|5av*Mq=~ZZ4Y>{+1AT6dE8rH z8$m6h$;m*-tTu(8vZlpzp}o8v7Z9PcmE2syRTOuqWsQbF_XYMMX}o_}q!letV81f6ov&yN8$y zkM&w}9K%EA<`d`p9U965O`JdV-n>iH`mEi z#MzhmQH#<{VjN2nT5R{_QePb2rSy~QOqABm(@SEB*72(`HL7vNerlCER%1J404()> z>*#Vx+j1*+^oPEdI0yZOBWe6ZatDDHRdkY1fK<54m{ghF>D8&sle;TSl*_}@(_=Ku z9I3Y9co3?If;20n6hrpuki2@%N&iz z$7Lt^6wB9IEMU}5%Am(hK$SKBmUmvWE~m(wcb_U1;MP($uJJr~(LLXwj*)`LXzI8G zY|{$jhf~FhB#RY{kjNj$yZ!Tm#WmD&8l6_}D)i{SL-ibdDI)SuTK}_w z^^kTt;9^%yH<-5O&~g>MCpQ>TE;?CFpjp{hM&)$nqHB5=!J!U8m{8Oz%Bi0--V2`? z%M0HB2$#b=nNlfzz(S1w)X^Pgzj+So)_+6>Z*a|E%tG7>A>ges78K25~m*L z!WHF7j?EYxK3)nG5w4$i1Kb)u9!3^AdlCG4q(#nB(F}hc>w~sjKAm$2mGw&T@a20Qy@CHMgbB1!*^=AzP(!cB$SQxc^SLl-Kim}UC;4#Y=kb}ka35GmGx8E$JJ zR5@X`V%sh2_E1)jKWBi5FW2MMr~^cIn6xLxd}<$yC(_X^=FZ&<<@7ur1HjbdPhkJg zQLB$${1vVzdiRj~g}NtF^^W%ghJK&8hd#hfJqRF-)GsRnYbt`v|4_I-8%74OoUj&7 zyDRllxG7%6c7MNK8~uQZ@UU=#`DbcmDgWEWuXA7&z92AORqeOApwn{0(5|970A3-Z z{$|(61*!Es?6OULbbk$~plnmU)JMAGg?osfl5u+RpCG>$MEmYIK!#t`mTZb42#9~! zcS>E6NSZIQ4;J3hfJwVB{=T9boV=IF`(%EY{-caZ7s8Oa+@ z4G!O_YC?n(GUI^m0!HtEhKCu6&m{H>-t`Lz@3Yw3rEJsttkM~M&+kMf6I$;T;cqav zH}VnNTHWMrnC69OzOKj|`7gD=KHs^&^mpF~4k{|4pa?_m<-4t}zXHE_MDq1A%sw=z z3@b}|#(&tY7m*f_ww=Z({IxHUVqgCUN%y)a{7(HR`9kxf^Z&nhS0#5l1fE!5R3z^g5s>c7cZK|il_&m(MY%gG4 zo}E25e)sntxXnznftZgXWMr>DTsi*CgRdRveZM|034Za3(89yzlGlaLI%zG|C-=eL z)8J#KNyh|#Gg)9QG<-8y8OO; zHr{i|w(7rhv0kV1Ic<9LKP?S0JOQQal4;egSaP8}KPoTi*p$nlHf4E#)u>im$Jf#P zX-IMQKbIdaKm1sSEIdhPp2S{W-14xX*WIDj0N&Kimg=oHbt#=a+>|h#`CXuLx6#C% zXspVW?DO0QzGZnhV`9qP!ZW4ee>*X#b)uCZo7|nUz24}Z>KGEv zYVL~H(jBiv_&Nj?)8#V#G#=_OPilZu;}j^pan_ooK5-@Aw>L&MDl!rOX>&pPHUtIu zXp#B{Jw*E~w;{OuTa$FUGAdf-cT^WQYMb~Z+f4qe^aZ)TyLa#LRom9nG4o9;=kH{B zdyjtMT8uM_OmV<^x5)t%74Yk~c5t|}lJr);C%Flut%3hAaU>b~B#nw%RfF0f4_@ZH zI^@Liv0vv#EYLWXvux6WdS}{A7-6e!e@wT7{m|+y+#U=ZxF7eZc zF7dfrZl_%Qf#D0GMDd)bENweN;1}{uDD9^x?ft`{?OcYU#}Qc+Mi_O%94eOTP!ttY z{#bAtabg6ivnU-ElQD&ZMEvA9O0W5FO;C(#;>hg~2?&>qw6&joB^I9EfW-)ST2sc` zFO2FE#y+yjbkb!Opz|6HdcvGsnRl*fEVn4ABt|ia18^#qYpVOQ`BjV;Y-ilXzfE=L zv3KPNh4|doC zO790zcgJ};J(Zc>MLHAW1~WQtC>!@(%H6p8x`!!@y3%mofM_$&HKA_%G;Iqd8;wk^Wi1m z`x&GUN}~k>fAj|%=iy(s4FX=XXy1*IrDFI)tct&FSFlPt;KA#q+_jak??J^7x7SDa z!J%IO7_j?{PF~-706g8oV|G(V5Vl@~>0bb13R)Kg|0;eKkltIMTT9n;ceiT9Ga<78A=z=eE4U`s&1w_*@#acfvtU0 zjqTb)5FLs^2xl+x77sP-SaCpE-&-+iFEV6oJ75*~h#lVxH`nekvEkLW`-S_XJH|H- z>ze5!$3AOUzp<3CMW-l(49k0))iab^vA- zyruNM6h>Q}KOzZOKKdg6`_;hzT(=!oy}7X#?ozcvHE`(=v( z>8KD}9KvdE?JPZU$`Hm+Z2TN|buxPpzd7Bw9XiI3$+x9!?%l?v)ODCBG)r-_9lGzl z^B2jE5({!seJJ0g7zHADD8VHe!^#B$ouS?I6Xnj3u@CkQ%2?E$*yb6^trIuwurP@p zV(k~j&#}mc&Df*u6?+J@SXAOLN6QrbQ4gJ=nJ~)cU|JVI*`*&34q3*fMQt)@J{7y_ zB1_%`OAotQvSsGXLmsovAlUJ4OVn++#-$s#of?2Uo6lhWk6iQUin?y<#k)rO!hoX|3f)4DNWA?O=ZI}(Ww5{2K4NVLS zcp8{8R;sq=Oo)x9E2*iet99fReLN_EQ%Hy(*+4;mX;I?N*y~^R zg~>y()Nx{dFs&w}{&AN2t2vOZsI>6SP!_EFC*l`yWUPJw}p>4>C+4i1Mw|S zLCgJWBAY1Y(=^QJks}GTt?hhS6ya>?=9N=&)IvF6T)4DUw--6-@Y2#aX&EZXEBwsX zIIOg-wCL6>hXD8M{S}q0M*}DWN-j0RY&Z12ZXUWwnJJPJQ=sdJ2-H5$9 znRrd4n6%{UYB-ZBwxv0Ph$|5sVOlBS`nslT88HLpwEDEKWHK1d)vC6k>iR>G_I!+B#9?Ojeto-8BNTb|YpqUvra#vcAv6>tQQ z?k+fQ6%YA=jfOcpVv&7=Y(m{8{B{a;Hx4T;S}UugX!Ufr5e&KEOOuPV`&2#Z`vyj1 zzL^4z4qd|ynXPAqNrzk-8TR|b)(_R#2_&la?^^;i1sWR;QuvMS#LCYKh*tVJ8s{59 zVnz+&Of+w?n0uODrFc^bqHb#!HX-6UALu?M??>I~UDRo-P3 zHmUl1tSNgEOr)xtR-di-uq^bjAtkurd%}(;=Zq4?C+5<_Swmm2T_fG$d%WcMIFVMC zqhTT{80A=5t5uy!W6zzO&@vY-QG&zE|TyIPc@ymK%O^C79ayu3D zSC@=sv}4yr1Y4B}t?>bCW_X2-|FvI0l)o6fER8gRdt(s^+CPW8F(?U{toMcJSpH8Q z>yC80H?tr|^`o!+?Wt_oji;e#d<~Z~f zWn1-?-lfZwVksenu1$GM6xs9%riT>5apAEBrWB4;+k*P~_0iL8mXZ<44i``26$86cZR{WXd>zyhZe=KGr&WjYymEL2zz2c9j>n0)|F zR<@nj+$4i|@;EfQC2I$cWYNf!?N$@X)^?5?I8z*56)lxA&3?wCVKT>lt_dDf@Br80 zM+}ZHhJpQP-7zKr!3EcmHURD&+H+ZQ+kTF${^`9^-XjE?f?j1v5RYFkrQ0mXpGVN3 zH5Uupfy&2t#$+UrpYqFuNc3-<=4LNfKa z0SNIIwoenxJkC-#!{opZ1h>ww=K5U9yFBv(D51yhgteuaJ073r1_^Z6HV1#z;4!?(io$Unx2$QjC9Ss=Yx= z@8^G>4{qn$J{eSrpWbxt7|i27uW=gch>8&G&AXn$+aOxIWii8OK*I4EiW4Z=wVvZ; zVwQqb((Gw#s;E&Zl?ilN#1DL0;0t_;JPaAtq)P5qz}!oS1cBSeX805?9)j}<$~M?v59&4&{l(G8QJAieMmBk?7VJf_|K$Db&nqo*)0p{=LY-Z3 z1m~S)t?C<=IP2DK7o>-T6Inecdn**ApFB`qE7=#PG2rAEe$!1pV6~}tIQYx{8D38I z_RQ6-KbJ{U_h?!_IOt(tiX*&R&MKiYm9Y1N zal_I%KOv~N3Nv|A-N?bK>3wDe%M8ESS94HlbxN&vX>|*sEfP_#e?6Iy>(ml+?p$rS z%UbU=^}4M&`l^tvsUanT96l&fes-`zlp0D622*UsGHYkl0kvt?JoSH&ZHEDysIW&=Ir7!Oocr8>r3J-g?(Vs@kdQVGImCK3Ox>268 zqb7DvH*F2o@CZ4R(8#63khVZjCAJ8b>S@}W>WOrq{Eer&U%gfIyP*7x9XB`kae=4S zUYUX|_54_`OEQ5}87JjVyo`~^WDGAxH4RGH88xX)s!ybexMLYorR>hairmAP*d>CS z_cqAsM5tJDx$`$ldGgGNvdLk_)IlA5%`LjcQ=m%`T)?jU%(wR2lsHxFLvb3@?5Ep7Hj-QZ68Y&*^ z%|3)e{%$0#t?0C!O`{*w=>R!;VU~6n&v|Yq`eC7lG9_M^F7ropBLx5STpzi^Hm^a) zrdebOPTbs5GXNoe+xfe!Xe_(w(7}C+y;|WUhaKl%apH@a)I@3$aTF(&AE!iC)f_pT z1h*zRQKAimFXZp%=`gxFv7^ordw6D*^uru?jn#k2y?LwBHL36mMDCM3QF5wx%R2H4 z{xw#yERt!;N{cf>=q*7FmTL;Y_2G;uP<(2}EDGy83kn;^>=X z*33Opt*`ANSScOBcU=wmGWETyVffo8&RoI?RXoj~so|ITyDpEStqe8|8JQC(DvZ~h zvuqkOb+vTl<#iM_6qGr8$(?ssR=pUbE`|0*(!=)B0z`G0G-4*9@xsF)Ysi1JCgX9oN1>j+6E#2{d1QE58QcvqGhU zg6uQt33kWuLSA2|knX;sI;CSu*LnYW}1ywpS#-ec{Vc0Rbt4_$>5Vo)Bdh)(7 zIwQs>v+45mOB>qIm}&f7z$J(khd6xB13k-mUM+FW=k*d8>fwwj2gjybfAa5W>D?2>stuXZP-N_`qV4Y5Fkg5HtDHVCRM&e?_J3*2~J-n;x zJ91qqG-JA^b)YcPt1}XzbF`gSKfL&nJn`_Bf?JXuB(;i;E(y2SE9cJmm1md}U;lT1 ziv(^(rnMk3YD#N&zITqc)u6zMSjeToygOgy$U-0km$}Pf)p)&~K_JItGwujWrnDoD zwLA8__sG_t%V_2Sx7MaIekt<^7h2&ol{;ewW-hQoqHArfI?|T5<>Hmy>?(>{Ed|y0 z=3V-0e&TUy|LTfLWjlzcGj&GS@q~qm*K^Dy6;)N`MzWuYRR%}Z@1UE(Ku?;=>W0(v zjFI-9lLgl8xvh-RK6EQqhNw!oC3RsSK< zUygyDK!NZ67p{RW2pnw}M-`X&bAdk3ynFJ+s>$ktIq`Tyo`hl%T9jVgs4u<_oKOwF z?ojRYIFAg#VEcy|kg4@)RQf@+fEDkQ(qfhwP%QP}R|a|obm&o$?`&UU10E>7Mv4jB z(vXsRnI_?O;5_z&y60pAA^LlHo3o`-4$`5g#4Rh}{#Zbu(ic5(u0rp~5#c7N&xAqAcnp0E<}I`$r@w;9h&gc^=_*J+ zrbGtQN7Bv{8ijxG7Z1Vx9q`yFj6Rc2(#Tl(8S;9Q{rtyggOyfWZaC#E0~=lGo$3rD zMH|E)vuA%_1YE^7j6cX%!Say2>t5y?^AP!BlzvNH5!2|7?LRLrv`NE)sk;-pqYMkY zrBrquJWoojB(aEx!mU4b4O}23^Tg5Z1npaj>v3N0^)<7981%yJQNZ7e60;5ay5VFp z78d?hUy*(qXL2L*&9UsnS;~En+e#UA$5Ho%A|iUIbKJ9)Emw!-h4OKT7EOC1<9cr& zi0iurnv*TJ2DBa=LmDz%0|m5-pC(rY@z~9Pl)r{&=Q>bCOGErcTrP7HL>a?f#HQ?w zpSm(GRRTbk&2ePT!n|X^$Yvin@)NrZI(+VP;*;q+-0P@)0f!{*l!bK5%4RSm$b#%r za+=Y_y}Jrd$EI=I+HbJy#u_OlI;V9ixlnH)-h(qDQ{_mc4k@XvGr0~{Cv{Bn<3>#- zVMu~%#$r2AX+y3J?7Ra%8_L`V`oQ7|+1xYl0?QeOcLTExcD*~j!`}hR-J_Qqyyk#( z-4odgW(L^sKt1mPZ$+}UA*k;8SPj3g_wq;K+#z*g5)4z{F?K-{s4gsxba<0(#jIQP zWZPkAg>ceH)13v~8qmE7ghK3Pt|5HxnKZgXKb^`WY}OD-?sv5TJJ|jD(JTh&jLW|R zQk4E~1-VcBl9DD(vC~tKH-d#&m(+y*gdbqR`$$VnWZmM2==Jkao4FQdh>^mBxU945 z!;l+@9<-Xv;=<$tHws(*g}Prh+mS>Vo9u_y&-Bg_w?<|2r#gDhT8DiI0?O$AhLJt7W45WK= zFS-e{Rr8+tLBoW5YdHsX$9OtM&^=S5o?sydl??E@p@(`42N%BY_4P3^LDu;HFEn13A%FRe_u(?YhM7ZOH@GdL=IH1KEKe5srkh>Hp zY^M)299A&v6?k$WVgl!6&mv!S1^5zQejUXqElsbusfudj#uwbx(?A(2lDlA-L8n?2 z-&^U59=G~2w`nVf)<-C#YG8AA$x-Uh!_D=|Imu;I`91)(Ou~-B#TC9i*ZcK7v7qo| z93#cm56q?~K%^&x>Ji&|Acx);7xg1M6su2K;FeY$%W(j~D8vxM;I`A427B@NE{Mb` zmN@c&)0*$w4d<8j=(3+J)+URBCeP2OoDCT1{J zT~DQv154tdNoBKUR^)IGDe&h}QNKX%=bQq)7~D;uiXShcJ7R(Vm5kXY zoT;nO)P{~sw;KEhHKs8w63f9tB40HK^pkKwPcDdyu&2Gv#*{#-yAUbR=_R;L_2VPl zOe!1moOKS{b}rY=MBms$=?tHMMogo^H4o0pFG1ScQGNP*!_obpzZ7R& z-JCswvs6rG%rUI9U8qZL{lFm4fFVxY9oN3Nyzec4t08tKey+8U@iBKE~fEIO*nfekOIzSy0U^Fc)U^ISXTvLb-C#Mx8=Y(MZJ_i>xjrHznH?(@1ZUwJh;$2IVc$7U^B7Aw$Em zzhXvBt=Hv=N?e~z8o)Hxa6FToIV@pNhM1GSN{&6$z#m>4{Cg;}Bzsz)&YNTT$qHy& z1c2guBiHm};kv&3WIIgUhO-3fSfw+ZtSuK}9j(bn^+89?PALy-1pQOwMz(U8|>L?otbC(Mibjl^k=$59otTfzq*&GLhAKFs}dFIt3CSJNUp*TWl3~TG)z5`+0Mfr~<0}|dKM<$Xfvf56X z_8ev`)gJ7TW>Yb5r7=@oA;~K_s$)Q)$M_%@ohc4|kq=)P)bWG%!_+@tzXRuXBY5Bk z>*Fkk-P3^es*NFHR!D7r#j+8^VFtqoQli8*fv%MKLcNZ@^u2rlULn$-}Y=?`fXnFhy3}emeeM>_7{Yv*&9&ks2`)xgC=3T z3%U0rgmyV7In~-4=GYriYN#HqlNzX5Rft%5;rKj4lq0+(H4S5!_4rL$3S9-jJ7c%*vMmB2PT7F>A&Q(v={#; ziPiF~y;2=nulIopjzY%J=w+`f?t+UyTO)y9)_7DAH=To&MZwWHtFOSu5S@{a#HXT5 zJ=-4l0Gk)vCqW?0<_!#TzRM867wq+xh2@A?GDj5Rh>30iuU6BZ=U-eWKN)~N?boaU zuT^n8z)FL*YWca(X8r4G>3)yTnvkagE?~5o=&p+mcW3@jpV)@@iy98d_uQZU-llpx zIh;`ZYDC9osPa8d0-r*M-=z)b<6TRoX|Yo`G{as7FnN^k6@YYjyBjT-7^Rwuk+m6f zDqAzZEMW$@P^lzIt2f{hitBM8RfW_mq#DhPaQ{Jv$V7d$Mzonrd{*YmUnmy%n@m6O zvM__}?5>?LT?rk6hGM(=VlmCVj_~r2WX0G~QhRUSJqnivr_{QQj(?o} zJ^AV7Q(S$2zTouhJ0-NwV3$fdx%x)0Gpk|nN1ar2E}xp5?{x*t#(M+yKvQBa$<-D- zIqz1SReU^*ur9wFYhg_6sfzZMzGOc^5$ROOZ@9f zZg|}iZ>aQVw;CeP)NWj4pE@@PYt|29k9w-l=MgXJ>n$C#CD)d8t;@;LTi-VW@b&d% zcU&3Q*;3b0*K?w*ZRO~#GOam#`z4zuqOKvJt|_PYzj*}khW6)dLafCu^!tGF5sW() z(n6hX>WMib+D)3wy)Gg(!KS5No;vx_(f*X$)a;z|&7J#<6@==e@+P$WJ6l>Qw1*RG zTH&45n`@w2pTSS@q%;m(Eu&{*qg}HwWM1`wo!CZbl{~pR56Zm_+3Xn7JSVgUWruj> z{J2g~dY>LXO~z2d*@9#pV7c4vfw)<#xiXTR(FdB1Hpt|4t+JMN{BXV3`qs|Ai{!2A zKF#3V8);OuRh6X%fv}t8TO)e?oPp3+{k_!<1N~J0S;zI!nKyT;JTkniu6TQQTT|}( z_Pn&z`Qd4K;r!i`;nC{rE8RVN+?y)3?Mskq^Zlvi@qw^(6#!xUPx#-4j9Xf2rlPO6x{oaK#eFq({?_MB+e;B7krmSD>s~0FOzBZFngG4 z(7S%*E^Um(0rr0>&K>$t=7628MskmylyqK$aMw>ybU=J2nJ-@6xxe^^c6Vp7qGlbV zT&o8QIaz8?f`09QIrv}n>()_NsUCM%bRq2OCw|fOgx*#@5t*YP+XD|-bFE;P9-3un zQkEDCDlg|6>HFe|jb-$0|r%VHJXI~isZPRJIGb-fA zgzmisuusijIHi&R8x%6_i*-wKaK@ByE(#^~AhXi_tqG_G>e(T{w?t7S)!7f-<6MYuXZU;=5jADo3`3@s>RO5bz+{1bN*x+ zYP^|xWalE(Ta)nbHac`u5~?v?0b6iWMl5qsJT@4cbnwSyP#aB5pn^HPICeBK5g*3( zXhx$LZ*@ZDA|lmGT)Om6qJh`uMH~@=a>6)*wqzQk(;MgKVW1xs>T1tani>itpMv#ir=X<2*d0Ca|b8S4VRETwE^qg3zu z{rBAlf{dJp?k~$Gwlf01)_{g*NM&0z<{iTVU?^0>wjlMqvn*I@uHQ%cF>e)OqcZE! zTK&qv9Lv8&F7p1xQV=x7N<3l~D{8qtOSc^4cv=o?arEa%B->RBXpcr0>p&rc zQaXy8Y}lk)ibXd_3lIfVgn_MoDK5D*cs61+a5qlPkV|V?E?ZC+S66FLYkZ*d%k`%S z<3}P@$iO$stt-S`Oyp#3M3V&<+ z{!Heuz@P_DY!R)FcbWj=gc*(fmqC6{OE4<~s)R1`)DI3$qLN#sYY!y-3m;7116Bjf zPZiAz#-v(Z&BN4xk1D%NWYUd0SftJFX!96$D-t_YUa7BdA3GGQDd>ftlG*Y2h@EK*=$$g^=<+YC zY-zOMR-uX&b62s!G{JGGH`+}((f-rHgY`T_cV-U(mWeR;oZ7eCXer**WtcP`U+<M6dNh7=;iH?AQ<<7~kzJzL zZ{)nnP;aj#j9h5l9#c#9bCOXPx;98PnLrCrbJ-zB1=>!|Ar{)p*F1^wldQ>V%93>N zEr%74zE~?fH1<#FdaMXJhVE*FRZq#x=sp+3U+P>d1}VIs?bhB=E!m?ntH8i+1naPc zb`mXKzx*WHY8AujxAOSqvQW+31TS z`P8|9&{Xnzgk}r5u@##Fg*xn7MdpuDl84TKDw1=j&8f7i;c5lI4RQ+b1o7=7fct>L zfW;LY>HGC%wMYL_850Rkw{HuMKMLi~`|XX6bk++3<%eae1D+=4jED&4vW2dZ!tmHf zxC`@0dkQM03PT-OH%D5AM}!++Rd7XU#?yrx)gkE4TD*jA6nr0cu0$xq#D*jTkCk~@alw$vg4;l96e4wV)bc< z<(Lr(&cCmI@jU?enV&*8bg`mB%a!gC)Vzs>2U})gYGqycj%OyOmdQ_N`l4GP_6K|UKvMBtFbQopv<6KL=bH<_k~3uT0iklT8>~>vW6jlZ+Z;i?cUaIUD`K=Ei0s@!0%n6TVrX3*yg% z$7XLbsyVp10~?O06=T;}qsZ)^l@15!vKKc5@KZT5l`ow?5=p25fsXbEq!|f z!eZz@6NQ$$Ss-ts8<_n9fkI!fWnmkwRim89TBr3Ii5)hTx0nNEwX-^t|2V^J!0=6o z)EUCdbAvM;?J98B6R13o<_ML)j<#56As#C&@$(f}bYB24%0iG+^2kQPJNSf1KYWh% z+^a4->Mb2#B%V5(jNBfSw=$R7ikvCj?PK<6%?zH&ki9ZazL*L&sMRXFBjB%Dw<^13 z8hr;t-yvtsj&t;JkIOCVp-1huT!VuU61wK#dZyI<;j7AP6qRa(&U7P&;`cang;h$F zJ$GhQ%y&`MmQ#Le!qp8&rIl9(ULtxgD!YJ|`TX&G#332Icm&6gruKNXi1H+SQ~OZA zWk3HHLdAcCFbWqtgf7ER3eN4gvDkZ zZx5A{a>;>%L5f2$h)w~oPOga4u1S1VBm{&?q(rf82oj%F0mY^h^VrXcF(bF}uXKHkU>C;3(&>X!!)t>U5PW3Es{;-nI)||K12-RUVBLsUrdaw1tY{Rh z53ntbu~gA6Q>bU!6Df0O8gwZZ`4NO;9=&pMnFCL-!t?{g$sq?F>S+38FZKLzD)*gu z5-~TWW)PJ~?4GQua19^r(NvF6HA)rxOH`ZeiE6DLmbx%Sb`n`1z{ z>=X9-{6&dnzRuO__fDXf1>DOzUSf5+rIf$p)_!kpPIFKiBAV4sudo z?<2IkEHL=@MRgk^a~)PMdEVhxVsZh;(mh?reX+x{cn8)1_}T}Uoom?6mtyTMON?92 zq1vI^>jQQdK#%sv3LW3xvCrs(k1dAp9_#m5+-JD@r)kbxzVL_W$8wCXv4!^#g74u? zY9YieW`WF(J-MQ+R9&Gn)7U6ORjwg~*KAz6q_UJ+P0iihR8`Vb&;d$UD#ASq93NNP&ai+^>;)Xm;$j#lpn<$L*HWK= z4~zsXT3ex`+S5>x5bC2E@2FsE@Z<#ev1_{)ZWS$w_+%hWoTmr%Ma5!P#nvpZTuqm^-e7!Y3Ed;Ge zxUjapoemRb$<~%^X#_9Y^&_LCL{t+bT2R;Vj)tH%%5G~xl>LD;+&kAUuC<_SG{EZ4!%6v`Mu2dCV*uSDB1EgM0v5%h(kq^j zKQlXJt%Q(>&o!+|XpG@k89>yF76@A=(i#hL%3lW|)GzAIQrGb?!pEJLrY@v5JW9?& zdup}KJ-AlcUJ6Bw1u8=%=n{O$N$!0G92-r{{a?q3cpWSH=@qi>6g#~{B~(bEf~