Browse Source

fix: Ci/fix tests (#506)

* chore: deps update

* test: fix tests

* ci: update

* fix: dependencies

* ci: drop python 3.7

* fix: docs, lint etc

* test: fixed exchange

* chore: docs lint

* ci: ignore e231

* fix: upgrade sphinx

* docs: read the docs updated

* chore: removed setuptools
pull/508/head v3.6.1
Richard Nemeth 1 year ago
committed by GitHub
parent
commit
03c317c68b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/workflows/daily.yaml
  2. 18
      .github/workflows/lint.yaml
  3. 4
      .github/workflows/publish.yaml
  4. 4
      .readthedocs.yaml
  5. 1374
      poetry.lock
  6. 9
      pyproject.toml
  7. 4
      src/keycloak/_version.py
  8. 6
      src/keycloak/connection.py
  9. 52
      src/keycloak/keycloak_admin.py
  10. 8
      src/keycloak/keycloak_openid.py
  11. 3
      src/keycloak/openid_connection.py
  12. 1
      src/keycloak/urls_patterns.py
  13. 57
      tests/test_keycloak_admin.py
  14. 2
      tox.env
  15. 2
      tox.ini

4
.github/workflows/daily.yaml

@ -10,8 +10,8 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
keycloak-version: ["20.0", "21.0", "latest"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
keycloak-version: ["20.0", "21.0", "22.0", "latest"]
env: env:
KEYCLOAK_DOCKER_IMAGE_TAG: ${{ matrix.keycloak-version }} KEYCLOAK_DOCKER_IMAGE_TAG: ${{ matrix.keycloak-version }}
steps: steps:

18
.github/workflows/lint.yaml

@ -11,16 +11,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: webiny/action-conventional-commits@v1.0.3
- uses: webiny/action-conventional-commits@v1.1.0
check-linting: check-linting:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
@ -37,10 +37,10 @@ jobs:
- check-linting - check-linting
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
@ -55,8 +55,8 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
keycloak-version: ["20.0", "21.0", "latest"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
keycloak-version: ["20.0", "21.0", "22.0", "latest"]
needs: needs:
- check-commits - check-commits
- check-linting - check-linting
@ -88,10 +88,10 @@ jobs:
- check-docs - check-docs
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip

4
.github/workflows/publish.yaml

@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: "0" fetch-depth: "0"
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip

4
.readthedocs.yaml

@ -1,9 +1,9 @@
version: 2 version: 2
build: build:
os: "ubuntu-20.04"
os: "ubuntu-22.04"
tools: tools:
python: "3.10"
python: "3.12"
jobs: jobs:
post_install: post_install:
- pip install -U poetry - pip install -U poetry

1374
poetry.lock
File diff suppressed because it is too large
View File

9
pyproject.toml

@ -29,20 +29,19 @@ Documentation = "https://python-keycloak.readthedocs.io/en/latest/"
"Issue tracker" = "https://github.com/marcospereirampj/python-keycloak/issues" "Issue tracker" = "https://github.com/marcospereirampj/python-keycloak/issues"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">=3.7,<4.0"
setuptools = "*"
python = ">=3.8,<4.0"
requests = ">=2.20.0" requests = ">=2.20.0"
python-jose = ">=3.3.0" python-jose = ">=3.3.0"
mock = {version = "^4.0.3", optional = true} mock = {version = "^4.0.3", optional = true}
alabaster = {version = "^0.7.12", optional = true} alabaster = {version = "^0.7.12", optional = true}
commonmark = {version = "^0.9.1", optional = true} commonmark = {version = "^0.9.1", optional = true}
recommonmark = {version = "^0.7.1", optional = true} recommonmark = {version = "^0.7.1", optional = true}
Sphinx = {version = "^5.3.0", optional = true}
Sphinx = {version = "^6.1.0", optional = true}
sphinx-rtd-theme = {version = "^1.0.0", optional = true} sphinx-rtd-theme = {version = "^1.0.0", optional = true}
readthedocs-sphinx-ext = {version = "^2.1.9", optional = true} readthedocs-sphinx-ext = {version = "^2.1.9", optional = true}
m2r2 = {version = "^0.3.2", optional = true} m2r2 = {version = "^0.3.2", optional = true}
sphinx-autoapi = {version = "^2.0.0", optional = true}
requests-toolbelt = "*"
sphinx-autoapi = {version = "^3.0.0", optional = true}
requests-toolbelt = ">=0.6.0"
deprecation = ">=2.1.0" deprecation = ">=2.1.0"
[tool.poetry.extras] [tool.poetry.extras]

4
src/keycloak/_version.py

@ -21,6 +21,6 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # 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 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import pkg_resources
from importlib import metadata
__version__ = pkg_resources.get_distribution("python-keycloak").version
__version__ = metadata.version("python-keycloak")

6
src/keycloak/connection.py

@ -43,7 +43,8 @@ class ConnectionManager(object):
:type headers: dict :type headers: dict
:param timeout: Timeout to use for requests to the server. :param timeout: Timeout to use for requests to the server.
:type timeout: int :type timeout: int
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param proxies: The proxies servers requests is sent by. :param proxies: The proxies servers requests is sent by.
:type proxies: dict :type proxies: dict
@ -58,7 +59,8 @@ class ConnectionManager(object):
:type headers: dict :type headers: dict
:param timeout: Timeout to use for requests to the server. :param timeout: Timeout to use for requests to the server.
:type timeout: int :type timeout: int
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param proxies: The proxies servers requests is sent by. :param proxies: The proxies servers requests is sent by.
:type proxies: dict :type proxies: dict

52
src/keycloak/keycloak_admin.py

@ -63,7 +63,8 @@ class KeycloakAdmin:
:type realm_name: str :type realm_name: str
:param client_id: client id :param client_id: client id
:type client_id: str :type client_id: str
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param client_secret_key: client secret key :param client_secret_key: client secret key
(optional, required only for access type confidential) (optional, required only for access type confidential)
@ -119,7 +120,8 @@ class KeycloakAdmin:
:type realm_name: str :type realm_name: str
:param client_id: client id :param client_id: client id
:type client_id: str :type client_id: str
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param client_secret_key: client secret key :param client_secret_key: client secret key
(optional, required only for access type confidential) (optional, required only for access type confidential)
@ -1239,7 +1241,7 @@ class KeycloakAdmin:
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def get_server_info(self): def get_server_info(self):
"""Get themes, social providers, auth providers, and event listeners available on this server.
"""Get themes, social providers, etc. on this server.
ServerInfoRepresentation ServerInfoRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation
@ -1783,48 +1785,6 @@ class KeycloakAdmin:
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
) )
def create_client_authz_scope_based_permission(self, client_id, payload, skip_exists=False):
"""Create scope-based permission of client.
Payload example::
payload={
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"name": "Permission-Name",
"resources": [
resource_id
],
"policies": [
policy_id
],
"scopes": [
scope_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}
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_BASED_PERMISSION.format(**params_path),
data=json.dumps(payload),
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
)
def get_client_authz_scopes(self, client_id): def get_client_authz_scopes(self, client_id):
"""Get scopes from client. """Get scopes from client.
@ -4272,7 +4232,7 @@ class KeycloakAdmin:
urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path), urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
data=json.dumps(payload), data=json.dumps(payload),
) )
return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
def update_client_authz_scope_permission(self, payload, client_id, scope_id): def update_client_authz_scope_permission(self, payload, client_id, scope_id):
"""Update permissions for a given scope. """Update permissions for a given scope.

8
src/keycloak/keycloak_openid.py

@ -69,7 +69,8 @@ class KeycloakOpenID:
:param client_id: client id :param client_id: client id
:param realm_name: realm name :param realm_name: realm name
:param client_secret_key: client secret key :param client_secret_key: client secret key
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:param custom_headers: dict of custom header to pass to each HTML request :param custom_headers: dict of custom header to pass to each HTML request
:param proxies: dict of proxies to sent the request by. :param proxies: dict of proxies to sent the request by.
:param timeout: connection timeout in seconds :param timeout: connection timeout in seconds
@ -96,7 +97,8 @@ class KeycloakOpenID:
:type realm_name: str :type realm_name: str
:param client_secret_key: client secret key :param client_secret_key: client secret key
:type client_secret_key: str :type client_secret_key: str
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param custom_headers: dict of custom header to pass to each HTML request :param custom_headers: dict of custom header to pass to each HTML request
:type custom_headers: dict :type custom_headers: dict
@ -715,7 +717,6 @@ class KeycloakOpenID:
) )
return raise_error_from_response(data_raw, KeycloakPostError) return raise_error_from_response(data_raw, KeycloakPostError)
def device(self): def device(self):
"""Get device authorization grant. """Get device authorization grant.
@ -743,7 +744,6 @@ class KeycloakOpenID:
data_raw = self.connection.raw_post(URL_DEVICE.format(**params_path), data=payload) data_raw = self.connection.raw_post(URL_DEVICE.format(**params_path), data=payload)
return raise_error_from_response(data_raw, KeycloakPostError) return raise_error_from_response(data_raw, KeycloakPostError)
def update_client(self, token: str, client_id: str, payload: dict): def update_client(self, token: str, client_id: str, payload: dict):
"""Update a client. """Update a client.

3
src/keycloak/openid_connection.py

@ -87,7 +87,8 @@ class KeycloakOpenIDConnection(ConnectionManager):
:type realm_name: str :type realm_name: str
:param client_id: client id :param client_id: client id
:type client_id: str :type client_id: str
:param verify: Boolean value to enable or disable certificate validation or a string containing a path to a CA bundle to use
:param verify: Boolean value to enable or disable certificate validation or a string
containing a path to a CA bundle to use
:type verify: Union[bool,str] :type verify: Union[bool,str]
:param client_secret_key: client secret key :param client_secret_key: client secret key
(optional, required only for access type confidential) (optional, required only for access type confidential)

1
src/keycloak/urls_patterns.py

@ -122,7 +122,6 @@ URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/rol
URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = ( URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = (
URL_ADMIN_CLIENT_AUTHZ + "/permission/resource?max=-1" URL_ADMIN_CLIENT_AUTHZ + "/permission/resource?max=-1"
) )
URL_ADMIN_CLIENT_AUTHZ_SCOPE_BASED_PERMISSION = URL_ADMIN_CLIENT_AUTHZ + "/permission/scope?max=-1"
URL_ADMIN_CLIENT_AUTHZ_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/{policy-id}" URL_ADMIN_CLIENT_AUTHZ_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/{policy-id}"
URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/scopes" URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/scopes"
URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/resources" URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/resources"

57
tests/test_keycloak_admin.py

@ -596,6 +596,7 @@ def test_server_info(admin: KeycloakAdmin):
"systemInfo", "systemInfo",
"memoryInfo", "memoryInfo",
"profileInfo", "profileInfo",
"features",
"themes", "themes",
"socialProviders", "socialProviders",
"identityProviders", "identityProviders",
@ -1045,7 +1046,6 @@ def test_clients(admin: KeycloakAdmin, realm: str):
client_id=auth_client_id, payload={"name": "test-authz-scope"} client_id=auth_client_id, payload={"name": "test-authz-scope"}
) )
assert res["name"] == "test-authz-scope", res assert res["name"] == "test-authz-scope", res
test_scope_id = res["id"]
with pytest.raises(KeycloakPostError) as err: with pytest.raises(KeycloakPostError) as err:
admin.create_client_authz_scopes( admin.create_client_authz_scopes(
@ -1060,40 +1060,6 @@ def test_clients(admin: KeycloakAdmin, realm: str):
assert len(res) == 1 assert len(res) == 1
assert {x["name"] for x in res} == {"test-authz-scope"} assert {x["name"] for x in res} == {"test-authz-scope"}
res = admin.create_client_authz_scope_based_permission(
client_id=auth_client_id,
payload={
"name": "test-permission-sb",
"resources": [test_resource_id],
"scopes": [test_scope_id],
},
)
assert res, res
assert res["name"] == "test-permission-sb"
assert res["resources"] == [test_resource_id]
assert res["scopes"] == [test_scope_id]
with pytest.raises(KeycloakPostError) as err:
admin.create_client_authz_scope_based_permission(
client_id=auth_client_id,
payload={
"name": "test-permission-sb",
"resources": [test_resource_id],
"scopes": [test_scope_id],
},
)
assert err.match('409: b\'{"error":"Policy with name')
assert admin.create_client_authz_scope_based_permission(
client_id=auth_client_id,
payload={
"name": "test-permission-sb",
"resources": [test_resource_id],
"scopes": [test_scope_id],
},
skip_exists=True,
) == {"msg": "Already exists"}
assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 3
# Test service account user # Test service account user
res = admin.get_client_service_account_user(client_id=auth_client_id) res = admin.get_client_service_account_user(client_id=auth_client_id)
assert res["username"] == "service-account-authz-client", res assert res["username"] == "service-account-authz-client", res
@ -1882,7 +1848,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
# Create permissions on the target client to reference this policy # Create permissions on the target client to reference this policy
admin.create_client_authz_scope_permission( admin.create_client_authz_scope_permission(
payload={ payload={
"id": token_exchange_permission_id,
"id": "some-id",
"name": "test-permission", "name": "test-permission",
"type": "scope", "type": "scope",
"logic": "POSITIVE", "logic": "POSITIVE",
@ -1896,13 +1862,13 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
permission_name = admin.get_client_authz_scope_permission( permission_name = admin.get_client_authz_scope_permission(
client_id=realm_management_id, scope_id=token_exchange_permission_id client_id=realm_management_id, scope_id=token_exchange_permission_id
)["name"] )["name"]
assert permission_name == "test-permission"
assert permission_name.startswith("token-exchange.permission.client.")
with pytest.raises(KeycloakPostError) as err: with pytest.raises(KeycloakPostError) as err:
admin.create_client_authz_scope_permission( admin.create_client_authz_scope_permission(
payload={"name": "test-permission", "scopes": [token_exchange_scope_id]}, payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
client_id="realm_management_id", client_id="realm_management_id",
) )
assert err.match('404: b\'{"errorMessage":"Could not find client"}\'')
assert err.match('404: b\'{"error":"Could not find client"}\'')
def test_email(admin: KeycloakAdmin, user: str): def test_email(admin: KeycloakAdmin, user: str):
@ -1973,6 +1939,7 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str):
admin.realm_name = realm admin.realm_name = realm
res = admin.get_authentication_flows() res = admin.get_authentication_flows()
assert len(res) <= 8, res
default_flows = len(res) default_flows = len(res)
assert {x["alias"] for x in res}.issubset( assert {x["alias"] for x in res}.issubset(
{ {
@ -1995,6 +1962,18 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str):
"providerId", "providerId",
"topLevel", "topLevel",
} }
assert {x["alias"] for x in res}.issubset(
{
"reset credentials",
"browser",
"registration",
"docker auth",
"direct grant",
"first broker login",
"clients",
"http challenge",
}
)
with pytest.raises(KeycloakGetError) as err: with pytest.raises(KeycloakGetError) as err:
admin.get_authentication_flow_for_id(flow_id="bad") admin.get_authentication_flow_for_id(flow_id="bad")
@ -2129,7 +2108,7 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str):
# Test list of auth providers # Test list of auth providers
res = admin.get_authenticator_providers() res = admin.get_authenticator_providers()
assert len(res) > 1
assert len(res) <= 38
res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie") res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
assert res == { assert res == {

2
tox.env

@ -1,4 +1,4 @@
KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOST={env:KEYCLOAK_HOST:localhost} KEYCLOAK_HOST={env:KEYCLOAK_HOST:localhost}
KEYCLOAK_PORT=8080
KEYCLOAK_PORT=8081

2
tox.ini

@ -47,7 +47,7 @@ commands =
[flake8] [flake8]
max-line-length = 99 max-line-length = 99
docstring-convention = all docstring-convention = all
ignore = D203, D213, W503
ignore = D203, D213, W503, E231
docstring_style = sphinx docstring_style = sphinx
[darglint] [darglint]

Loading…
Cancel
Save