You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6256 lines
224 KiB
6256 lines
224 KiB
"""Test the keycloak admin object."""
|
|
|
|
import copy
|
|
import os
|
|
import uuid
|
|
from inspect import iscoroutinefunction, signature
|
|
from typing import Tuple
|
|
|
|
import freezegun
|
|
import pytest
|
|
from dateutil import parser as datetime_parser
|
|
from packaging.version import Version
|
|
|
|
import keycloak
|
|
from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakOpenIDConnection
|
|
from keycloak.connection import ConnectionManager
|
|
from keycloak.exceptions import (
|
|
KeycloakAuthenticationError,
|
|
KeycloakDeleteError,
|
|
KeycloakGetError,
|
|
KeycloakPostError,
|
|
KeycloakPutError,
|
|
)
|
|
|
|
CLIENT_NOT_FOUND_REGEX = '404: b\'{"error":"Client not found".*}\''
|
|
CLIENT_SCOPE_NOT_FOUND_REGEX = '404: b\'{"error":"Client scope not found".*}\''
|
|
COULD_NOT_FIND_ROLE_REGEX = '404: b\'{"error":"Could not find role".*}\''
|
|
COULD_NOT_FIND_ROLE_WITH_ID_REGEX = '404: b\'{"error":"Could not find role with id".*}\''
|
|
HTTP_404_REGEX = '404: b\'{"error":"HTTP 404 Not Found".*}\''
|
|
ILLEGAL_EXECUTION_REGEX = '404: b\'{"error":"Illegal execution".*}\''
|
|
NO_CLIENT_SCOPE_REGEX = '404: b\'{"error":"Could not find client scope".*}\''
|
|
UNKOWN_ERROR_REGEX = 'b\'{"error":"unknown_error".*}\''
|
|
USER_NOT_FOUND_REGEX = '404: b\'{"error":"User not found".*}\''
|
|
|
|
|
|
def test_keycloak_version():
|
|
"""Test version."""
|
|
assert keycloak.__version__, keycloak.__version__
|
|
|
|
|
|
def test_keycloak_admin_init(env):
|
|
"""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,
|
|
password=env.KEYCLOAK_ADMIN_PASSWORD,
|
|
)
|
|
assert (
|
|
admin.connection.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}"
|
|
), admin.connection.server_url
|
|
assert admin.connection.realm_name == "master", admin.connection.realm_name
|
|
assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
|
|
assert admin.connection.client_id == "admin-cli", admin.connection.client_id
|
|
assert admin.connection.client_secret_key is None, admin.connection.client_secret_key
|
|
assert admin.connection.verify, admin.connection.verify
|
|
assert admin.connection.username == env.KEYCLOAK_ADMIN, admin.connection.username
|
|
assert admin.connection.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.connection.password
|
|
assert admin.connection.totp is None, admin.connection.totp
|
|
assert admin.connection.token is None, admin.connection.token
|
|
assert admin.connection.user_realm_name is None, admin.connection.user_realm_name
|
|
assert admin.connection.custom_headers is None, admin.connection.custom_headers
|
|
|
|
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.connection.token is None
|
|
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.connection.token is None
|
|
|
|
admin.get_realms()
|
|
token = admin.connection.token
|
|
admin = KeycloakAdmin(
|
|
server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
|
|
token=token,
|
|
realm_name=None,
|
|
user_realm_name=None,
|
|
)
|
|
assert admin.connection.token == token
|
|
|
|
admin.create_realm(payload={"realm": "authz", "enabled": True})
|
|
admin.connection.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"))
|
|
adminAuth = KeycloakAdmin(
|
|
server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
|
|
user_realm_name="authz",
|
|
client_id="authz-client",
|
|
client_secret_key=secret["value"],
|
|
)
|
|
adminAuth.connection.refresh_token()
|
|
assert adminAuth.connection.token is not None
|
|
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"},
|
|
).connection.token
|
|
is None
|
|
)
|
|
|
|
keycloak_connection = KeycloakOpenIDConnection(
|
|
server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
|
|
username=env.KEYCLOAK_ADMIN,
|
|
password=env.KEYCLOAK_ADMIN_PASSWORD,
|
|
realm_name="master",
|
|
client_id="admin-cli",
|
|
verify=True,
|
|
)
|
|
keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
|
|
keycloak_admin.connection.get_token()
|
|
assert keycloak_admin.connection.token
|
|
|
|
|
|
def test_realms(admin: KeycloakAdmin):
|
|
"""Test realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type 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_changing_of_realms(admin: KeycloakAdmin, realm: str):
|
|
"""Test changing of realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
assert admin.get_current_realm() == "master"
|
|
admin.change_current_realm(realm)
|
|
assert admin.get_current_realm() == realm
|
|
|
|
|
|
def test_import_export_realms(admin: KeycloakAdmin, realm: str):
|
|
"""Test import and export of realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'' # noqa: E501
|
|
)
|
|
|
|
|
|
def test_partial_import_realm(admin: KeycloakAdmin, realm: str):
|
|
"""Test partial import of realm configuration.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
test_realm_role = str(uuid.uuid4())
|
|
test_user = str(uuid.uuid4())
|
|
test_client = str(uuid.uuid4())
|
|
|
|
admin.change_current_realm(realm)
|
|
client_id = admin.create_client(payload={"name": test_client, "clientId": test_client})
|
|
|
|
realm_export = admin.export_realm(export_clients=True, export_groups_and_role=False)
|
|
|
|
client_config = [
|
|
client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
|
|
][0]
|
|
|
|
# delete before partial import
|
|
admin.delete_client(client_id)
|
|
|
|
payload = {
|
|
"ifResourceExists": "SKIP",
|
|
"id": realm_export["id"],
|
|
"realm": realm,
|
|
"clients": [client_config],
|
|
"roles": {"realm": [{"name": test_realm_role}]},
|
|
"users": [{"username": test_user, "email": f"{test_user}@test.test"}],
|
|
}
|
|
|
|
# check add
|
|
res = admin.partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["added"] == 3
|
|
|
|
# check skip
|
|
res = admin.partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["skipped"] == 3
|
|
|
|
# check overwrite
|
|
payload["ifResourceExists"] = "OVERWRITE"
|
|
res = admin.partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["overwritten"] == 3
|
|
|
|
|
|
def test_users(admin: KeycloakAdmin, realm: str):
|
|
"""Test users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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(".*User exists with same.*")
|
|
|
|
# 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 disable user
|
|
res = admin.disable_user(user_id=user_id)
|
|
assert res == {}, res
|
|
assert not admin.get_user(user_id=user_id)["enabled"]
|
|
|
|
# Test enable user
|
|
res = admin.enable_user(user_id=user_id)
|
|
assert res == {}, res
|
|
assert admin.get_user(user_id=user_id)["enabled"]
|
|
|
|
# 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 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
|
|
|
|
# Test user groups bad id
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_user_groups(user_id="does-not-exist")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
# 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(USER_NOT_FOUND_REGEX)
|
|
|
|
# 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(USER_NOT_FOUND_REGEX)
|
|
|
|
# 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(USER_NOT_FOUND_REGEX)
|
|
|
|
# Test delete fail
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
admin.delete_user(user_id="non-existent-id")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
|
|
def test_enable_disable_all_users(admin: KeycloakAdmin, realm: str):
|
|
"""Test enable and disable all users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
user_id_1 = admin.create_user(
|
|
payload={"username": "test", "email": "test@test.test", "enabled": True}
|
|
)
|
|
user_id_2 = admin.create_user(
|
|
payload={"username": "test2", "email": "test2@test.test", "enabled": True}
|
|
)
|
|
user_id_3 = admin.create_user(
|
|
payload={"username": "test3", "email": "test3@test.test", "enabled": True}
|
|
)
|
|
|
|
assert admin.get_user(user_id_1)["enabled"]
|
|
assert admin.get_user(user_id_2)["enabled"]
|
|
assert admin.get_user(user_id_3)["enabled"]
|
|
|
|
admin.disable_all_users()
|
|
|
|
assert not admin.get_user(user_id_1)["enabled"]
|
|
assert not admin.get_user(user_id_2)["enabled"]
|
|
assert not admin.get_user(user_id_3)["enabled"]
|
|
|
|
admin.enable_all_users()
|
|
|
|
assert admin.get_user(user_id_1)["enabled"]
|
|
assert admin.get_user(user_id_2)["enabled"]
|
|
assert admin.get_user(user_id_3)["enabled"]
|
|
|
|
|
|
def test_users_roles(admin: KeycloakAdmin, realm: str):
|
|
"""Test users roles.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
|
|
|
|
# Test all level user roles
|
|
client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
|
|
admin.create_client_role(client_role_id=client_id, payload={"name": "test-role"})
|
|
admin.assign_client_role(
|
|
client_id=client_id,
|
|
user_id=user_id,
|
|
roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
|
|
)
|
|
all_roles = admin.get_all_roles_of_user(user_id=user_id)
|
|
realm_roles = all_roles["realmMappings"]
|
|
assert len(realm_roles) == 1, realm_roles
|
|
client_roles = all_roles["clientMappings"]
|
|
assert len(client_roles) == 1, client_roles
|
|
|
|
# Test all level user roles fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_all_roles_of_user(user_id="non-existent-id")
|
|
err.match('404: b\'{"error":"User not found"')
|
|
|
|
admin.delete_user(user_id)
|
|
admin.delete_client(client_id)
|
|
|
|
|
|
def test_users_pagination(admin: KeycloakAdmin, realm: str):
|
|
"""Test user pagination.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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_user_groups_pagination(admin: KeycloakAdmin, realm: str):
|
|
"""Test user groups pagination.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
user_id = admin.create_user(
|
|
payload={"username": "username_1", "email": "username_1@test.test"}
|
|
)
|
|
|
|
for ind in range(admin.PAGE_SIZE + 50):
|
|
group_name = f"group_{ind}"
|
|
group_id = admin.create_group(payload={"name": group_name})
|
|
admin.group_user_add(user_id=user_id, group_id=group_id)
|
|
|
|
groups = admin.get_user_groups(user_id=user_id)
|
|
assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
|
|
|
|
groups = admin.get_user_groups(user_id=user_id, query={"first": 100, "max": -1, "search": ""})
|
|
assert len(groups) == 50, len(groups)
|
|
|
|
groups = admin.get_user_groups(user_id=user_id, query={"max": 20, "first": -1, "search": ""})
|
|
assert len(groups) == 20, len(groups)
|
|
|
|
|
|
def test_idps(admin: KeycloakAdmin, realm: str):
|
|
"""Test IDPs.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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 get idp
|
|
idp = admin.get_idp("github")
|
|
assert "github" == idp["alias"]
|
|
assert idp.get("config")
|
|
assert "test" == idp["config"]["clientId"]
|
|
assert "**********" == idp["config"]["clientSecret"]
|
|
|
|
# Test get idp fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_idp("does-not-exist")
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
# 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",
|
|
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(HTTP_404_REGEX)
|
|
|
|
# Test IdP mappers listing
|
|
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 res == dict(), res
|
|
|
|
# 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(HTTP_404_REGEX)
|
|
|
|
|
|
def test_user_credentials(admin: KeycloakAdmin, user: str):
|
|
"""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
|
|
|
|
# Test user password set fail
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
admin.set_user_password(user_id="does-not-exist", password="")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
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(USER_NOT_FOUND_REGEX)
|
|
|
|
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):
|
|
"""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"
|
|
)
|
|
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(USER_NOT_FOUND_REGEX)
|
|
|
|
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(USER_NOT_FOUND_REGEX)
|
|
|
|
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):
|
|
"""Test server info.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
info = admin.get_server_info()
|
|
assert set(info.keys()).issubset(
|
|
{
|
|
"systemInfo",
|
|
"memoryInfo",
|
|
"profileInfo",
|
|
"features",
|
|
"themes",
|
|
"socialProviders",
|
|
"identityProviders",
|
|
"providers",
|
|
"protocolMapperTypes",
|
|
"builtinProtocolMappers",
|
|
"clientInstallations",
|
|
"componentTypes",
|
|
"passwordPolicies",
|
|
"enums",
|
|
"cryptoInfo",
|
|
"features",
|
|
}
|
|
), info.keys()
|
|
|
|
|
|
def test_groups(admin: KeycloakAdmin, user: str):
|
|
"""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
|
|
|
|
# Test create group
|
|
group_id = admin.create_group(payload={"name": "main-group"})
|
|
assert group_id is not None, group_id
|
|
|
|
# Test group count
|
|
count = admin.groups_count()
|
|
assert count.get("count") == 1, count
|
|
|
|
# Test group count with query
|
|
count = admin.groups_count(query={"search": "notpresent"})
|
|
assert count.get("count") == 0
|
|
|
|
# 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"), 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[0]["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[0]["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
|
|
subgroup_2 = admin.get_group(group_id=subgroup_id_2)
|
|
res = admin.get_subgroups(group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1")
|
|
assert res is not None, res
|
|
assert res["id"] == subsubgroup_id_1
|
|
|
|
# Test nested search from main group
|
|
res = admin.get_subgroups(
|
|
group=admin.get_group(group_id=group_id, full_hierarchy=True),
|
|
path="/main-group/subgroup-2/subsubgroup-1",
|
|
)
|
|
assert res["id"] == subsubgroup_id_1
|
|
|
|
# Test nested search from all groups
|
|
res = admin.get_groups(full_hierarchy=True)
|
|
assert len(res) == 1
|
|
assert len(res[0]["subGroups"]) == 2
|
|
assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1][0]["subGroups"]) == 0
|
|
assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2][0]["subGroups"]) == 1
|
|
|
|
# Test that query params are not allowed for full hierarchy
|
|
with pytest.raises(ValueError) as err:
|
|
admin.get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
|
|
|
|
# Test that query params are passed
|
|
if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
|
|
os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"]
|
|
) >= Version("23"):
|
|
res = admin.get_group_children(group_id=group_id, query={"max": 1})
|
|
assert len(res) == 1
|
|
|
|
assert err.match("Cannot use both query and full_hierarchy parameters")
|
|
|
|
main_group_id_2 = admin.create_group(payload={"name": "main-group-2"})
|
|
assert len(admin.get_groups(full_hierarchy=True)) == 2
|
|
|
|
# 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 not None, res
|
|
assert res["id"] == subgroup_id_1, res
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
|
|
assert err.match('404: b\'{"error":"Group path does not exist".*}\'')
|
|
|
|
res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
|
|
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(USER_NOT_FOUND_REGEX), 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(UNKOWN_ERROR_REGEX), 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
|
|
res = admin.delete_group(group_id=main_group_id_2)
|
|
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):
|
|
"""Test clients.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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_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"})
|
|
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 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 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={
|
|
"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(HTTP_404_REGEX)
|
|
|
|
# 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(HTTP_404_REGEX)
|
|
|
|
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"]
|
|
|
|
res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=test_resource_id)
|
|
assert res["_id"] == test_resource_id, res
|
|
assert res["name"] == "test-resource", res
|
|
|
|
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"}
|
|
|
|
res = admin.create_client_authz_resource(
|
|
client_id=auth_client_id, payload={"name": "temp-resource"}
|
|
)
|
|
assert res["name"] == "temp-resource", res
|
|
temp_resource_id: str = res["_id"]
|
|
# Test update authz resources
|
|
admin.update_client_authz_resource(
|
|
client_id=auth_client_id,
|
|
resource_id=temp_resource_id,
|
|
payload={"name": "temp-updated-resource"},
|
|
)
|
|
res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
|
|
assert res["name"] == "temp-updated-resource", res
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
admin.update_client_authz_resource(
|
|
client_id=auth_client_id,
|
|
resource_id="invalid_resource_id",
|
|
payload={"name": "temp-updated-resource"},
|
|
)
|
|
assert err.match("404: b''"), err
|
|
admin.delete_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
|
|
assert err.match("404: b''")
|
|
|
|
# Authz policies
|
|
res = admin.get_client_authz_policies(client_id=auth_client_id)
|
|
assert len(res) == 1, res
|
|
assert res[0]["name"] == "Default Policy"
|
|
|
|
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
|
|
role_based_policy_id = res["id"]
|
|
role_based_policy_name = res["name"]
|
|
|
|
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
|
|
|
|
res = admin.create_client_authz_role_based_policy(
|
|
client_id=auth_client_id,
|
|
payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
|
|
)
|
|
res2 = admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
assert res["id"] == res2["id"]
|
|
admin.delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
assert err.match("404: b''")
|
|
|
|
res = admin.create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
)
|
|
assert res["name"] == "test-authz-policy", res
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
admin.create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
)
|
|
assert err.match('409: b\'{"error":"Policy with name')
|
|
assert admin.create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
skip_exists=True,
|
|
) == {"msg": "Already exists"}
|
|
assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 3
|
|
|
|
# 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"
|
|
|
|
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]
|
|
resource_based_permission_id = res["id"]
|
|
resource_based_permission_name = res["name"]
|
|
|
|
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 associating client policy with resource based permission
|
|
res = admin.update_client_authz_resource_permission(
|
|
client_id=auth_client_id,
|
|
resource_id=resource_based_permission_id,
|
|
payload={
|
|
"id": resource_based_permission_id,
|
|
"name": resource_based_permission_name,
|
|
"type": "resource",
|
|
"logic": "POSITIVE",
|
|
"decisionStrategy": "UNANIMOUS",
|
|
"resources": [test_resource_id],
|
|
"scopes": [],
|
|
"policies": [role_based_policy_id],
|
|
},
|
|
)
|
|
|
|
# Test getting associated policies for a permission
|
|
associated_policies = admin.get_client_authz_permission_associated_policies(
|
|
client_id=auth_client_id, policy_id=resource_based_permission_id
|
|
)
|
|
assert len(associated_policies) == 1
|
|
assert associated_policies[0]["name"].startswith(role_based_policy_name)
|
|
|
|
# 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(HTTP_404_REGEX)
|
|
|
|
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="invalid_client_id", payload={"name": "test-authz-scope"}
|
|
)
|
|
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"}
|
|
)
|
|
|
|
res = admin.get_client_authz_scopes(client_id=auth_client_id)
|
|
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)
|
|
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 ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
|
|
UNKOWN_ERROR_REGEX
|
|
)
|
|
|
|
# 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".*}\'')
|
|
|
|
# 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_id="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_id="test-confidential")
|
|
)
|
|
assert res
|
|
assert (
|
|
admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
|
|
== res
|
|
)
|
|
|
|
|
|
def test_realm_roles(admin: KeycloakAdmin, realm: str):
|
|
"""Test realm roles.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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 get realm roles with search text
|
|
searched_roles = admin.get_realm_roles(search_text="uma_a")
|
|
searched_role_names = [x["name"] for x in searched_roles]
|
|
assert "uma_authorization" in searched_role_names, searched_role_names
|
|
assert "offline_access" not in searched_role_names, searched_role_names
|
|
|
|
# Test empty members
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_realm_role_members(role_name="does-not-exist")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
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"}, skip_exists=True)
|
|
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 get realm role by its id
|
|
role_id = admin.get_realm_role(role_name="test-realm-role")["id"]
|
|
res = admin.get_realm_role_by_id(role_id)
|
|
assert res["name"] == "test-realm-role"
|
|
|
|
# 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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# 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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX)
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
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(USER_NOT_FOUND_REGEX), err
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
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 realm role group list
|
|
res = admin.get_realm_role_groups(role_name="test-realm-role-update")
|
|
assert len(res) == 1
|
|
assert res[0]["id"] == group_id
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_realm_role_groups(role_name="non-existent-role")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test with query params
|
|
res = admin.get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
|
|
assert len(res) == 1
|
|
|
|
# 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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
|
|
@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.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
:param client: Keycloak client
|
|
:type client: str
|
|
:param arg_brief_repr: Brief representation
|
|
:type arg_brief_repr: dict
|
|
:param includes_attributes: Indicator whether to include attributes
|
|
:type includes_attributes: bool
|
|
:param testcase: Test case
|
|
:type testcase: str
|
|
"""
|
|
# 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.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
|
|
"""Test client assignment of other client roles.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
:param client: Keycloak client
|
|
:type client: str
|
|
"""
|
|
admin.change_current_realm(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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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_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.change_current_realm(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) in [6, 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) in [6, 7], 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) in [5, 6], 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.change_current_realm(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.
|
|
|
|
: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
|
|
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"}, skip_exists=True
|
|
)
|
|
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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
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(COULD_NOT_FIND_ROLE_REGEX)
|
|
assert len(admin.get_client_roles(client_id=client)) == 1
|
|
|
|
# Test update client role
|
|
res = admin.update_client_role(
|
|
client_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_id=client,
|
|
role_name="client-role-test",
|
|
payload={"name": "client-role-test-update"},
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# 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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
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(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
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(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
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(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(UNKOWN_ERROR_REGEX), err
|
|
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(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test of roles by id - Get role
|
|
admin.create_client_role(
|
|
client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
|
|
)
|
|
role = admin.get_client_role(client_id=client, role_name="client-role-by-id-test")
|
|
res = admin.get_role_by_id(role_id=role["id"])
|
|
assert res["name"] == "client-role-by-id-test"
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_role_by_id(role_id="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
# Test of roles by id - Update role
|
|
res = admin.update_role_by_id(
|
|
role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
|
|
)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
res = admin.update_role_by_id(
|
|
role_id="bad", payload={"name": "client-role-by-id-test-update"}
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
# Test of roles by id - Delete role
|
|
res = admin.delete_role_by_id(role_id=role["id"])
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
admin.delete_role_by_id(role_id="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
|
|
def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
|
|
"""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.change_current_realm(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
|
|
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": 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(
|
|
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,
|
|
)
|
|
|
|
# Create permissions on the target client to reference this policy
|
|
admin.create_client_authz_scope_permission(
|
|
payload={
|
|
"id": "some-id",
|
|
"name": "test-permission",
|
|
"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,
|
|
)
|
|
permission_name = admin.get_client_authz_scope_permission(
|
|
client_id=realm_management_id, scope_id=token_exchange_permission_id
|
|
)["name"]
|
|
assert permission_name.startswith("token-exchange.permission.client.")
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
admin.create_client_authz_scope_permission(
|
|
payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
|
|
client_id="realm_management_id",
|
|
)
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
|
|
def test_email(admin: KeycloakAdmin, user: str):
|
|
"""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())
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
|
|
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 .*"}\'')
|
|
|
|
|
|
def test_get_sessions(admin: KeycloakAdmin):
|
|
"""Test get sessions.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.connection.username))
|
|
assert len(sessions) >= 1
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_sessions(user_id="bad")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
|
|
def test_get_client_installation_provider(admin: KeycloakAdmin, client: str):
|
|
"""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".*}\'')
|
|
|
|
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):
|
|
"""Test auth flows.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
res = admin.get_authentication_flows()
|
|
assert len(res) <= 8, res
|
|
default_flows = len(res)
|
|
assert {x["alias"] for x in res}.issubset(
|
|
{
|
|
"reset credentials",
|
|
"browser",
|
|
"registration",
|
|
"http challenge",
|
|
"docker auth",
|
|
"direct grant",
|
|
"first broker login",
|
|
"clients",
|
|
}
|
|
)
|
|
assert set(res[0].keys()) == {
|
|
"alias",
|
|
"authenticationExecutions",
|
|
"builtIn",
|
|
"description",
|
|
"id",
|
|
"providerId",
|
|
"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:
|
|
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 ('b\'{"error":"Flow not found"' in str(err)) or 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()) == (default_flows + 1)
|
|
|
|
# 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 ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
|
|
exec_id = res[0]["id"]
|
|
|
|
res = admin.get_authentication_flow_execution(execution_id=exec_id)
|
|
assert set(res.keys()).issubset(
|
|
{
|
|
"alternative",
|
|
"authenticator",
|
|
"authenticatorFlow",
|
|
"autheticatorFlow",
|
|
"conditional",
|
|
"disabled",
|
|
"enabled",
|
|
"id",
|
|
"parentFlow",
|
|
"priority",
|
|
"required",
|
|
"requirement",
|
|
}
|
|
), res.keys()
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
admin.get_authentication_flow_execution(execution_id="bad")
|
|
assert err.match(ILLEGAL_EXECUTION_REGEX)
|
|
|
|
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 or (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(ILLEGAL_EXECUTION_REGEX)
|
|
|
|
# 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 ('b\'{"error":"Could not find flow with id"' in str(err)) or (
|
|
'b\'{"error":"Flow not found"' in str(err)
|
|
)
|
|
|
|
|
|
def test_authentication_configs(admin: KeycloakAdmin, realm: str):
|
|
"""Test authentication configs.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
# Test list of auth providers
|
|
res = admin.get_authenticator_providers()
|
|
assert len(res) <= 38
|
|
|
|
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):
|
|
"""Test sync users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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".*}\'')
|
|
|
|
|
|
def test_client_scopes(admin: KeycloakAdmin, realm: str):
|
|
"""Test client scopes.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
# Test get client scopes
|
|
res = admin.get_client_scopes()
|
|
scope_names = {x["name"] for x in res}
|
|
assert len(res) in [10, 11]
|
|
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(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
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", "protocol": "openid-connect"}, skip_exists=True
|
|
)
|
|
assert res
|
|
res2 = admin.create_client_scope(
|
|
payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
|
|
)
|
|
assert res == res2
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
admin.create_client_scope(
|
|
payload={"name": "test-scope", "protocol": "openid-connect"}, 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(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
res_update = admin.update_client_scope(
|
|
client_scope_id=res, payload={"name": "test-scope-update"}
|
|
)
|
|
assert res_update == dict()
|
|
assert 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(NO_CLIENT_SCOPE_REGEX)
|
|
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) in [6, 7]
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
admin.add_default_default_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_add = admin.add_default_default_client_scope(scope_id=res)
|
|
assert res_add == dict()
|
|
assert len(admin.get_default_default_client_scopes()) in [7, 8]
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
admin.delete_default_default_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_del = admin.delete_default_default_client_scope(scope_id=res)
|
|
assert res_del == dict()
|
|
assert len(admin.get_default_default_client_scopes()) in [6, 7]
|
|
|
|
# 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(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
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(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
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(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
|
|
def test_components(admin: KeycloakAdmin, realm: str):
|
|
"""Test components.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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".*}\'')
|
|
|
|
|
|
def test_keys(admin: KeycloakAdmin, realm: str):
|
|
"""Test keys.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} or set(
|
|
admin.get_keys()["active"].keys()
|
|
) == {"RSA-OAEP", "RS256", "HS512", "AES"}
|
|
assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
|
|
"HS256",
|
|
"RSA-OAEP",
|
|
"AES",
|
|
"RS256",
|
|
} or {k["algorithm"] for k in admin.get_keys()["keys"]} == {
|
|
"HS512",
|
|
"RSA-OAEP",
|
|
"AES",
|
|
"RS256",
|
|
}
|
|
|
|
|
|
def test_admin_events(admin: KeycloakAdmin, realm: str):
|
|
"""Test events.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
admin.create_client(payload={"name": "test", "clientId": "test"})
|
|
|
|
events = admin.get_admin_events()
|
|
assert events == list()
|
|
|
|
|
|
def test_user_events(admin: KeycloakAdmin, realm: str):
|
|
"""Test events.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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()
|
|
|
|
|
|
@freezegun.freeze_time("2023-02-25 10:00:00")
|
|
def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
|
|
"""Test auto refresh token.
|
|
|
|
:param admin_frozen: Keycloak Admin client with time frozen in place
|
|
:type admin_frozen: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin = admin_frozen
|
|
admin.get_realm(realm_name=realm)
|
|
# Test get refresh
|
|
admin.connection.custom_headers = {
|
|
"Authorization": "Bearer bad",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
with pytest.raises(KeycloakAuthenticationError) as err:
|
|
admin.get_realm(realm_name=realm)
|
|
assert err.match('401: b\'{"error":"HTTP 401 Unauthorized".*}\'')
|
|
|
|
# Freeze time to simulate the access token expiring
|
|
with freezegun.freeze_time("2023-02-25 10:05:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
|
|
assert admin.get_realm(realm_name=realm)
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
|
|
|
|
# Test bad refresh token, but first make sure access token has expired again
|
|
with freezegun.freeze_time("2023-02-25 10:10:00"):
|
|
admin.connection.custom_headers = {"Content-Type": "application/json"}
|
|
admin.connection.token["refresh_token"] = "bad"
|
|
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"}\''
|
|
)
|
|
admin.connection.get_token()
|
|
|
|
# Test post refresh
|
|
with freezegun.freeze_time("2023-02-25 10:15:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
|
|
admin.connection.token = None
|
|
assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
|
|
|
|
# Test update refresh
|
|
with freezegun.freeze_time("2023-02-25 10:25:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
|
|
admin.connection.token = None
|
|
assert (
|
|
admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
|
|
== dict()
|
|
)
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
|
|
|
|
# Test delete refresh
|
|
with freezegun.freeze_time("2023-02-25 10:35:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
|
|
admin.connection.token = None
|
|
assert admin.delete_realm(realm_name="test-refresh") == dict()
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
|
|
|
|
|
|
def test_get_required_actions(admin: KeycloakAdmin, realm: str):
|
|
"""Test required actions.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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):
|
|
"""Test get required action by alias.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
ractions = admin.get_required_actions()
|
|
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):
|
|
"""Test update required action.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(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
|
|
):
|
|
"""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.change_current_realm(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
|
|
):
|
|
"""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.change_current_realm(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_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple):
|
|
"""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.change_current_realm(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])
|
|
|
|
|
|
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.change_current_realm(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.change_current_realm(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.change_current_realm(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
|
|
|
|
|
|
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.change_current_realm(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.change_current_realm(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.change_current_realm(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.change_current_realm("doesnotexist")
|
|
admin.get_realm_default_roles()
|
|
assert err.match('404: b\'{"error":"Realm not found.".*}\'')
|
|
admin.change_current_realm(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".*}\'')
|
|
|
|
|
|
def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the keys cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
res = admin.clear_keys_cache()
|
|
assert res == {}
|
|
|
|
|
|
def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the realm cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
res = admin.clear_realm_cache()
|
|
assert res == {}
|
|
|
|
|
|
def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the user cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
res = admin.clear_user_cache()
|
|
assert res == {}
|
|
|
|
|
|
def test_initial_access_token(
|
|
admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
|
|
) -> None:
|
|
"""Test initial access token and client creation.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
|
|
:type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
|
|
"""
|
|
res = admin.create_initial_access_token(2, 3)
|
|
assert "token" in res
|
|
assert res["count"] == 2
|
|
assert res["expiration"] == 3
|
|
|
|
oid, username, password = oid_with_credentials
|
|
|
|
client = str(uuid.uuid4())
|
|
secret = str(uuid.uuid4())
|
|
|
|
res = oid.register_client(
|
|
token=res["token"],
|
|
payload={
|
|
"name": "DynamicRegisteredClient",
|
|
"clientId": client,
|
|
"enabled": True,
|
|
"publicClient": False,
|
|
"protocol": "openid-connect",
|
|
"secret": secret,
|
|
"clientAuthenticatorType": "client-secret",
|
|
},
|
|
)
|
|
assert res["clientId"] == client
|
|
|
|
new_secret = str(uuid.uuid4())
|
|
res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
|
|
assert res["secret"] == new_secret
|
|
|
|
|
|
def test_refresh_token(admin: KeycloakAdmin):
|
|
"""Test refresh token on connection even if it is expired.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
admin.get_realms()
|
|
assert admin.connection.token is not None
|
|
admin.user_logout(admin.get_user_id(admin.connection.username))
|
|
admin.connection.refresh_token()
|
|
|
|
|
|
# async function start
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_realms(admin: KeycloakAdmin):
|
|
"""Test realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
# Get realms
|
|
realms = await admin.a_get_realms()
|
|
assert len(realms) == 1, realms
|
|
assert "master" == realms[0]["realm"]
|
|
|
|
# Create a test realm
|
|
res = await admin.a_create_realm(payload={"realm": "test"})
|
|
assert res == b"", res
|
|
|
|
# Create the same realm, should fail
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
res = await admin.a_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 = await admin.a_create_realm(payload={"realm": "test"}, skip_exists=True)
|
|
assert res == {"msg": "Already exists"}, res
|
|
|
|
# Get a single realm
|
|
res = await admin.a_get_realm(realm_name="test")
|
|
assert res["realm"] == "test"
|
|
|
|
# Get non-existing realm
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_realm(realm_name="non-existent")
|
|
assert err.match('404: b\'{"error":"Realm not found.".*\'')
|
|
|
|
# Update realm
|
|
res = await admin.a_update_realm(realm_name="test", payload={"accountTheme": "test"})
|
|
assert res == dict(), res
|
|
|
|
# Check that the update worked
|
|
res = await admin.a_get_realm(realm_name="test")
|
|
assert res["realm"] == "test"
|
|
assert res["accountTheme"] == "test"
|
|
|
|
# Update wrong payload
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_realm(realm_name="test", payload={"wrong": "payload"})
|
|
assert err.match('400: b\'{"error":"Unrecognized field')
|
|
|
|
# Check that get realms returns both realms
|
|
realms = await admin.a_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 = await admin.a_delete_realm(realm_name="test")
|
|
assert res == dict(), res
|
|
|
|
# Check that the realm does not exist anymore
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_realm(realm_name="test")
|
|
assert err.match('404: b\'{"error":"Realm not found.".*}\'')
|
|
|
|
# Delete non-existing realm
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_realm(realm_name="non-existent")
|
|
assert err.match('404: b\'{"error":"Realm not found.".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_changing_of_realms(admin: KeycloakAdmin, realm: str):
|
|
"""Test changing of realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
assert await admin.a_get_current_realm() == "master"
|
|
await admin.a_change_current_realm(realm)
|
|
assert await admin.a_get_current_realm() == realm
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_import_export_realms(admin: KeycloakAdmin, realm: str):
|
|
"""Test import and export of realms.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=True)
|
|
assert realm_export != dict(), realm_export
|
|
|
|
await admin.a_delete_realm(realm_name=realm)
|
|
admin.realm_name = "master"
|
|
res = await admin.a_import_realm(payload=realm_export)
|
|
assert res == b"", res
|
|
|
|
# Test bad import
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_import_realm(payload=dict())
|
|
assert err.match(
|
|
'500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'' # noqa: E501
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_partial_import_realm(admin: KeycloakAdmin, realm: str):
|
|
"""Test partial import of realm configuration.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
test_realm_role = str(uuid.uuid4())
|
|
test_user = str(uuid.uuid4())
|
|
test_client = str(uuid.uuid4())
|
|
|
|
await admin.a_change_current_realm(realm)
|
|
client_id = await admin.a_create_client(payload={"name": test_client, "clientId": test_client})
|
|
|
|
realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=False)
|
|
|
|
client_config = [
|
|
client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
|
|
][0]
|
|
|
|
# delete before partial import
|
|
await admin.a_delete_client(client_id)
|
|
|
|
payload = {
|
|
"ifResourceExists": "SKIP",
|
|
"id": realm_export["id"],
|
|
"realm": realm,
|
|
"clients": [client_config],
|
|
"roles": {"realm": [{"name": test_realm_role}]},
|
|
"users": [{"username": test_user, "email": f"{test_user}@test.test"}],
|
|
}
|
|
|
|
# check add
|
|
res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["added"] == 3
|
|
|
|
# check skip
|
|
res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["skipped"] == 3
|
|
|
|
# check overwrite
|
|
payload["ifResourceExists"] = "OVERWRITE"
|
|
res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
|
|
assert res["overwritten"] == 3
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_users(admin: KeycloakAdmin, realm: str):
|
|
"""Test users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Check no users present
|
|
users = await admin.a_get_users()
|
|
assert users == list(), users
|
|
|
|
# Test create user
|
|
user_id = await admin.a_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:
|
|
await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
|
|
assert err.match(".*User exists with same.*")
|
|
|
|
# Test create the same user, exists_ok true
|
|
user_id_2 = await admin.a_create_user(
|
|
payload={"username": "test", "email": "test@test.test"}, exist_ok=True
|
|
)
|
|
assert user_id == user_id_2
|
|
|
|
# Test get user
|
|
user = await admin.a_get_user(user_id=user_id)
|
|
assert user["username"] == "test", user["username"]
|
|
assert user["email"] == "test@test.test", user["email"]
|
|
|
|
# Test update user
|
|
res = await admin.a_update_user(user_id=user_id, payload={"firstName": "Test"})
|
|
assert res == dict(), res
|
|
user = await admin.a_get_user(user_id=user_id)
|
|
assert user["firstName"] == "Test"
|
|
|
|
# Test update user fail
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_user(user_id=user_id, payload={"wrong": "payload"})
|
|
assert err.match('400: b\'{"error":"Unrecognized field')
|
|
|
|
# Test disable user
|
|
res = await admin.a_disable_user(user_id=user_id)
|
|
assert res == {}, res
|
|
assert not (await admin.a_get_user(user_id=user_id))["enabled"]
|
|
|
|
# Test enable user
|
|
res = await admin.a_enable_user(user_id=user_id)
|
|
assert res == {}, res
|
|
assert (await admin.a_get_user(user_id=user_id))["enabled"]
|
|
|
|
# Test get users again
|
|
users = await admin.a_get_users()
|
|
usernames = [x["username"] for x in users]
|
|
assert "test" in usernames
|
|
|
|
# Test users counts
|
|
count = await admin.a_users_count()
|
|
assert count == 1, count
|
|
|
|
# Test users count with query
|
|
count = await admin.a_users_count(query={"username": "notpresent"})
|
|
assert count == 0
|
|
|
|
# Test user groups
|
|
groups = await admin.a_get_user_groups(user_id=user["id"])
|
|
assert len(groups) == 0
|
|
|
|
# Test user groups bad id
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_user_groups(user_id="does-not-exist")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
# Test logout
|
|
res = await admin.a_user_logout(user_id=user["id"])
|
|
assert res == dict(), res
|
|
|
|
# Test logout fail
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_user_logout(user_id="non-existent-id")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
# Test consents
|
|
res = await admin.a_user_consents(user_id=user["id"])
|
|
assert len(res) == 0, res
|
|
|
|
# Test consents fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_user_consents(user_id="non-existent-id")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
# Test delete user
|
|
res = await admin.a_delete_user(user_id=user_id)
|
|
assert res == dict(), res
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_user(user_id=user_id)
|
|
err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
# Test delete fail
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_user(user_id="non-existent-id")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_enable_disable_all_users(admin: KeycloakAdmin, realm: str):
|
|
"""Test enable and disable all users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
user_id_1 = await admin.a_create_user(
|
|
payload={"username": "test", "email": "test@test.test", "enabled": True}
|
|
)
|
|
user_id_2 = await admin.a_create_user(
|
|
payload={"username": "test2", "email": "test2@test.test", "enabled": True}
|
|
)
|
|
user_id_3 = await admin.a_create_user(
|
|
payload={"username": "test3", "email": "test3@test.test", "enabled": True}
|
|
)
|
|
|
|
assert (await admin.a_get_user(user_id_1))["enabled"]
|
|
assert (await admin.a_get_user(user_id_2))["enabled"]
|
|
assert (await admin.a_get_user(user_id_3))["enabled"]
|
|
|
|
await admin.a_disable_all_users()
|
|
|
|
assert not (await admin.a_get_user(user_id_1))["enabled"]
|
|
assert not (await admin.a_get_user(user_id_2))["enabled"]
|
|
assert not (await admin.a_get_user(user_id_3))["enabled"]
|
|
|
|
await admin.a_enable_all_users()
|
|
|
|
assert (await admin.a_get_user(user_id_1))["enabled"]
|
|
assert (await admin.a_get_user(user_id_2))["enabled"]
|
|
assert (await admin.a_get_user(user_id_3))["enabled"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_users_roles(admin: KeycloakAdmin, realm: str):
|
|
"""Test users roles.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
|
|
|
|
# Test all level user roles
|
|
client_id = await admin.a_create_client(
|
|
payload={"name": "test-client", "clientId": "test-client"}
|
|
)
|
|
await admin.a_create_client_role(client_role_id=client_id, payload={"name": "test-role"})
|
|
await admin.a_assign_client_role(
|
|
client_id=client_id,
|
|
user_id=user_id,
|
|
roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
|
|
)
|
|
all_roles = await admin.a_get_all_roles_of_user(user_id=user_id)
|
|
realm_roles = all_roles["realmMappings"]
|
|
assert len(realm_roles) == 1, realm_roles
|
|
client_roles = all_roles["clientMappings"]
|
|
assert len(client_roles) == 1, client_roles
|
|
|
|
# Test all level user roles fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_all_roles_of_user(user_id="non-existent-id")
|
|
err.match('404: b\'{"error":"User not found"')
|
|
|
|
await admin.a_delete_user(user_id)
|
|
await admin.a_delete_client(client_id)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_users_pagination(admin: KeycloakAdmin, realm: str):
|
|
"""Test user pagination.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(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 = await admin.a_get_users()
|
|
assert len(users) == admin.PAGE_SIZE + 50, len(users)
|
|
|
|
users = await admin.a_get_users(query={"first": 100})
|
|
assert len(users) == 50, len(users)
|
|
|
|
users = await admin.a_get_users(query={"max": 20})
|
|
assert len(users) == 20, len(users)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_user_groups_pagination(admin: KeycloakAdmin, realm: str):
|
|
"""Test user groups pagination.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
user_id = await admin.a_create_user(
|
|
payload={"username": "username_1", "email": "username_1@test.test"}
|
|
)
|
|
|
|
for ind in range(admin.PAGE_SIZE + 50):
|
|
group_name = f"group_{ind}"
|
|
group_id = await admin.a_create_group(payload={"name": group_name})
|
|
await admin.a_group_user_add(user_id=user_id, group_id=group_id)
|
|
|
|
groups = await admin.a_get_user_groups(user_id=user_id)
|
|
assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
|
|
|
|
groups = await admin.a_get_user_groups(
|
|
user_id=user_id, query={"first": 100, "max": -1, "search": ""}
|
|
)
|
|
assert len(groups) == 50, len(groups)
|
|
|
|
groups = await admin.a_get_user_groups(
|
|
user_id=user_id, query={"max": 20, "first": -1, "search": ""}
|
|
)
|
|
assert len(groups) == 20, len(groups)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_idps(admin: KeycloakAdmin, realm: str):
|
|
"""Test IDPs.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Create IDP
|
|
res = await admin.a_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:
|
|
await admin.a_create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
|
|
assert err.match("Invalid identity provider id"), err
|
|
|
|
# Test listing
|
|
idps = await admin.a_get_idps()
|
|
assert len(idps) == 1
|
|
assert "github" == idps[0]["alias"]
|
|
|
|
# Test get idp
|
|
idp = await admin.a_get_idp("github")
|
|
assert "github" == idp["alias"]
|
|
assert idp.get("config")
|
|
assert "test" == idp["config"]["clientId"]
|
|
assert "**********" == idp["config"]["clientSecret"]
|
|
|
|
# Test get idp fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_idp("does-not-exist")
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
# Test IdP update
|
|
res = await admin.a_update_idp(idp_alias="github", payload=idps[0])
|
|
|
|
assert res == {}, res
|
|
|
|
# Test adding a mapper
|
|
res = await admin.a_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:
|
|
await admin.a_add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
# Test IdP mappers listing
|
|
idp_mappers = await admin.a_get_idp_mappers(idp_alias="github")
|
|
assert len(idp_mappers) == 1
|
|
|
|
# Test IdP mapper update
|
|
res = await admin.a_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 res == dict(), res
|
|
|
|
# Test delete
|
|
res = await admin.a_delete_idp(idp_alias="github")
|
|
assert res == dict(), res
|
|
|
|
# Test delete fail
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_idp(idp_alias="does-not-exist")
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_user_credentials(admin: KeycloakAdmin, user: str):
|
|
"""Test user credentials.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param user: Keycloak user
|
|
:type user: str
|
|
"""
|
|
res = await admin.a_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:
|
|
await admin.a_set_user_password(user_id="does-not-exist", password="")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
credentials = await admin.a_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:
|
|
await admin.a_get_credentials(user_id="does-not-exist")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
res = await admin.a_delete_credential(user_id=user, credential_id=credentials[0]["id"])
|
|
assert res == dict(), res
|
|
|
|
# Test delete fail
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_credential(user_id=user, credential_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Credential not found".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_social_logins(admin: KeycloakAdmin, user: str):
|
|
"""Test social logins.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param user: Keycloak user
|
|
:type user: str
|
|
"""
|
|
res = await admin.a_add_user_social_login(
|
|
user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
|
|
)
|
|
assert res == dict(), res
|
|
await admin.a_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:
|
|
await admin.a_add_user_social_login(
|
|
user_id="does-not-exist",
|
|
provider_id="does-not-exist",
|
|
provider_userid="test",
|
|
provider_username="test",
|
|
)
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
res = await admin.a_get_user_social_logins(user_id=user)
|
|
assert res == list(), res
|
|
|
|
# Test get social logins fail
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_user_social_logins(user_id="does-not-exist")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
res = await admin.a_delete_user_social_login(user_id=user, provider_id="gitlab")
|
|
assert res == {}, res
|
|
|
|
res = await admin.a_delete_user_social_login(user_id=user, provider_id="github")
|
|
assert res == {}, res
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_user_social_login(user_id=user, provider_id="instagram")
|
|
assert err.match('404: b\'{"error":"Link not found".*}\''), err
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_server_info(admin: KeycloakAdmin):
|
|
"""Test server info.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
info = await admin.a_get_server_info()
|
|
assert set(info.keys()).issubset(
|
|
{
|
|
"systemInfo",
|
|
"memoryInfo",
|
|
"profileInfo",
|
|
"features",
|
|
"themes",
|
|
"socialProviders",
|
|
"identityProviders",
|
|
"providers",
|
|
"protocolMapperTypes",
|
|
"builtinProtocolMappers",
|
|
"clientInstallations",
|
|
"componentTypes",
|
|
"passwordPolicies",
|
|
"enums",
|
|
"cryptoInfo",
|
|
"features",
|
|
}
|
|
), info.keys()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_groups(admin: KeycloakAdmin, user: str):
|
|
"""Test groups.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param user: Keycloak user
|
|
:type user: str
|
|
"""
|
|
# Test get groups
|
|
groups = await admin.a_get_groups()
|
|
assert len(groups) == 0
|
|
|
|
# Test create group
|
|
group_id = await admin.a_create_group(payload={"name": "main-group"})
|
|
assert group_id is not None, group_id
|
|
|
|
# Test group count
|
|
count = await admin.a_groups_count()
|
|
assert count.get("count") == 1, count
|
|
|
|
# Test group count with query
|
|
count = await admin.a_groups_count(query={"search": "notpresent"})
|
|
assert count.get("count") == 0
|
|
|
|
# Test create subgroups
|
|
subgroup_id_1 = await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
|
|
subgroup_id_2 = await admin.a_create_group(payload={"name": "subgroup-2"}, parent=group_id)
|
|
|
|
# Test create group fail
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
|
|
assert err.match("409"), err
|
|
|
|
# Test skip exists OK
|
|
subgroup_id_1_eq = await admin.a_create_group(
|
|
payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
|
|
)
|
|
assert subgroup_id_1_eq is None
|
|
|
|
# Test get groups again
|
|
groups = await admin.a_get_groups()
|
|
assert len(groups) == 1, groups
|
|
assert len(groups[0]["subGroups"]) == 2, groups[0]["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 = await admin.a_get_groups(query={"max": 10})
|
|
assert len(groups) == 1, groups
|
|
assert len(groups[0]["subGroups"]) == 2, groups[0]["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 = await admin.a_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:
|
|
await admin.a_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 = await admin.a_create_group(
|
|
payload={"name": "subsubgroup-1"}, parent=subgroup_id_2
|
|
)
|
|
main_group = await admin.a_get_group(group_id=group_id)
|
|
|
|
# Test nested searches
|
|
subgroup_2 = await admin.a_get_group(group_id=subgroup_id_2)
|
|
res = await admin.a_get_subgroups(
|
|
group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1"
|
|
)
|
|
assert res is not None, res
|
|
assert res["id"] == subsubgroup_id_1
|
|
|
|
# Test nested search from main group
|
|
res = await admin.a_get_subgroups(
|
|
group=await admin.a_get_group(group_id=group_id, full_hierarchy=True),
|
|
path="/main-group/subgroup-2/subsubgroup-1",
|
|
)
|
|
assert res["id"] == subsubgroup_id_1
|
|
|
|
# Test nested search from all groups
|
|
res = await admin.a_get_groups(full_hierarchy=True)
|
|
assert len(res) == 1
|
|
assert len(res[0]["subGroups"]) == 2
|
|
assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1][0]["subGroups"]) == 0
|
|
assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2][0]["subGroups"]) == 1
|
|
|
|
# Test that query params are not allowed for full hierarchy
|
|
with pytest.raises(ValueError) as err:
|
|
await admin.a_get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
|
|
|
|
# Test that query params are passed
|
|
if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
|
|
os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"]
|
|
) >= Version("23"):
|
|
res = await admin.a_get_group_children(group_id=group_id, query={"max": 1})
|
|
assert len(res) == 1
|
|
|
|
assert err.match("Cannot use both query and full_hierarchy parameters")
|
|
|
|
main_group_id_2 = await admin.a_create_group(payload={"name": "main-group-2"})
|
|
assert len(await admin.a_get_groups(full_hierarchy=True)) == 2
|
|
|
|
# Test empty search
|
|
res = await admin.a_get_subgroups(group=main_group, path="/none")
|
|
assert res is None, res
|
|
|
|
# Test get group by path
|
|
res = await admin.a_get_group_by_path(path="/main-group/subgroup-1")
|
|
assert res is not None, res
|
|
assert res["id"] == subgroup_id_1, res
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
|
|
assert err.match('404: b\'{"error":"Group path does not exist".*}\'')
|
|
|
|
res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
|
|
assert res is not None, res
|
|
assert res["id"] == subsubgroup_id_1
|
|
|
|
res = await admin.a_get_group_by_path(path="/main-group")
|
|
assert res is not None, res
|
|
assert res["id"] == group_id, res
|
|
|
|
# Test group members
|
|
res = await admin.a_get_group_members(group_id=subgroup_id_2)
|
|
assert len(res) == 0, res
|
|
|
|
# Test fail group members
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_group_members(group_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
|
|
|
|
res = await admin.a_group_user_add(user_id=user, group_id=subgroup_id_2)
|
|
assert res == dict(), res
|
|
|
|
res = await admin.a_get_group_members(group_id=subgroup_id_2)
|
|
assert len(res) == 1, res
|
|
assert res[0]["id"] == user
|
|
|
|
# Test get group members query
|
|
res = await admin.a_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:
|
|
await admin.a_group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
|
|
assert err.match(USER_NOT_FOUND_REGEX), err
|
|
|
|
res = await admin.a_group_user_remove(user_id=user, group_id=subgroup_id_2)
|
|
assert res == dict(), res
|
|
|
|
# Test set permissions
|
|
res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=True)
|
|
assert res["enabled"], res
|
|
res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=False)
|
|
assert not res["enabled"], res
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled="blah")
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
|
|
# Test update group
|
|
res = await admin.a_update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
|
|
assert res == dict(), res
|
|
assert (await admin.a_get_group(group_id=subgroup_id_2))["name"] == "new-subgroup-2"
|
|
|
|
# test update fail
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_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 = await admin.a_delete_group(group_id=group_id)
|
|
assert res == dict(), res
|
|
res = await admin.a_delete_group(group_id=main_group_id_2)
|
|
assert res == dict(), res
|
|
assert len(await admin.a_get_groups()) == 0
|
|
|
|
# Test delete fail
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_group(group_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_clients(admin: KeycloakAdmin, realm: str):
|
|
"""Test clients.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test get clients
|
|
clients = await admin.a_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 = await admin.a_create_client(
|
|
payload={"name": "test-client", "clientId": "test-client"}
|
|
)
|
|
assert client_id, client_id
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_client(payload={"name": "test-client", "clientId": "test-client"})
|
|
assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
|
|
|
|
client_id_2 = await admin.a_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 = await admin.a_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:
|
|
await admin.a_get_client(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
assert len(await admin.a_get_clients()) == 7
|
|
|
|
# Test get client id
|
|
assert await admin.a_get_client_id(client_id="test-client") == client_id
|
|
assert await admin.a_get_client_id(client_id="does-not-exist") is None
|
|
|
|
# Test update client
|
|
res = await admin.a_update_client(client_id=client_id, payload={"name": "test-client-change"})
|
|
assert res == dict(), res
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_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 = await admin.a_get_mappers_from_client(client_id=client_id)
|
|
assert len(res) == 0
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_add_mapper_to_client(client_id="does-not-exist", payload=dict())
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
res = await admin.a_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(await admin.a_get_mappers_from_client(client_id=client_id)) == 1
|
|
|
|
mapper = (await admin.a_get_mappers_from_client(client_id=client_id))[0]
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_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 = await admin.a_update_client_mapper(
|
|
client_id=client_id, mapper_id=mapper["id"], payload=mapper
|
|
)
|
|
assert res == dict()
|
|
|
|
res = await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_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:
|
|
await admin.a_get_client_all_sessions(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
assert await admin.a_get_client_all_sessions(client_id=client_id) == list()
|
|
assert await admin.a_get_client_sessions_stats() == list()
|
|
|
|
# Test authz
|
|
auth_client_id = await admin.a_create_client(
|
|
payload={
|
|
"name": "authz-client",
|
|
"clientId": "authz-client",
|
|
"authorizationServicesEnabled": True,
|
|
"serviceAccountsEnabled": True,
|
|
}
|
|
)
|
|
res = await admin.a_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:
|
|
await admin.a_get_client_authz_settings(client_id=client_id)
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
# Authz resources
|
|
res = await admin.a_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:
|
|
await admin.a_get_client_authz_resources(client_id=client_id)
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
res = await admin.a_create_client_authz_resource(
|
|
client_id=auth_client_id, payload={"name": "test-resource"}
|
|
)
|
|
assert res["name"] == "test-resource", res
|
|
test_resource_id = res["_id"]
|
|
|
|
res = await admin.a_get_client_authz_resource(
|
|
client_id=auth_client_id, resource_id=test_resource_id
|
|
)
|
|
assert res["_id"] == test_resource_id, res
|
|
assert res["name"] == "test-resource", res
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_client_authz_resource(
|
|
client_id=auth_client_id, payload={"name": "test-resource"}
|
|
)
|
|
assert err.match('409: b\'{"error":"invalid_request"')
|
|
assert await admin.a_create_client_authz_resource(
|
|
client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
|
|
) == {"msg": "Already exists"}
|
|
|
|
res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
|
|
assert len(res) == 2
|
|
assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
|
|
|
|
res = await admin.a_create_client_authz_resource(
|
|
client_id=auth_client_id, payload={"name": "temp-resource"}
|
|
)
|
|
assert res["name"] == "temp-resource", res
|
|
temp_resource_id: str = res["_id"]
|
|
# Test update authz resources
|
|
await admin.a_update_client_authz_resource(
|
|
client_id=auth_client_id,
|
|
resource_id=temp_resource_id,
|
|
payload={"name": "temp-updated-resource"},
|
|
)
|
|
res = await admin.a_get_client_authz_resource(
|
|
client_id=auth_client_id, resource_id=temp_resource_id
|
|
)
|
|
assert res["name"] == "temp-updated-resource", res
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_client_authz_resource(
|
|
client_id=auth_client_id,
|
|
resource_id="invalid_resource_id",
|
|
payload={"name": "temp-updated-resource"},
|
|
)
|
|
assert err.match("404: b''"), err
|
|
await admin.a_delete_client_authz_resource(
|
|
client_id=auth_client_id, resource_id=temp_resource_id
|
|
)
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_authz_resource(
|
|
client_id=auth_client_id, resource_id=temp_resource_id
|
|
)
|
|
assert err.match("404: b''")
|
|
|
|
# Authz policies
|
|
res = await admin.a_get_client_authz_policies(client_id=auth_client_id)
|
|
assert len(res) == 1, res
|
|
assert res[0]["name"] == "Default Policy"
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_authz_policies(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
role_id = (await admin.a_get_realm_role(role_name="offline_access"))["id"]
|
|
res = await admin.a_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:
|
|
await admin.a_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 await admin.a_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(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 2
|
|
role_based_policy_id = res["id"]
|
|
role_based_policy_name = res["name"]
|
|
|
|
res = await admin.a_create_client_authz_role_based_policy(
|
|
client_id=auth_client_id,
|
|
payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
|
|
)
|
|
res2 = await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
assert res["id"] == res2["id"]
|
|
await admin.a_delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
|
|
assert err.match("404: b''")
|
|
|
|
res = await admin.a_create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
)
|
|
assert res["name"] == "test-authz-policy", res
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
)
|
|
assert err.match('409: b\'{"error":"Policy with name')
|
|
assert await admin.a_create_client_authz_policy(
|
|
client_id=auth_client_id,
|
|
payload={
|
|
"name": "test-authz-policy",
|
|
"type": "time",
|
|
"config": {"hourEnd": "18", "hour": "9"},
|
|
},
|
|
skip_exists=True,
|
|
) == {"msg": "Already exists"}
|
|
assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 3
|
|
|
|
# Test authz permissions
|
|
res = await admin.a_get_client_authz_permissions(client_id=auth_client_id)
|
|
assert len(res) == 1, res
|
|
assert res[0]["name"] == "Default Permission"
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_authz_permissions(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
res = await admin.a_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]
|
|
resource_based_permission_id = res["id"]
|
|
resource_based_permission_name = res["name"]
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_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 await admin.a_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(await admin.a_get_client_authz_permissions(client_id=auth_client_id)) == 2
|
|
|
|
# Test associating client policy with resource based permission
|
|
res = await admin.a_update_client_authz_resource_permission(
|
|
client_id=auth_client_id,
|
|
resource_id=resource_based_permission_id,
|
|
payload={
|
|
"id": resource_based_permission_id,
|
|
"name": resource_based_permission_name,
|
|
"type": "resource",
|
|
"logic": "POSITIVE",
|
|
"decisionStrategy": "UNANIMOUS",
|
|
"resources": [test_resource_id],
|
|
"scopes": [],
|
|
"policies": [role_based_policy_id],
|
|
},
|
|
)
|
|
|
|
# Test getting associated policies for a permission
|
|
associated_policies = await admin.a_get_client_authz_permission_associated_policies(
|
|
client_id=auth_client_id, policy_id=resource_based_permission_id
|
|
)
|
|
assert len(associated_policies) == 1
|
|
assert associated_policies[0]["name"].startswith(role_based_policy_name)
|
|
|
|
# Test authz scopes
|
|
res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
|
|
assert len(res) == 0, res
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_authz_scopes(client_id=client_id)
|
|
assert err.match(HTTP_404_REGEX)
|
|
|
|
res = await admin.a_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:
|
|
await admin.a_create_client_authz_scopes(
|
|
client_id="invalid_client_id", payload={"name": "test-authz-scope"}
|
|
)
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
assert await admin.a_create_client_authz_scopes(
|
|
client_id=auth_client_id, payload={"name": "test-authz-scope"}
|
|
)
|
|
|
|
res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
|
|
assert len(res) == 1
|
|
assert {x["name"] for x in res} == {"test-authz-scope"}
|
|
|
|
# Test service account user
|
|
res = await admin.a_get_client_service_account_user(client_id=auth_client_id)
|
|
assert res["username"] == "service-account-authz-client", res
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_service_account_user(client_id=client_id)
|
|
assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
|
|
UNKOWN_ERROR_REGEX
|
|
)
|
|
|
|
# Test delete client
|
|
res = await admin.a_delete_client(client_id=auth_client_id)
|
|
assert res == dict(), res
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_client(client_id=auth_client_id)
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
# Test client credentials
|
|
await admin.a_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:
|
|
await admin.a_get_client_secrets(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
secrets = await admin.a_get_client_secrets(
|
|
client_id=await admin.a_get_client_id(client_id="test-confidential")
|
|
)
|
|
assert secrets == {"type": "secret", "value": "test-secret"}
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_generate_client_secrets(client_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
res = await admin.a_generate_client_secrets(
|
|
client_id=await admin.a_get_client_id(client_id="test-confidential")
|
|
)
|
|
assert res
|
|
assert (
|
|
await admin.a_get_client_secrets(
|
|
client_id=await admin.a_get_client_id(client_id="test-confidential")
|
|
)
|
|
== res
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_realm_roles(admin: KeycloakAdmin, realm: str):
|
|
"""Test realm roles.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test get realm roles
|
|
roles = await admin.a_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 get realm roles with search text
|
|
searched_roles = await admin.a_get_realm_roles(search_text="uma_a")
|
|
searched_role_names = [x["name"] for x in searched_roles]
|
|
assert "uma_authorization" in searched_role_names, searched_role_names
|
|
assert "offline_access" not in searched_role_names, searched_role_names
|
|
|
|
# Test empty members
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_realm_role_members(role_name="does-not-exist")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
members = await admin.a_get_realm_role_members(role_name="offline_access")
|
|
assert members == list(), members
|
|
|
|
# Test create realm role
|
|
role_id = await admin.a_create_realm_role(
|
|
payload={"name": "test-realm-role"}, skip_exists=True
|
|
)
|
|
assert role_id, role_id
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_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 = await admin.a_create_realm_role(
|
|
payload={"name": "test-realm-role"}, skip_exists=True
|
|
)
|
|
assert role_id == role_id_2
|
|
|
|
# Test get realm role by its id
|
|
role_id = (await admin.a_get_realm_role(role_name="test-realm-role"))["id"]
|
|
res = await admin.a_get_realm_role_by_id(role_id)
|
|
assert res["name"] == "test-realm-role"
|
|
|
|
# Test update realm role
|
|
res = await admin.a_update_realm_role(
|
|
role_name="test-realm-role", payload={"name": "test-realm-role-update"}
|
|
)
|
|
assert res == dict(), res
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_realm_role(
|
|
role_name="test-realm-role", payload={"name": "test-realm-role-update"}
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test realm role user assignment
|
|
user_id = await admin.a_create_user(
|
|
payload={"username": "role-testing", "email": "test@test.test"}
|
|
)
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_assign_realm_roles(user_id=user_id, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_realm_roles(
|
|
user_id=user_id,
|
|
roles=[
|
|
await admin.a_get_realm_role(role_name="offline_access"),
|
|
await admin.a_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 await admin.a_get_realm_role_members(role_name="offline_access")
|
|
]
|
|
assert admin.get_user(user_id=user_id)["username"] in [
|
|
x["username"]
|
|
for x in await admin.a_get_realm_role_members(role_name="test-realm-role-update")
|
|
]
|
|
|
|
roles = await admin.a_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(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_delete_realm_roles_of_user(
|
|
user_id=user_id, roles=[await admin.a_get_realm_role(role_name="offline_access")]
|
|
)
|
|
assert res == dict(), res
|
|
assert await admin.a_get_realm_role_members(role_name="offline_access") == list()
|
|
roles = await admin.a_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 = await admin.a_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 = await admin.a_create_group(payload={"name": "test-group"})
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_assign_group_realm_roles(group_id=group_id, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_group_realm_roles(
|
|
group_id=group_id,
|
|
roles=[
|
|
await admin.a_get_realm_role(role_name="offline_access"),
|
|
await admin.a_get_realm_role(role_name="test-realm-role-update"),
|
|
],
|
|
)
|
|
assert res == dict(), res
|
|
|
|
roles = await admin.a_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:
|
|
await admin.a_delete_group_realm_roles(group_id=group_id, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX)
|
|
res = await admin.a_delete_group_realm_roles(
|
|
group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
|
|
)
|
|
assert res == dict(), res
|
|
roles = await admin.a_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 = await admin.a_create_realm_role(payload={"name": "test-composite-role"})
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_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 = await admin.a_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:
|
|
await admin.a_get_composite_realm_roles_of_role(role_name="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
res = await admin.a_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:
|
|
await admin.a_get_composite_realm_roles_of_user(user_id="bad")
|
|
assert err.match(USER_NOT_FOUND_REGEX), err
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_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 = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
|
|
assert len(res) == 0
|
|
|
|
# Test realm role group list
|
|
res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update")
|
|
assert len(res) == 1
|
|
assert res[0]["id"] == group_id
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_realm_role_groups(role_name="non-existent-role")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test with query params
|
|
res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
|
|
assert len(res) == 1
|
|
|
|
# Test delete realm role
|
|
res = await admin.a_delete_realm_role(role_name=composite_role)
|
|
assert res == dict(), res
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_realm_role(role_name=composite_role)
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize(
|
|
"testcase, arg_brief_repr, includes_attributes",
|
|
[
|
|
("brief True", {"brief_representation": True}, False),
|
|
("brief False", {"brief_representation": False}, True),
|
|
("default", {}, False),
|
|
],
|
|
)
|
|
async def test_a_role_attributes(
|
|
admin: KeycloakAdmin,
|
|
realm: str,
|
|
client: str,
|
|
arg_brief_repr: dict,
|
|
includes_attributes: bool,
|
|
testcase: str,
|
|
):
|
|
"""Test getting role attributes for bulk calls.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
:param client: Keycloak client
|
|
:type client: str
|
|
:param arg_brief_repr: Brief representation
|
|
:type arg_brief_repr: dict
|
|
:param includes_attributes: Indicator whether to include attributes
|
|
:type includes_attributes: bool
|
|
:param testcase: Test case
|
|
:type testcase: str
|
|
"""
|
|
# setup
|
|
attribute_role = "test-realm-role-w-attr"
|
|
test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
|
|
role_id = await admin.a_create_realm_role(
|
|
payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
|
|
)
|
|
assert role_id, role_id
|
|
|
|
cli_role_id = await admin.a_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 = await admin.a_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 = await admin.a_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 = await admin.a_delete_realm_role(role_name=attribute_role)
|
|
assert res == dict(), res
|
|
|
|
res = await admin.a_delete_client_role(client, role_name=attribute_role)
|
|
assert res == dict(), res
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
|
|
"""Test client realm roles.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test get realm roles
|
|
roles = await admin.a_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 = await admin.a_create_realm_role(
|
|
payload={"name": "test-realm-role"}, skip_exists=True
|
|
)
|
|
assert role_id, role_id
|
|
|
|
# Test realm role client assignment
|
|
client_id = await admin.a_create_client(
|
|
payload={"name": "role-testing-client", "clientId": "role-testing-client"}
|
|
)
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_realm_roles_to_client_scope(
|
|
client_id=client_id,
|
|
roles=[
|
|
await admin.a_get_realm_role(role_name="offline_access"),
|
|
await admin.a_get_realm_role(role_name="test-realm-role"),
|
|
],
|
|
)
|
|
assert res == dict(), res
|
|
|
|
roles = await admin.a_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:
|
|
await admin.a_delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_delete_realm_roles_of_client_scope(
|
|
client_id=client_id, roles=[await admin.a_get_realm_role(role_name="offline_access")]
|
|
)
|
|
assert res == dict(), res
|
|
roles = await admin.a_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 = await admin.a_delete_realm_roles_of_client_scope(
|
|
client_id=client_id, roles=[await admin.a_get_realm_role(role_name="test-realm-role")]
|
|
)
|
|
assert res == dict(), res
|
|
roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
|
|
assert len(roles) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
|
|
"""Test client assignment of other client roles.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
:param client: Keycloak client
|
|
:type client: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
client_id = await admin.a_create_client(
|
|
payload={"name": "role-testing-client", "clientId": "role-testing-client"}
|
|
)
|
|
|
|
# Test get client roles
|
|
roles = await admin.a_get_client_roles_of_client_scope(client_id, client)
|
|
assert len(roles) == 0, roles
|
|
|
|
# create client role for test
|
|
client_role_id = await admin.a_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:
|
|
await admin.a_assign_client_roles_to_client_scope(
|
|
client_id=client_id, client_roles_owner_id=client, roles=["bad"]
|
|
)
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_client_roles_to_client_scope(
|
|
client_id=client_id,
|
|
client_roles_owner_id=client,
|
|
roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
|
|
)
|
|
assert res == dict(), res
|
|
|
|
roles = await admin.a_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:
|
|
await admin.a_delete_client_roles_of_client_scope(
|
|
client_id=client_id, client_roles_owner_id=client, roles=["bad"]
|
|
)
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_delete_client_roles_of_client_scope(
|
|
client_id=client_id,
|
|
client_roles_owner_id=client,
|
|
roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
|
|
)
|
|
assert res == dict(), res
|
|
roles = await admin.a_get_client_roles_of_client_scope(
|
|
client_id=client_id, client_roles_owner_id=client
|
|
)
|
|
assert len(roles) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
client_id = await admin.a_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 = await admin.a_get_client_default_client_scopes(client_id)
|
|
assert len(default_client_scopes) in [6, 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 = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
|
|
new_default_client_scope_data = {
|
|
"realm": realm,
|
|
"client": client_id,
|
|
"clientScopeId": new_client_scope_id,
|
|
}
|
|
await admin.a_add_client_default_client_scope(
|
|
client_id, new_client_scope_id, new_default_client_scope_data
|
|
)
|
|
default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
|
|
assert len(default_client_scopes) in [6, 7], default_client_scopes
|
|
|
|
# Test remove a client default scope
|
|
await admin.a_delete_client_default_client_scope(client_id, new_client_scope_id)
|
|
default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
|
|
assert len(default_client_scopes) in [5, 6], default_client_scopes
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
client_id = await admin.a_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 = await admin.a_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 = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
|
|
new_optional_client_scope_data = {
|
|
"realm": realm,
|
|
"client": client_id,
|
|
"clientScopeId": new_client_scope_id,
|
|
}
|
|
await admin.a_add_client_optional_client_scope(
|
|
client_id, new_client_scope_id, new_optional_client_scope_data
|
|
)
|
|
optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
|
|
assert len(optional_client_scopes) == 5, optional_client_scopes
|
|
|
|
# Test remove a client optional scope
|
|
await admin.a_delete_client_optional_client_scope(client_id, new_client_scope_id)
|
|
optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
|
|
assert len(optional_client_scopes) == 4, optional_client_scopes
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_client_roles(admin: KeycloakAdmin, client: str):
|
|
"""Test client roles.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param client: Keycloak client
|
|
:type client: str
|
|
"""
|
|
# Test get client roles
|
|
res = await admin.a_get_client_roles(client_id=client)
|
|
assert len(res) == 0
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_roles(client_id="bad")
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
# Test create client role
|
|
client_role_id = await admin.a_create_client_role(
|
|
client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
|
|
)
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_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 = await admin.a_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 = await admin.a_get_client_role(client_id=client, role_name="client-role-test")
|
|
assert res["name"] == client_role_id
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_role(client_id=client, role_name="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
res_ = await admin.a_get_client_role_id(client_id=client, role_name="client-role-test")
|
|
assert res_ == res["id"]
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_role_id(client_id=client, role_name="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
assert len(await admin.a_get_client_roles(client_id=client)) == 1
|
|
|
|
# Test update client role
|
|
res = await admin.a_update_client_role(
|
|
client_id=client, role_name="client-role-test", payload={"name": "client-role-test-update"}
|
|
)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
res = await admin.a_update_client_role(
|
|
client_id=client,
|
|
role_name="client-role-test",
|
|
payload={"name": "client-role-test-update"},
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test user with client role
|
|
res = await admin.a_get_client_role_members(
|
|
client_id=client, role_name="client-role-test-update"
|
|
)
|
|
assert len(res) == 0
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_role_members(client_id=client, role_name="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_client_role(
|
|
user_id=user_id,
|
|
client_id=client,
|
|
roles=[
|
|
await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
|
|
],
|
|
)
|
|
assert res == dict()
|
|
assert (
|
|
len(
|
|
await admin.a_get_client_role_members(
|
|
client_id=client, role_name="client-role-test-update"
|
|
)
|
|
)
|
|
== 1
|
|
)
|
|
|
|
roles = await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)
|
|
assert len(roles) == 1, roles
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_roles_of_user(user_id=user_id, client_id="bad")
|
|
assert err.match(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
roles = await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id=client)
|
|
assert len(roles) == 1, roles
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
|
|
assert err.match(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
roles = await admin.a_get_available_client_roles_of_user(user_id=user_id, client_id=client)
|
|
assert len(roles) == 0, roles
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
|
|
assert err.match(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
await admin.a_delete_client_roles_of_user(
|
|
user_id=user_id,
|
|
client_id=client,
|
|
roles=[
|
|
await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
|
|
],
|
|
)
|
|
assert len(await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
|
|
|
|
# Test groups and client roles
|
|
res = await admin.a_get_client_role_groups(
|
|
client_id=client, role_name="client-role-test-update"
|
|
)
|
|
assert len(res) == 0
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_role_groups(client_id=client, role_name="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
group_id = await admin.a_create_group(payload={"name": "test-group"})
|
|
res = await admin.a_get_group_client_roles(group_id=group_id, client_id=client)
|
|
assert len(res) == 0
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_group_client_roles(group_id=group_id, client_id="bad")
|
|
assert err.match(CLIENT_NOT_FOUND_REGEX)
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_assign_group_client_roles(
|
|
group_id=group_id,
|
|
client_id=client,
|
|
roles=[
|
|
await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
|
|
],
|
|
)
|
|
assert res == dict()
|
|
assert (
|
|
len(
|
|
await admin.a_get_client_role_groups(
|
|
client_id=client, role_name="client-role-test-update"
|
|
)
|
|
)
|
|
== 1
|
|
)
|
|
assert len(await admin.a_get_group_client_roles(group_id=group_id, client_id=client)) == 1
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_delete_group_client_roles(
|
|
group_id=group_id,
|
|
client_id=client,
|
|
roles=[
|
|
await admin.a_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:
|
|
await admin.a_add_composite_client_roles_to_role(
|
|
client_role_id=client, role_name="client-role-test-update", roles=["bad"]
|
|
)
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
res = await admin.a_add_composite_client_roles_to_role(
|
|
client_role_id=client,
|
|
role_name="client-role-test-update",
|
|
roles=[await admin.a_get_realm_role(role_name="offline_access")],
|
|
)
|
|
assert res == dict()
|
|
assert (await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"))[
|
|
"composite"
|
|
]
|
|
|
|
# Test delete of client role
|
|
res = await admin.a_delete_client_role(
|
|
client_role_id=client, role_name="client-role-test-update"
|
|
)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_client_role(
|
|
client_role_id=client, role_name="client-role-test-update"
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_REGEX)
|
|
|
|
# Test of roles by id - Get role
|
|
await admin.a_create_client_role(
|
|
client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
|
|
)
|
|
role = await admin.a_get_client_role(client_id=client, role_name="client-role-by-id-test")
|
|
res = await admin.a_get_role_by_id(role_id=role["id"])
|
|
assert res["name"] == "client-role-by-id-test"
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_role_by_id(role_id="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
# Test of roles by id - Update role
|
|
res = await admin.a_update_role_by_id(
|
|
role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
|
|
)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
res = await admin.a_update_role_by_id(
|
|
role_id="bad", payload={"name": "client-role-by-id-test-update"}
|
|
)
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
# Test of roles by id - Delete role
|
|
res = await admin.a_delete_role_by_id(role_id=role["id"])
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_role_by_id(role_id="bad")
|
|
assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_enable_token_exchange(admin: KeycloakAdmin, realm: str):
|
|
"""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
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Create test clients
|
|
source_client_id = await admin.a_create_client(
|
|
payload={"name": "Source Client", "clientId": "source-client"}
|
|
)
|
|
target_client_id = await admin.a_create_client(
|
|
payload={"name": "Target Client", "clientId": "target-client"}
|
|
)
|
|
for c in await admin.a_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
|
|
await admin.a_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 = (
|
|
await admin.a_get_client_management_permissions(client_id=target_client_id)
|
|
)["scopePermissions"]["token-exchange"]
|
|
scopes = await admin.a_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 = await admin.a_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
|
|
policy_name = "Exchange source client token with target client token"
|
|
client_policy_id = (
|
|
await admin.a_create_client_authz_client_policy(
|
|
payload={
|
|
"type": "client",
|
|
"logic": "POSITIVE",
|
|
"decisionStrategy": "UNANIMOUS",
|
|
"name": policy_name,
|
|
"clients": [source_client_id],
|
|
},
|
|
client_id=realm_management_id,
|
|
)
|
|
)["id"]
|
|
policies = await admin.a_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 = (
|
|
await admin.a_get_client_authz_scope_permission(
|
|
client_id=realm_management_id, scope_id=token_exchange_permission_id
|
|
)
|
|
)["name"]
|
|
await admin.a_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,
|
|
)
|
|
|
|
# Create permissions on the target client to reference this policy
|
|
await admin.a_create_client_authz_scope_permission(
|
|
payload={
|
|
"id": "some-id",
|
|
"name": "test-permission",
|
|
"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,
|
|
)
|
|
permission_name = (
|
|
await admin.a_get_client_authz_scope_permission(
|
|
client_id=realm_management_id, scope_id=token_exchange_permission_id
|
|
)
|
|
)["name"]
|
|
assert permission_name.startswith("token-exchange.permission.client.")
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_client_authz_scope_permission(
|
|
payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
|
|
client_id="realm_management_id",
|
|
)
|
|
assert err.match('404: b\'{"error":"Could not find client".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_email(admin: KeycloakAdmin, user: str):
|
|
"""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:
|
|
await admin.a_send_update_account(user_id=user, payload=dict())
|
|
assert err.match(UNKOWN_ERROR_REGEX), err
|
|
|
|
admin.update_user(user_id=user, payload={"enabled": True})
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_send_verify_email(user_id=user)
|
|
assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_get_sessions(admin: KeycloakAdmin):
|
|
"""Test get sessions.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
sessions = await admin.a_get_sessions(
|
|
user_id=admin.get_user_id(username=admin.connection.username)
|
|
)
|
|
assert len(sessions) >= 1
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_sessions(user_id="bad")
|
|
assert err.match(USER_NOT_FOUND_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_get_client_installation_provider(admin: KeycloakAdmin, client: str):
|
|
"""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:
|
|
await admin.a_get_client_installation_provider(client_id=client, provider_id="bad")
|
|
assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
|
|
|
|
installation = await admin.a_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",
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_auth_flows(admin: KeycloakAdmin, realm: str):
|
|
"""Test auth flows.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
res = await admin.a_get_authentication_flows()
|
|
assert len(res) <= 8, res
|
|
default_flows = len(res)
|
|
assert {x["alias"] for x in res}.issubset(
|
|
{
|
|
"reset credentials",
|
|
"browser",
|
|
"registration",
|
|
"http challenge",
|
|
"docker auth",
|
|
"direct grant",
|
|
"first broker login",
|
|
"clients",
|
|
}
|
|
)
|
|
assert set(res[0].keys()) == {
|
|
"alias",
|
|
"authenticationExecutions",
|
|
"builtIn",
|
|
"description",
|
|
"id",
|
|
"providerId",
|
|
"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:
|
|
await admin.a_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 = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
|
|
assert res["alias"] == "browser"
|
|
|
|
# Test copying
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_copy_authentication_flow(payload=dict(), flow_alias="bad")
|
|
assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
|
|
|
|
res = await admin.a_copy_authentication_flow(
|
|
payload={"newName": "test-browser"}, flow_alias="browser"
|
|
)
|
|
assert res == b"", res
|
|
assert len(await admin.a_get_authentication_flows()) == (default_flows + 1)
|
|
|
|
# Test create
|
|
res = await admin.a_create_authentication_flow(
|
|
payload={"alias": "test-create", "providerId": "basic-flow"}
|
|
)
|
|
assert res == b""
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_authentication_flow(
|
|
payload={"alias": "test-create", "builtIn": False}
|
|
)
|
|
assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
|
|
assert await admin.a_create_authentication_flow(
|
|
payload={"alias": "test-create"}, skip_exists=True
|
|
) == {"msg": "Already exists"}
|
|
|
|
# Test flow executions
|
|
res = await admin.a_get_authentication_flow_executions(flow_alias="browser")
|
|
assert len(res) == 8, res
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_authentication_flow_executions(flow_alias="bad")
|
|
assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
|
|
exec_id = res[0]["id"]
|
|
|
|
res = await admin.a_get_authentication_flow_execution(execution_id=exec_id)
|
|
assert set(res.keys()).issubset(
|
|
{
|
|
"alternative",
|
|
"authenticator",
|
|
"authenticatorFlow",
|
|
"autheticatorFlow",
|
|
"conditional",
|
|
"disabled",
|
|
"enabled",
|
|
"id",
|
|
"parentFlow",
|
|
"priority",
|
|
"required",
|
|
"requirement",
|
|
}
|
|
), res.keys()
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_authentication_flow_execution(execution_id="bad")
|
|
assert err.match(ILLEGAL_EXECUTION_REGEX)
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_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 = await admin.a_create_authentication_flow_execution(
|
|
payload={"provider": "auth-cookie"}, flow_alias="test-create"
|
|
)
|
|
assert res == b""
|
|
assert len(await admin.a_get_authentication_flow_executions(flow_alias="test-create")) == 1
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_authentication_flow_executions(
|
|
payload={"required": "yes"}, flow_alias="test-create"
|
|
)
|
|
assert err.match('400: b\'{"error":"Unrecognized field')
|
|
payload = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]
|
|
payload["displayName"] = "test"
|
|
res = await admin.a_update_authentication_flow_executions(
|
|
payload=payload, flow_alias="test-create"
|
|
)
|
|
assert res or (res == {})
|
|
|
|
exec_id = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]["id"]
|
|
res = await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
|
|
assert err.match(ILLEGAL_EXECUTION_REGEX)
|
|
|
|
# Test subflows
|
|
res = await admin.a_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:
|
|
await admin.a_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 = await admin.a_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 await admin.a_get_authentication_flows() if x["alias"] == "test-browser"
|
|
][0]["id"]
|
|
res = await admin.a_delete_authentication_flow(flow_id=flow_id)
|
|
assert res == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_authentication_flow(flow_id=flow_id)
|
|
assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
|
|
'b\'{"error":"Flow not found"' in str(err)
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_authentication_configs(admin: KeycloakAdmin, realm: str):
|
|
"""Test authentication configs.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin.change_current_realm(realm)
|
|
|
|
# Test list of auth providers
|
|
res = await admin.a_get_authenticator_providers()
|
|
assert len(res) <= 38
|
|
|
|
res = await admin.a_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:
|
|
await admin.a_get_authenticator_config(config_id="bad")
|
|
assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_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:
|
|
await admin.a_delete_authenticator_config(config_id="bad")
|
|
assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_sync_users(admin: KeycloakAdmin, realm: str):
|
|
"""Test sync users.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Only testing the error message
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_sync_users(storage_id="does-not-exist", action="triggerFullSync")
|
|
assert err.match('404: b\'{"error":"Could not find component".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_client_scopes(admin: KeycloakAdmin, realm: str):
|
|
"""Test client scopes.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test get client scopes
|
|
res = await admin.a_get_client_scopes()
|
|
scope_names = {x["name"] for x in res}
|
|
assert len(res) in [10, 11]
|
|
assert "email" in scope_names
|
|
assert "profile" in scope_names
|
|
assert "offline_access" in scope_names
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_client_scope(client_scope_id="does-not-exist")
|
|
assert err.match(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
scope = await admin.a_get_client_scope(client_scope_id=res[0]["id"])
|
|
assert res[0] == scope
|
|
|
|
scope = await admin.a_get_client_scope_by_name(client_scope_name=res[0]["name"])
|
|
assert res[0] == scope
|
|
|
|
# Test create client scope
|
|
res = await admin.a_create_client_scope(
|
|
payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
|
|
)
|
|
assert res
|
|
res2 = await admin.a_create_client_scope(
|
|
payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
|
|
)
|
|
assert res == res2
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_client_scope(
|
|
payload={"name": "test-scope", "protocol": "openid-connect"}, 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:
|
|
await admin.a_update_client_scope(client_scope_id="does-not-exist", payload=dict())
|
|
assert err.match(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
res_update = await admin.a_update_client_scope(
|
|
client_scope_id=res, payload={"name": "test-scope-update"}
|
|
)
|
|
assert res_update == dict()
|
|
assert (await admin.a_get_client_scope(client_scope_id=res))["name"] == "test-scope-update"
|
|
|
|
# Test get mappers
|
|
mappers = await admin.a_get_mappers_from_client_scope(client_scope_id=res)
|
|
assert mappers == list()
|
|
|
|
# Test add mapper
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_add_mapper_to_client_scope(client_scope_id=res, payload=dict())
|
|
assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
|
|
|
|
res_add = await admin.a_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(await admin.a_get_mappers_from_client_scope(client_scope_id=res)) == 1
|
|
|
|
# Test update mapper
|
|
test_mapper = (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_mapper_in_client_scope(
|
|
client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
|
|
)
|
|
assert err.match(NO_CLIENT_SCOPE_REGEX)
|
|
test_mapper["config"]["user.attribute"] = "test"
|
|
res_update = await admin.a_update_mapper_in_client_scope(
|
|
client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
|
|
)
|
|
assert res_update == dict()
|
|
assert (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]["config"][
|
|
"user.attribute"
|
|
] == "test"
|
|
|
|
# Test delete mapper
|
|
res_del = await admin.a_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:
|
|
await admin.a_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 = await admin.a_get_default_default_client_scopes()
|
|
assert len(res_defaults) in [6, 7]
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_add_default_default_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_add = await admin.a_add_default_default_client_scope(scope_id=res)
|
|
assert res_add == dict()
|
|
assert len(admin.get_default_default_client_scopes()) in [7, 8]
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_default_default_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_del = await admin.a_delete_default_default_client_scope(scope_id=res)
|
|
assert res_del == dict()
|
|
assert len(admin.get_default_default_client_scopes()) in [6, 7]
|
|
|
|
# Test default optional scopes
|
|
res_defaults = await admin.a_get_default_optional_client_scopes()
|
|
assert len(res_defaults) == 4
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_add_default_optional_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_add = await admin.a_add_default_optional_client_scope(scope_id=res)
|
|
assert res_add == dict()
|
|
assert len(await admin.a_get_default_optional_client_scopes()) == 5
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_default_optional_client_scope(scope_id="does-not-exist")
|
|
assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
|
|
|
|
res_del = await admin.a_delete_default_optional_client_scope(scope_id=res)
|
|
assert res_del == dict()
|
|
assert len(await admin.a_get_default_optional_client_scopes()) == 4
|
|
|
|
# Test client scope delete
|
|
res_del = await admin.a_delete_client_scope(client_scope_id=res)
|
|
assert res_del == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_client_scope(client_scope_id=res)
|
|
assert err.match(NO_CLIENT_SCOPE_REGEX)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_components(admin: KeycloakAdmin, realm: str):
|
|
"""Test components.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test get components
|
|
res = await admin.a_get_components()
|
|
assert len(res) == 12
|
|
|
|
with pytest.raises(KeycloakGetError) as err:
|
|
await admin.a_get_component(component_id="does-not-exist")
|
|
assert err.match('404: b\'{"error":"Could not find component".*}\'')
|
|
|
|
res_get = await admin.a_get_component(component_id=res[0]["id"])
|
|
assert res_get == res[0]
|
|
|
|
# Test create component
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_create_component(payload={"bad": "dict"})
|
|
assert err.match('400: b\'{"error":"Unrecognized field')
|
|
|
|
res = await admin.a_create_component(
|
|
payload={
|
|
"name": "Test Component",
|
|
"providerId": "max-clients",
|
|
"providerType": "org.keycloak.services.clientregistration."
|
|
+ "policy.ClientRegistrationPolicy",
|
|
"config": {"max-clients": ["1000"]},
|
|
}
|
|
)
|
|
assert res
|
|
assert (await admin.a_get_component(component_id=res))["name"] == "Test Component"
|
|
|
|
# Test update component
|
|
component = await admin.a_get_component(component_id=res)
|
|
component["name"] = "Test Component Update"
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_update_component(component_id="does-not-exist", payload=dict())
|
|
assert err.match('404: b\'{"error":"Could not find component".*}\'')
|
|
res_upd = await admin.a_update_component(component_id=res, payload=component)
|
|
assert res_upd == dict()
|
|
assert (await admin.a_get_component(component_id=res))["name"] == "Test Component Update"
|
|
|
|
# Test delete component
|
|
res_del = await admin.a_delete_component(component_id=res)
|
|
assert res_del == dict()
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_delete_component(component_id=res)
|
|
assert err.match('404: b\'{"error":"Could not find component".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_keys(admin: KeycloakAdmin, realm: str):
|
|
"""Test keys.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
assert set((await admin.a_get_keys())["active"].keys()) == {
|
|
"AES",
|
|
"HS256",
|
|
"RS256",
|
|
"RSA-OAEP",
|
|
} or set((await admin.a_get_keys())["active"].keys()) == {"RSA-OAEP", "RS256", "HS512", "AES"}
|
|
assert {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
|
|
"HS256",
|
|
"RSA-OAEP",
|
|
"AES",
|
|
"RS256",
|
|
} or {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
|
|
"HS512",
|
|
"RSA-OAEP",
|
|
"AES",
|
|
"RS256",
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_admin_events(admin: KeycloakAdmin, realm: str):
|
|
"""Test events.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
await admin.a_create_client(payload={"name": "test", "clientId": "test"})
|
|
|
|
events = await admin.a_get_admin_events()
|
|
assert events == list()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_user_events(admin: KeycloakAdmin, realm: str):
|
|
"""Test events.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
events = await admin.a_get_events()
|
|
assert events == list()
|
|
|
|
with pytest.raises(KeycloakPutError) as err:
|
|
await admin.a_set_events(payload={"bad": "conf"})
|
|
assert err.match('400: b\'{"error":"Unrecognized field')
|
|
|
|
res = await admin.a_set_events(
|
|
payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True}
|
|
)
|
|
assert res == dict()
|
|
|
|
await admin.a_create_client(payload={"name": "test", "clientId": "test"})
|
|
|
|
events = await admin.a_get_events()
|
|
assert events == list()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@freezegun.freeze_time("2023-02-25 10:00:00")
|
|
async def test_a_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
|
|
"""Test auto refresh token.
|
|
|
|
:param admin_frozen: Keycloak Admin client with time frozen in place
|
|
:type admin_frozen: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
admin = admin_frozen
|
|
admin.get_realm(realm)
|
|
# Test get refresh
|
|
admin.connection.custom_headers = {
|
|
"Authorization": "Bearer bad",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
with pytest.raises(KeycloakAuthenticationError) as err:
|
|
await admin.a_get_realm(realm_name=realm)
|
|
assert err.match('401: b\'{"error":"HTTP 401 Unauthorized".*}\'')
|
|
|
|
# Freeze time to simulate the access token expiring
|
|
with freezegun.freeze_time("2023-02-25 10:05:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
|
|
assert await admin.a_get_realm(realm_name=realm)
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
|
|
|
|
# Test bad refresh token, but first make sure access token has expired again
|
|
with freezegun.freeze_time("2023-02-25 10:10:00"):
|
|
admin.connection.custom_headers = {"Content-Type": "application/json"}
|
|
admin.connection.token["refresh_token"] = "bad"
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_get_realm(realm_name="test-refresh")
|
|
assert err.match(
|
|
'400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
|
|
)
|
|
admin.connection.get_token()
|
|
|
|
# Test post refresh
|
|
with freezegun.freeze_time("2023-02-25 10:15:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
|
|
admin.connection.token = None
|
|
assert await admin.a_create_realm(payload={"realm": "test-refresh"}) == b""
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
|
|
|
|
# Test update refresh
|
|
with freezegun.freeze_time("2023-02-25 10:25:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
|
|
admin.connection.token = None
|
|
assert (
|
|
await admin.a_update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
|
|
== dict()
|
|
)
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
|
|
|
|
# Test delete refresh
|
|
with freezegun.freeze_time("2023-02-25 10:35:00"):
|
|
assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
|
|
admin.connection.token = None
|
|
assert await admin.a_delete_realm(realm_name="test-refresh") == dict()
|
|
assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_get_required_actions(admin: KeycloakAdmin, realm: str):
|
|
"""Test required actions.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
ractions = await admin.a_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
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
|
|
"""Test get required action by alias.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
ractions = await admin.a_get_required_actions()
|
|
ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
|
|
assert ra in ractions
|
|
assert ra["alias"] == "UPDATE_PASSWORD"
|
|
assert await admin.a_get_required_action_by_alias("does-not-exist") is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_update_required_action(admin: KeycloakAdmin, realm: str):
|
|
"""Test update required action.
|
|
|
|
:param admin: Keycloak Admin client
|
|
:type admin: KeycloakAdmin
|
|
:param realm: Keycloak realm
|
|
:type realm: str
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
|
|
old = copy.deepcopy(ra)
|
|
ra["enabled"] = False
|
|
admin.update_required_action("UPDATE_PASSWORD", ra)
|
|
newra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
|
|
assert old != newra
|
|
assert newra["enabled"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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.
|
|
|
|
: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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
role = await admin.a_get_client_role(client, composite_client_role)
|
|
await admin.a_assign_group_client_roles(group_id=group, client_id=client, roles=[role])
|
|
result = await admin.a_get_composite_client_roles_of_group(client, group)
|
|
assert role["id"] in [x["id"] for x in result]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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.
|
|
|
|
: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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
child = await admin.a_get_client_role(client, client_role)
|
|
parent = await admin.a_get_client_role(client, composite_client_role)
|
|
res = await admin.a_get_role_client_level_children(client, parent["id"])
|
|
assert child["id"] in [x["id"] for x in res]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_upload_certificate(
|
|
admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple
|
|
):
|
|
"""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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
cert, _ = selfsigned_cert
|
|
cert = cert.decode("utf-8").strip()
|
|
admin.upload_certificate(client, cert)
|
|
cl = await admin.a_get_client(client)
|
|
assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Turn on bruteforce protection
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
|
|
res = await admin.a_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 = await admin.a_get_user_id(username)
|
|
bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
|
|
|
|
assert bruteforce_status["numFailures"] == 1
|
|
|
|
# Cleanup
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
|
|
res = await admin.a_get_realm(realm_name=realm)
|
|
assert res["bruteForceProtected"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Turn on bruteforce protection
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
|
|
res = await admin.a_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 = await admin.a_get_user_id(username)
|
|
bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
|
|
assert bruteforce_status["numFailures"] == 1
|
|
|
|
res = await admin.a_clear_bruteforce_attempts_for_user(user_id)
|
|
bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
|
|
assert bruteforce_status["numFailures"] == 0
|
|
|
|
# Cleanup
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
|
|
res = await admin.a_get_realm(realm_name=realm)
|
|
assert res["bruteForceProtected"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Turn on bruteforce protection
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
|
|
res = await admin.a_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 = await admin.a_get_user_id(username)
|
|
bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
|
|
assert bruteforce_status["numFailures"] == 1
|
|
|
|
res = await admin.a_clear_all_bruteforce_attempts()
|
|
bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
|
|
assert bruteforce_status["numFailures"] == 0
|
|
|
|
# Cleanup
|
|
res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
|
|
res = await admin.a_get_realm(realm_name=realm)
|
|
assert res["bruteForceProtected"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
|
|
assert (
|
|
len(
|
|
[
|
|
x["name"]
|
|
for x in await admin.a_get_realm_roles()
|
|
if x["name"] == f"default-roles-{realm}"
|
|
]
|
|
)
|
|
== 1
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
assert (
|
|
await admin.a_get_default_realm_role_id()
|
|
== [
|
|
x["id"]
|
|
for x in await admin.a_get_realm_roles()
|
|
if x["name"] == f"default-roles-{realm}"
|
|
][0]
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_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
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test listing all default realm roles
|
|
roles = await admin.a_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:
|
|
await admin.a_change_current_realm("doesnotexist")
|
|
await admin.a_get_realm_default_roles()
|
|
assert err.match('404: b\'{"error":"Realm not found.".*}\'')
|
|
await admin.a_change_current_realm(realm)
|
|
|
|
# Test removing a default realm role
|
|
res = await admin.a_remove_realm_default_roles(payload=[roles[0]])
|
|
assert res == {}
|
|
assert roles[0] not in await admin.a_get_realm_default_roles()
|
|
assert len(await admin.a_get_realm_default_roles()) == 1
|
|
|
|
with pytest.raises(KeycloakDeleteError) as err:
|
|
await admin.a_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 = await admin.a_add_realm_default_roles(payload=[roles[0]])
|
|
assert res == {}
|
|
assert roles[0] in await admin.a_get_realm_default_roles()
|
|
assert len(await admin.a_get_realm_default_roles()) == 2
|
|
|
|
with pytest.raises(KeycloakPostError) as err:
|
|
await admin.a_add_realm_default_roles(payload=[{"id": "bad id"}])
|
|
assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the keys cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
res = await admin.a_clear_keys_cache()
|
|
assert res == {}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the realm cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
res = await admin.a_clear_realm_cache()
|
|
assert res == {}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
|
|
"""Test clearing the user cache.
|
|
|
|
:param realm: Realm name
|
|
:type realm: str
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
await admin.a_change_current_realm(realm)
|
|
res = await admin.a_clear_user_cache()
|
|
assert res == {}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_initial_access_token(
|
|
admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
|
|
) -> None:
|
|
"""Test initial access token and client creation.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
:param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
|
|
:type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
|
|
"""
|
|
res = await admin.a_create_initial_access_token(2, 3)
|
|
assert "token" in res
|
|
assert res["count"] == 2
|
|
assert res["expiration"] == 3
|
|
|
|
oid, username, password = oid_with_credentials
|
|
|
|
client = str(uuid.uuid4())
|
|
secret = str(uuid.uuid4())
|
|
|
|
res = await oid.a_register_client(
|
|
token=res["token"],
|
|
payload={
|
|
"name": "DynamicRegisteredClient",
|
|
"clientId": client,
|
|
"enabled": True,
|
|
"publicClient": False,
|
|
"protocol": "openid-connect",
|
|
"secret": secret,
|
|
"clientAuthenticatorType": "client-secret",
|
|
},
|
|
)
|
|
assert res["clientId"] == client
|
|
|
|
new_secret = str(uuid.uuid4())
|
|
res = await oid.a_update_client(
|
|
res["registrationAccessToken"], client, payload={"secret": new_secret}
|
|
)
|
|
assert res["secret"] == new_secret
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_refresh_token(admin: KeycloakAdmin):
|
|
"""Test refresh token on connection even if it is expired.
|
|
|
|
:param admin: Keycloak admin
|
|
:type admin: KeycloakAdmin
|
|
"""
|
|
admin.get_realms()
|
|
assert admin.connection.token is not None
|
|
await admin.a_user_logout(await admin.a_get_user_id(admin.connection.username))
|
|
admin.connection.refresh_token()
|
|
|
|
|
|
def test_counter_part():
|
|
"""Test that each function has its async counter part."""
|
|
admin_methods = [func for func in dir(KeycloakAdmin) if callable(getattr(KeycloakAdmin, func))]
|
|
sync_methods = [
|
|
method
|
|
for method in admin_methods
|
|
if not method.startswith("a_") and not method.startswith("_")
|
|
]
|
|
async_methods = [
|
|
method for method in admin_methods if iscoroutinefunction(getattr(KeycloakAdmin, method))
|
|
]
|
|
|
|
for method in sync_methods:
|
|
async_method = f"a_{method}"
|
|
assert (async_method in admin_methods) is True
|
|
sync_sign = signature(getattr(KeycloakAdmin, method))
|
|
async_sign = signature(getattr(KeycloakAdmin, async_method))
|
|
assert sync_sign.parameters == async_sign.parameters
|
|
|
|
for async_method in async_methods:
|
|
if async_method[2:].startswith("_"):
|
|
continue
|
|
|
|
assert async_method[2:] in sync_methods
|