Richard Nemeth
3 years ago
No known key found for this signature in database
GPG Key ID: 21C39470DF3DEC39
35 changed files with 2416 additions and 700 deletions
-
37.circleci/config.yml
-
32.github/workflows/bump.yaml
-
27.github/workflows/daily.yaml
-
90.github/workflows/lint.yaml
-
33.github/workflows/publish.yaml
-
1.gitignore
-
16.pre-commit-config.yaml
-
10.readthedocs.yaml
-
8.releaserc.json
-
31CHANGELOG.md
-
1CODEOWNERS
-
86CONTRIBUTING.md
-
3MANIFEST.in
-
5dev-requirements.txt
-
9docs-requirements.txt
-
70docs/source/conf.py
-
1keycloak/_version.py
-
53keycloak/authorization/__init__.py
-
5keycloak/authorization/policy.py
-
70keycloak/connection.py
-
26keycloak/exceptions.py
-
606keycloak/keycloak_admin.py
-
126keycloak/keycloak_openid.py
-
191keycloak/tests/test_connection.py
-
59keycloak/urls_patterns.py
-
6pyproject.toml
-
6requirements.txt
-
65setup.py
-
35test_keycloak_init.sh
-
0tests/__init__.py
-
61tests/conftest.py
-
1201tests/test_keycloak_admin.py
-
26tests/test_urls_patterns.py
-
4tox.env
-
48tox.ini
@ -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 |
@ -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 }} |
@ -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 |
@ -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 |
@ -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/* |
@ -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 |
@ -0,0 +1,10 @@ |
|||
version: 2 |
|||
|
|||
build: |
|||
os: "ubuntu-20.04" |
|||
tools: |
|||
python: "3.10" |
|||
|
|||
python: |
|||
install: |
|||
- requirements: docs-requirements.txt |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"plugins": ["@semantic-release/commit-analyzer"], |
|||
"verifyConditions": false, |
|||
"npmPublish": false, |
|||
"publish": false, |
|||
"fail": false, |
|||
"success": false |
|||
} |
@ -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 |
@ -0,0 +1 @@ |
|||
* @ryshoooo @marcospereirampj |
@ -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. |
@ -1 +1,4 @@ |
|||
include LICENSE |
|||
include requirements.txt |
|||
include dev-requirements.txt |
|||
include docs-requirements.txt |
@ -0,0 +1,5 @@ |
|||
tox |
|||
pytest |
|||
pytest-cov |
|||
wheel |
|||
pre-commit |
@ -0,0 +1,9 @@ |
|||
mock |
|||
alabaster |
|||
commonmark |
|||
recommonmark |
|||
sphinx |
|||
sphinx-rtd-theme |
|||
readthedocs-sphinx-ext |
|||
m2r2 |
|||
sphinx-autoapi |
@ -0,0 +1 @@ |
|||
__version__ = "0.0.0" |
606
keycloak/keycloak_admin.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,191 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> |
|||
# |
|||
# 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 <http://www.gnu.org/licenses/>. |
|||
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() |
@ -0,0 +1,6 @@ |
|||
[tool.black] |
|||
line-length = 99 |
|||
|
|||
[tool.isort] |
|||
line_length = 99 |
|||
profile = "black" |
@ -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 |
|||
urllib3>=1.26.0 |
@ -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", |
|||
], |
|||
) |
@ -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} |
@ -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) |
1201
tests/test_keycloak_admin.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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) |
@ -0,0 +1,4 @@ |
|||
KEYCLOAK_ADMIN=admin |
|||
KEYCLOAK_ADMIN_PASSWORD=admin |
|||
KEYCLOAK_HOST={env:KEYCLOAK_HOST:localhost} |
|||
KEYCLOAK_PORT=8080 |
@ -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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue