No known key found for this signature in database
GPG Key ID: 21C39470DF3DEC39
19 changed files with 213 additions and 280 deletions
-
37.circleci/config.yml
-
29.github/workflows/lint.yaml
-
2.gitignore
-
16.pre-commit-config.yaml
-
15CONTRIBUTING.md
-
70docs/source/conf.py
-
1docs/source/index.rst
-
2requirements.txt
-
4setup.py
-
35test_keycloak_init.sh
-
14tests/conftest.py
-
189tests/test_connection.py
-
9tests/test_keycloak_admin.py
-
4tox.env
-
32tox.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,29 @@ |
|||||
|
name: Python package linting |
||||
|
|
||||
|
on: |
||||
|
push: |
||||
|
branches: [ master ] |
||||
|
pull_request: |
||||
|
branches: [ master ] |
||||
|
|
||||
|
jobs: |
||||
|
build: |
||||
|
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 }} |
||||
|
- name: Install dependencies |
||||
|
run: | |
||||
|
python -m pip install --upgrade pip |
||||
|
python -m pip install tox |
||||
|
- name: Check linting, formatting |
||||
|
run: | |
||||
|
tox -e check |
||||
@ -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,15 @@ |
|||||
|
# Contributing |
||||
|
|
||||
|
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: |
||||
|
|
||||
|
```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 |
||||
|
``` |
||||
@ -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:8080; do |
||||
|
sleep 5; |
||||
|
if [ ${SECONDS} -gt 180 ]; then |
||||
|
echo "Timeout exceeded"; |
||||
|
exit 1; |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
# Ensuring that postgres 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,14 @@ |
|||||
|
import os |
||||
|
|
||||
|
import pytest |
||||
|
|
||||
|
|
||||
|
@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() |
||||
@ -1,189 +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 keycloak.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,9 @@ |
|||||
|
from keycloak import KeycloakAdmin |
||||
|
|
||||
|
|
||||
|
def test_keycloak_admin_init(env): |
||||
|
KeycloakAdmin( |
||||
|
server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", |
||||
|
username=env.KEYCLOAK_ADMIN, |
||||
|
password=env.KEYCLOAK_ADMIN_PASSWORD, |
||||
|
) |
||||
@ -0,0 +1,4 @@ |
|||||
|
KEYCLOAK_ADMIN=admin |
||||
|
KEYCLOAK_ADMIN_PASSWORD=admin |
||||
|
KEYCLOAK_HOST={env:KEYCLOAK_HOST:localhost} |
||||
|
KEYCLOAK_PORT=8080 |
||||
@ -0,0 +1,32 @@ |
|||||
|
[tox] |
||||
|
envlist = check, docs, tests |
||||
|
|
||||
|
[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:tests] |
||||
|
deps = |
||||
|
-rrequirements.txt |
||||
|
setenv = file|tox.env |
||||
|
commands = |
||||
|
./test_keycloak_init.sh "pytest -v --cov=keycloak --cov-report term-missing {posargs}" |
||||
|
|
||||
|
[flake8] |
||||
|
max-line-length = 99 |
||||
|
|
||||
|
[black] |
||||
|
line-length = 99 |
||||
|
|
||||
|
[isort] |
||||
|
line_length = 99 |
||||
|
profile = "black" |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue