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.

7012 lines
242 KiB

11 months ago
  1. """Test the keycloak admin object."""
  2. import contextlib
  3. import copy
  4. import os
  5. import uuid
  6. from inspect import iscoroutinefunction, signature
  7. from unittest.mock import ANY, patch
  8. import freezegun
  9. import pytest
  10. from dateutil import parser as datetime_parser
  11. from packaging.version import Version
  12. import keycloak
  13. from keycloak import (
  14. KeycloakAdmin,
  15. KeycloakConnectionError,
  16. KeycloakOpenID,
  17. KeycloakOpenIDConnection,
  18. )
  19. from keycloak.connection import ConnectionManager
  20. from keycloak.exceptions import (
  21. KeycloakAuthenticationError,
  22. KeycloakDeleteError,
  23. KeycloakGetError,
  24. KeycloakPostError,
  25. KeycloakPutError,
  26. )
  27. from tests.conftest import KeycloakTestEnv
  28. CLIENT_NOT_FOUND_REGEX = '404: b\'{"error":"Client not found".*}\''
  29. CLIENT_SCOPE_NOT_FOUND_REGEX = '404: b\'{"error":"Client scope not found".*}\''
  30. COULD_NOT_FIND_ROLE_REGEX = '404: b\'{"error":"Could not find role".*}\''
  31. COULD_NOT_FIND_ROLE_WITH_ID_REGEX = '404: b\'{"error":"Could not find role with id".*}\''
  32. HTTP_404_REGEX = '404: b\'{"error":"HTTP 404 Not Found".*}\''
  33. ILLEGAL_EXECUTION_REGEX = '404: b\'{"error":"Illegal execution".*}\''
  34. NO_CLIENT_SCOPE_REGEX = '404: b\'{"error":"Could not find client scope".*}\''
  35. UNKOWN_ERROR_REGEX = 'b\'{"error":"unknown_error".*}\''
  36. USER_NOT_FOUND_REGEX = '404: b\'{"error":"User not found".*}\''
  37. def test_keycloak_version() -> None:
  38. """Test version."""
  39. assert keycloak.__version__, keycloak.__version__
  40. def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
  41. """
  42. Test keycloak admin init.
  43. :param env: Environment fixture
  44. :type env: KeycloakTestEnv
  45. """
  46. admin = KeycloakAdmin(
  47. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  48. username=env.keycloak_admin,
  49. password=env.keycloak_admin_password,
  50. )
  51. assert admin.connection.server_url == f"http://{env.keycloak_host}:{env.keycloak_port}", (
  52. admin.connection.server_url
  53. )
  54. assert admin.connection.realm_name == "master", admin.connection.realm_name
  55. assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
  56. assert admin.connection.client_id == "admin-cli", admin.connection.client_id
  57. assert admin.connection.client_secret_key is None, admin.connection.client_secret_key
  58. assert admin.connection.verify, admin.connection.verify
  59. assert admin.connection.username == env.keycloak_admin, admin.connection.username
  60. assert admin.connection.password == env.keycloak_admin_password, admin.connection.password
  61. assert admin.connection.totp is None, admin.connection.totp
  62. assert admin.connection.token is None, admin.connection.token
  63. assert admin.connection.user_realm_name is None, admin.connection.user_realm_name
  64. assert admin.connection.custom_headers is None, admin.connection.custom_headers
  65. admin = KeycloakAdmin(
  66. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  67. username=env.keycloak_admin,
  68. password=env.keycloak_admin_password,
  69. realm_name=None,
  70. user_realm_name="master",
  71. )
  72. assert admin.connection.token is None
  73. admin = KeycloakAdmin(
  74. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  75. username=env.keycloak_admin,
  76. password=env.keycloak_admin_password,
  77. realm_name=None,
  78. user_realm_name=None,
  79. )
  80. assert admin.connection.token is None
  81. admin.get_realms()
  82. token = admin.connection.token
  83. admin = KeycloakAdmin(
  84. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  85. token=token,
  86. realm_name=None,
  87. user_realm_name=None,
  88. )
  89. assert admin.connection.token == token
  90. admin.create_realm(payload={"realm": "authz", "enabled": True})
  91. admin.connection.realm_name = "authz"
  92. admin.create_client(
  93. payload={
  94. "name": "authz-client",
  95. "clientId": "authz-client",
  96. "authorizationServicesEnabled": True,
  97. "serviceAccountsEnabled": True,
  98. "clientAuthenticatorType": "client-secret",
  99. "directAccessGrantsEnabled": False,
  100. "enabled": True,
  101. "implicitFlowEnabled": False,
  102. "publicClient": False,
  103. },
  104. )
  105. secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client"))
  106. admin_auth = KeycloakAdmin(
  107. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  108. user_realm_name="authz",
  109. client_id="authz-client",
  110. client_secret_key=secret["value"],
  111. )
  112. admin_auth.connection.refresh_token()
  113. assert admin_auth.connection.token is not None
  114. admin.delete_realm(realm_name="authz")
  115. assert (
  116. KeycloakAdmin(
  117. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  118. username=None,
  119. password=None,
  120. client_secret_key=None,
  121. custom_headers={"custom": "header"},
  122. ).connection.token
  123. is None
  124. )
  125. keycloak_connection = KeycloakOpenIDConnection(
  126. server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
  127. username=env.keycloak_admin,
  128. password=env.keycloak_admin_password,
  129. realm_name="master",
  130. client_id="admin-cli",
  131. verify=True,
  132. )
  133. keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
  134. keycloak_admin.connection.get_token()
  135. assert keycloak_admin.connection.token
  136. def test_realms(admin: KeycloakAdmin) -> None:
  137. """
  138. Test realms.
  139. :param admin: Keycloak Admin client
  140. :type admin: KeycloakAdmin
  141. """
  142. # Get realms
  143. realms = admin.get_realms()
  144. assert len(realms) == 1, realms
  145. assert realms[0]["realm"] == "master"
  146. # Create a test realm
  147. res = admin.create_realm(payload={"realm": "test"})
  148. assert res == b"", res
  149. # Create the same realm, should fail
  150. with pytest.raises(KeycloakPostError) as err:
  151. res = admin.create_realm(payload={"realm": "test"})
  152. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  153. # Create the same realm, skip_exists true
  154. res = admin.create_realm(payload={"realm": "test"}, skip_exists=True)
  155. assert res == {"msg": "Already exists"}, res
  156. # Get a single realm
  157. res = admin.get_realm(realm_name="test")
  158. assert res["realm"] == "test"
  159. # Get non-existing realm
  160. with pytest.raises(KeycloakGetError) as err:
  161. admin.get_realm(realm_name="non-existent")
  162. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  163. # Update realm
  164. res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"})
  165. assert res == {}, res
  166. # Check that the update worked
  167. res = admin.get_realm(realm_name="test")
  168. assert res["realm"] == "test"
  169. assert res["accountTheme"] == "test"
  170. # Update wrong payload
  171. with pytest.raises(KeycloakPutError) as err:
  172. admin.update_realm(realm_name="test", payload={"wrong": "payload"})
  173. assert err.match("Unrecognized field")
  174. # Check that get realms returns both realms
  175. realms = admin.get_realms()
  176. realm_names = [x["realm"] for x in realms]
  177. assert len(realms) == 2, realms
  178. assert "master" in realm_names, realm_names
  179. assert "test" in realm_names, realm_names
  180. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  181. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  182. ) >= Version("24"):
  183. # Get users profile, add an attribute
  184. user_profile = admin.get_realm_users_profile()
  185. assert "attributes" in user_profile
  186. new_attribute = {
  187. "name": "surname",
  188. "displayName": "",
  189. "validations": {},
  190. "annotations": {},
  191. "permissions": {"view": [], "edit": ["admin"]},
  192. "multivalued": False,
  193. }
  194. user_profile["attributes"].append(new_attribute)
  195. res = admin.update_realm_users_profile(user_profile)
  196. # Check for new attribute in result
  197. assert "surname" in [x["name"] for x in res["attributes"]]
  198. # Delete the realm
  199. res = admin.delete_realm(realm_name="test")
  200. assert res == {}, res
  201. # Check that the realm does not exist anymore
  202. with pytest.raises(KeycloakGetError) as err:
  203. admin.get_realm(realm_name="test")
  204. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  205. # Delete non-existing realm
  206. with pytest.raises(KeycloakDeleteError) as err:
  207. admin.delete_realm(realm_name="non-existent")
  208. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  209. def test_changing_of_realms(admin: KeycloakAdmin, realm: str) -> None:
  210. """
  211. Test changing of realms.
  212. :param admin: Keycloak Admin client
  213. :type admin: KeycloakAdmin
  214. :param realm: Keycloak realm
  215. :type realm: str
  216. """
  217. assert admin.get_current_realm() == "master"
  218. admin.change_current_realm(realm)
  219. assert admin.get_current_realm() == realm
  220. def test_import_export_realms(admin: KeycloakAdmin, realm: str) -> None:
  221. """
  222. Test import and export of realms.
  223. :param admin: Keycloak Admin client
  224. :type admin: KeycloakAdmin
  225. :param realm: Keycloak realm
  226. :type realm: str
  227. """
  228. admin.change_current_realm(realm)
  229. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True)
  230. assert realm_export != {}, realm_export
  231. admin.delete_realm(realm_name=realm)
  232. admin.realm_name = "master"
  233. res = admin.import_realm(payload=realm_export)
  234. assert res == b"", res
  235. # Test bad import
  236. with pytest.raises(KeycloakPostError) as err:
  237. admin.import_realm(payload={})
  238. assert err.match(
  239. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'', # noqa: E501
  240. )
  241. def test_partial_import_realm(admin: KeycloakAdmin, realm: str) -> None:
  242. """
  243. Test partial import of realm configuration.
  244. :param admin: Keycloak Admin client
  245. :type admin: KeycloakAdmin
  246. :param realm: Keycloak realm
  247. :type realm: str
  248. """
  249. test_realm_role = str(uuid.uuid4())
  250. test_user = str(uuid.uuid4())
  251. test_client = str(uuid.uuid4())
  252. admin.change_current_realm(realm)
  253. client_id = admin.create_client(payload={"name": test_client, "clientId": test_client})
  254. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=False)
  255. client_config = next(
  256. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  257. )
  258. # delete before partial import
  259. admin.delete_client(client_id)
  260. payload = {
  261. "ifResourceExists": "SKIP",
  262. "id": realm_export["id"],
  263. "realm": realm,
  264. "clients": [client_config],
  265. "roles": {"realm": [{"name": test_realm_role}]},
  266. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  267. }
  268. # check add
  269. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  270. assert res["added"] == 3
  271. # check skip
  272. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  273. assert res["skipped"] == 3
  274. # check overwrite
  275. payload["ifResourceExists"] = "OVERWRITE"
  276. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  277. assert res["overwritten"] == 3
  278. def test_users(admin: KeycloakAdmin, realm: str) -> None:
  279. """
  280. Test users.
  281. :param admin: Keycloak Admin client
  282. :type admin: KeycloakAdmin
  283. :param realm: Keycloak realm
  284. :type realm: str
  285. """
  286. admin.change_current_realm(realm)
  287. # Check no users present
  288. users = admin.get_users()
  289. assert users == [], users
  290. # Test create user
  291. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  292. assert user_id is not None, user_id
  293. # Test create the same user
  294. with pytest.raises(KeycloakPostError) as err:
  295. admin.create_user(payload={"username": "test", "email": "test@test.test"})
  296. assert err.match(".*User exists with same.*")
  297. # Test create the same user, exists_ok true
  298. user_id_2 = admin.create_user(
  299. payload={"username": "test", "email": "test@test.test"},
  300. exist_ok=True,
  301. )
  302. assert user_id == user_id_2
  303. # Test get user
  304. user = admin.get_user(user_id=user_id)
  305. assert user["username"] == "test", user["username"]
  306. assert user["email"] == "test@test.test", user["email"]
  307. # Test update user
  308. res = admin.update_user(user_id=user_id, payload={"firstName": "Test"})
  309. assert res == {}, res
  310. user = admin.get_user(user_id=user_id)
  311. assert user["firstName"] == "Test"
  312. # Test update user fail
  313. with pytest.raises(KeycloakPutError) as err:
  314. admin.update_user(user_id=user_id, payload={"wrong": "payload"})
  315. assert err.match("Unrecognized field")
  316. # Test disable user
  317. res = admin.disable_user(user_id=user_id)
  318. assert res == {}, res
  319. assert not admin.get_user(user_id=user_id)["enabled"]
  320. # Test enable user
  321. res = admin.enable_user(user_id=user_id)
  322. assert res == {}, res
  323. assert admin.get_user(user_id=user_id)["enabled"]
  324. # Test get users again
  325. users = admin.get_users()
  326. usernames = [x["username"] for x in users]
  327. assert "test" in usernames
  328. # Test users counts
  329. count = admin.users_count()
  330. assert count == 1, count
  331. # Test users count with query
  332. count = admin.users_count(query={"username": "notpresent"})
  333. assert count == 0
  334. # Test user groups
  335. groups = admin.get_user_groups(user_id=user["id"])
  336. assert len(groups) == 0
  337. # Test user groups bad id
  338. with pytest.raises(KeycloakGetError) as err:
  339. admin.get_user_groups(user_id="does-not-exist")
  340. assert err.match(USER_NOT_FOUND_REGEX)
  341. # Test logout
  342. res = admin.user_logout(user_id=user["id"])
  343. assert res == {}, res
  344. # Test logout fail
  345. with pytest.raises(KeycloakPostError) as err:
  346. admin.user_logout(user_id="non-existent-id")
  347. assert err.match(USER_NOT_FOUND_REGEX)
  348. # Test consents
  349. res = admin.user_consents(user_id=user["id"])
  350. assert len(res) == 0, res
  351. # Test consents fail
  352. with pytest.raises(KeycloakGetError) as err:
  353. admin.user_consents(user_id="non-existent-id")
  354. assert err.match(USER_NOT_FOUND_REGEX)
  355. # Test delete user
  356. res = admin.delete_user(user_id=user_id)
  357. assert res == {}, res
  358. with pytest.raises(KeycloakGetError) as err:
  359. admin.get_user(user_id=user_id)
  360. err.match(USER_NOT_FOUND_REGEX)
  361. # Test delete fail
  362. with pytest.raises(KeycloakDeleteError) as err:
  363. admin.delete_user(user_id="non-existent-id")
  364. assert err.match(USER_NOT_FOUND_REGEX)
  365. def test_enable_disable_all_users(admin: KeycloakAdmin, realm: str) -> None:
  366. """
  367. Test enable and disable all users.
  368. :param admin: Keycloak Admin client
  369. :type admin: KeycloakAdmin
  370. :param realm: Keycloak realm
  371. :type realm: str
  372. """
  373. admin.change_current_realm(realm)
  374. user_id_1 = admin.create_user(
  375. payload={"username": "test", "email": "test@test.test", "enabled": True},
  376. )
  377. user_id_2 = admin.create_user(
  378. payload={"username": "test2", "email": "test2@test.test", "enabled": True},
  379. )
  380. user_id_3 = admin.create_user(
  381. payload={"username": "test3", "email": "test3@test.test", "enabled": True},
  382. )
  383. assert admin.get_user(user_id_1)["enabled"]
  384. assert admin.get_user(user_id_2)["enabled"]
  385. assert admin.get_user(user_id_3)["enabled"]
  386. admin.disable_all_users()
  387. assert not admin.get_user(user_id_1)["enabled"]
  388. assert not admin.get_user(user_id_2)["enabled"]
  389. assert not admin.get_user(user_id_3)["enabled"]
  390. admin.enable_all_users()
  391. assert admin.get_user(user_id_1)["enabled"]
  392. assert admin.get_user(user_id_2)["enabled"]
  393. assert admin.get_user(user_id_3)["enabled"]
  394. def test_users_roles(admin: KeycloakAdmin, realm: str) -> None:
  395. """
  396. Test users roles.
  397. :param admin: Keycloak Admin client
  398. :type admin: KeycloakAdmin
  399. :param realm: Keycloak realm
  400. :type realm: str
  401. """
  402. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  403. # Test all level user roles
  404. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  405. admin.create_client_role(client_role_id=client_id, payload={"name": "test-role"})
  406. admin.assign_client_role(
  407. client_id=client_id,
  408. user_id=user_id,
  409. roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
  410. )
  411. all_roles = admin.get_all_roles_of_user(user_id=user_id)
  412. realm_roles = all_roles["realmMappings"]
  413. assert len(realm_roles) == 1, realm_roles
  414. client_roles = all_roles["clientMappings"]
  415. assert len(client_roles) == 1, client_roles
  416. # Test all level user roles fail
  417. with pytest.raises(KeycloakGetError) as err:
  418. admin.get_all_roles_of_user(user_id="non-existent-id")
  419. err.match('404: b\'{"error":"User not found"')
  420. admin.delete_user(user_id)
  421. admin.delete_client(client_id)
  422. def test_users_pagination(admin: KeycloakAdmin, realm: str) -> None:
  423. """
  424. Test user pagination.
  425. :param admin: Keycloak Admin client
  426. :type admin: KeycloakAdmin
  427. :param realm: Keycloak realm
  428. :type realm: str
  429. """
  430. admin.change_current_realm(realm)
  431. for ind in range(admin.PAGE_SIZE + 50):
  432. username = f"user_{ind}"
  433. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  434. users = admin.get_users()
  435. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  436. users = admin.get_users(query={"first": 100})
  437. assert len(users) == 50, len(users)
  438. users = admin.get_users(query={"max": 20})
  439. assert len(users) == 20, len(users)
  440. def test_user_groups_pagination(admin: KeycloakAdmin, realm: str) -> None:
  441. """
  442. Test user groups pagination.
  443. :param admin: Keycloak Admin client
  444. :type admin: KeycloakAdmin
  445. :param realm: Keycloak realm
  446. :type realm: str
  447. """
  448. admin.change_current_realm(realm)
  449. user_id = admin.create_user(
  450. payload={"username": "username_1", "email": "username_1@test.test"},
  451. )
  452. for ind in range(admin.PAGE_SIZE + 50):
  453. group_name = f"group_{ind}"
  454. group_id = admin.create_group(payload={"name": group_name})
  455. admin.group_user_add(user_id=user_id, group_id=group_id)
  456. groups = admin.get_user_groups(user_id=user_id)
  457. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  458. groups = admin.get_user_groups(user_id=user_id, query={"first": 100, "max": -1, "search": ""})
  459. assert len(groups) == 50, len(groups)
  460. groups = admin.get_user_groups(user_id=user_id, query={"max": 20, "first": -1, "search": ""})
  461. assert len(groups) == 20, len(groups)
  462. def test_idps(admin: KeycloakAdmin, realm: str) -> None:
  463. """
  464. Test IDPs.
  465. :param admin: Keycloak Admin client
  466. :type admin: KeycloakAdmin
  467. :param realm: Keycloak realm
  468. :type realm: str
  469. """
  470. admin.change_current_realm(realm)
  471. # Create IDP
  472. res = admin.create_idp(
  473. payload={
  474. "providerId": "github",
  475. "alias": "github",
  476. "config": {"clientId": "test", "clientSecret": "test"},
  477. },
  478. )
  479. assert res == b"", res
  480. # Test create idp fail
  481. with pytest.raises(KeycloakPostError) as err:
  482. admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  483. assert err.match("Invalid identity provider id"), err
  484. # Test listing
  485. idps = admin.get_idps()
  486. assert len(idps) == 1
  487. assert idps[0]["alias"] == "github"
  488. # Test get idp
  489. idp = admin.get_idp("github")
  490. assert idp["alias"] == "github"
  491. assert idp.get("config")
  492. assert idp["config"]["clientId"] == "test"
  493. assert idp["config"]["clientSecret"] == "**********"
  494. # Test get idp fail
  495. with pytest.raises(KeycloakGetError) as err:
  496. admin.get_idp("does-not-exist")
  497. assert err.match(HTTP_404_REGEX)
  498. # Test IdP update
  499. res = admin.update_idp(idp_alias="github", payload=idps[0])
  500. assert res == {}, res
  501. # Test adding a mapper
  502. res = admin.add_mapper_to_idp(
  503. idp_alias="github",
  504. payload={
  505. "identityProviderAlias": "github",
  506. "identityProviderMapper": "github-user-attribute-mapper",
  507. "name": "test",
  508. },
  509. )
  510. assert res == b"", res
  511. # Test mapper fail
  512. with pytest.raises(KeycloakPostError) as err:
  513. admin.add_mapper_to_idp(idp_alias="does-no-texist", payload={})
  514. assert err.match(HTTP_404_REGEX)
  515. # Test IdP mappers listing
  516. idp_mappers = admin.get_idp_mappers(idp_alias="github")
  517. assert len(idp_mappers) == 1
  518. # Test IdP mapper update
  519. res = admin.update_mapper_in_idp(
  520. idp_alias="github",
  521. mapper_id=idp_mappers[0]["id"],
  522. # For an obscure reason, keycloak expect all fields
  523. payload={
  524. "id": idp_mappers[0]["id"],
  525. "identityProviderAlias": "github-alias",
  526. "identityProviderMapper": "github-user-attribute-mapper",
  527. "name": "test",
  528. "config": idp_mappers[0]["config"],
  529. },
  530. )
  531. assert res == {}, res
  532. # Test delete
  533. res = admin.delete_idp(idp_alias="github")
  534. assert res == {}, res
  535. # Test delete fail
  536. with pytest.raises(KeycloakDeleteError) as err:
  537. admin.delete_idp(idp_alias="does-not-exist")
  538. assert err.match(HTTP_404_REGEX)
  539. def test_user_credentials(admin: KeycloakAdmin, user: str) -> None:
  540. """
  541. Test user credentials.
  542. :param admin: Keycloak Admin client
  543. :type admin: KeycloakAdmin
  544. :param user: Keycloak user
  545. :type user: str
  546. """
  547. res = admin.set_user_password(user_id=user, password="booya", temporary=True) # noqa: S106
  548. assert res == {}, res
  549. # Test user password set fail
  550. with pytest.raises(KeycloakPutError) as err:
  551. admin.set_user_password(user_id="does-not-exist", password="")
  552. assert err.match(USER_NOT_FOUND_REGEX)
  553. credentials = admin.get_credentials(user_id=user)
  554. assert len(credentials) == 1
  555. assert credentials[0]["type"] == "password", credentials
  556. # Test get credentials fail
  557. with pytest.raises(KeycloakGetError) as err:
  558. admin.get_credentials(user_id="does-not-exist")
  559. assert err.match(USER_NOT_FOUND_REGEX)
  560. res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"])
  561. assert res == {}, res
  562. # Test delete fail
  563. with pytest.raises(KeycloakDeleteError) as err:
  564. admin.delete_credential(user_id=user, credential_id="does-not-exist")
  565. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  566. def test_social_logins(admin: KeycloakAdmin, user: str) -> None:
  567. """
  568. Test social logins.
  569. :param admin: Keycloak Admin client
  570. :type admin: KeycloakAdmin
  571. :param user: Keycloak user
  572. :type user: str
  573. """
  574. res = admin.add_user_social_login(
  575. user_id=user,
  576. provider_id="gitlab",
  577. provider_userid="test",
  578. provider_username="test",
  579. )
  580. assert res == {}, res
  581. admin.add_user_social_login(
  582. user_id=user,
  583. provider_id="github",
  584. provider_userid="test",
  585. provider_username="test",
  586. )
  587. assert res == {}, res
  588. # Test add social login fail
  589. with pytest.raises(KeycloakPostError) as err:
  590. admin.add_user_social_login(
  591. user_id="does-not-exist",
  592. provider_id="does-not-exist",
  593. provider_userid="test",
  594. provider_username="test",
  595. )
  596. assert err.match(USER_NOT_FOUND_REGEX)
  597. res = admin.get_user_social_logins(user_id=user)
  598. assert res == [], res
  599. # Test get social logins fail
  600. with pytest.raises(KeycloakGetError) as err:
  601. admin.get_user_social_logins(user_id="does-not-exist")
  602. assert err.match(USER_NOT_FOUND_REGEX)
  603. res = admin.delete_user_social_login(user_id=user, provider_id="gitlab")
  604. assert res == {}, res
  605. res = admin.delete_user_social_login(user_id=user, provider_id="github")
  606. assert res == {}, res
  607. with pytest.raises(KeycloakDeleteError) as err:
  608. admin.delete_user_social_login(user_id=user, provider_id="instagram")
  609. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  610. def test_server_info(admin: KeycloakAdmin) -> None:
  611. """
  612. Test server info.
  613. :param admin: Keycloak Admin client
  614. :type admin: KeycloakAdmin
  615. """
  616. info = admin.get_server_info()
  617. assert set(info.keys()).issubset(
  618. {
  619. "systemInfo",
  620. "memoryInfo",
  621. "profileInfo",
  622. "features",
  623. "themes",
  624. "socialProviders",
  625. "identityProviders",
  626. "providers",
  627. "protocolMapperTypes",
  628. "builtinProtocolMappers",
  629. "clientInstallations",
  630. "componentTypes",
  631. "passwordPolicies",
  632. "enums",
  633. "cryptoInfo",
  634. },
  635. ), info.keys()
  636. def test_groups(admin: KeycloakAdmin, user: str) -> None:
  637. """
  638. Test groups.
  639. :param admin: Keycloak Admin client
  640. :type admin: KeycloakAdmin
  641. :param user: Keycloak user
  642. :type user: str
  643. """
  644. # Test get groups
  645. groups = admin.get_groups()
  646. assert len(groups) == 0
  647. # Test create group
  648. group_id = admin.create_group(payload={"name": "main-group"})
  649. assert group_id is not None, group_id
  650. # Test group count
  651. count = admin.groups_count()
  652. assert count.get("count") == 1, count
  653. # Test group count with query
  654. count = admin.groups_count(query={"search": "notpresent"})
  655. assert count.get("count") == 0
  656. # Test create subgroups
  657. subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  658. subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id)
  659. # Test create group fail
  660. with pytest.raises(KeycloakPostError) as err:
  661. admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  662. assert err.match("409"), err
  663. # Test skip exists OK
  664. subgroup_id_1_eq = admin.create_group(
  665. payload={"name": "subgroup-1"},
  666. parent=group_id,
  667. skip_exists=True,
  668. )
  669. assert subgroup_id_1_eq is None
  670. # Test get groups again
  671. groups = admin.get_groups()
  672. assert len(groups) == 1, groups
  673. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  674. assert groups[0]["id"] == group_id
  675. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  676. # Test get groups query
  677. groups = admin.get_groups(query={"max": 10})
  678. assert len(groups) == 1, groups
  679. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  680. assert groups[0]["id"] == group_id
  681. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  682. # Test get group
  683. res = admin.get_group(group_id=subgroup_id_1)
  684. assert res["id"] == subgroup_id_1, res
  685. assert res["name"] == "subgroup-1"
  686. assert res["path"] == "/main-group/subgroup-1"
  687. # Test get group fail
  688. with pytest.raises(KeycloakGetError) as err:
  689. admin.get_group(group_id="does-not-exist")
  690. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  691. # Create 1 more subgroup
  692. subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2)
  693. main_group = admin.get_group(group_id=group_id)
  694. # Test nested searches
  695. subgroup_2 = admin.get_group(group_id=subgroup_id_2)
  696. res = admin.get_subgroups(group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1")
  697. assert res is not None, res
  698. assert res["id"] == subsubgroup_id_1
  699. # Test nested search from main group
  700. res = admin.get_subgroups(
  701. group=admin.get_group(group_id=group_id, full_hierarchy=True),
  702. path="/main-group/subgroup-2/subsubgroup-1",
  703. )
  704. assert res["id"] == subsubgroup_id_1
  705. # Test nested search from all groups
  706. res = admin.get_groups(full_hierarchy=True)
  707. assert len(res) == 1
  708. assert len(res[0]["subGroups"]) == 2
  709. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1)["subGroups"]) == 0
  710. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2)["subGroups"]) == 1
  711. # Test that query params are not allowed for full hierarchy
  712. with pytest.raises(ValueError) as err:
  713. admin.get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
  714. # Test that query params are passed
  715. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  716. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  717. ) >= Version("23"):
  718. res = admin.get_group_children(group_id=group_id, query={"max": 1})
  719. assert len(res) == 1
  720. assert err.match("Cannot use both query and full_hierarchy parameters")
  721. main_group_id_2 = admin.create_group(payload={"name": "main-group-2"})
  722. assert len(admin.get_groups(full_hierarchy=True)) == 2
  723. # Test empty search
  724. res = admin.get_subgroups(group=main_group, path="/none")
  725. assert res is None, res
  726. # Test get group by path
  727. res = admin.get_group_by_path(path="/main-group/subgroup-1")
  728. assert res is not None, res
  729. assert res["id"] == subgroup_id_1, res
  730. res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  731. assert res["error"] == "Group path does not exist"
  732. res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  733. assert res is not None, res
  734. assert res["id"] == subsubgroup_id_1
  735. res = admin.get_group_by_path(path="/main-group")
  736. assert res is not None, res
  737. assert res["id"] == group_id, res
  738. # Test group members
  739. res = admin.get_group_members(group_id=subgroup_id_2)
  740. assert len(res) == 0, res
  741. # Test fail group members
  742. with pytest.raises(KeycloakGetError) as err:
  743. admin.get_group_members(group_id="does-not-exist")
  744. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  745. res = admin.group_user_add(user_id=user, group_id=subgroup_id_2)
  746. assert res == {}, res
  747. res = admin.get_group_members(group_id=subgroup_id_2)
  748. assert len(res) == 1, res
  749. assert res[0]["id"] == user
  750. # Test get group members query
  751. res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10})
  752. assert len(res) == 1, res
  753. assert res[0]["id"] == user
  754. with pytest.raises(KeycloakDeleteError) as err:
  755. admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  756. assert err.match(USER_NOT_FOUND_REGEX), err
  757. res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2)
  758. assert res == {}, res
  759. # Test set permissions
  760. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True)
  761. assert res["enabled"], res
  762. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False)
  763. assert not res["enabled"], res
  764. with pytest.raises(KeycloakPutError) as err:
  765. admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  766. assert err.match(UNKOWN_ERROR_REGEX), err
  767. # Test update group
  768. res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  769. assert res == {}, res
  770. assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2"
  771. # test update fail
  772. with pytest.raises(KeycloakPutError) as err:
  773. admin.update_group(group_id="does-not-exist", payload={})
  774. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  775. # Test delete
  776. res = admin.delete_group(group_id=group_id)
  777. assert res == {}, res
  778. res = admin.delete_group(group_id=main_group_id_2)
  779. assert res == {}, res
  780. assert len(admin.get_groups()) == 0
  781. # Test delete fail
  782. with pytest.raises(KeycloakDeleteError) as err:
  783. admin.delete_group(group_id="does-not-exist")
  784. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  785. def test_clients(admin: KeycloakAdmin, realm: str) -> None:
  786. """
  787. Test clients.
  788. :param admin: Keycloak Admin client
  789. :type admin: KeycloakAdmin
  790. :param realm: Keycloak realm
  791. :type realm: str
  792. """
  793. admin.change_current_realm(realm)
  794. # Test get clients
  795. clients = admin.get_clients()
  796. assert len(clients) == 6, clients
  797. assert {x["name"] for x in clients} == {
  798. "${client_admin-cli}",
  799. "${client_security-admin-console}",
  800. "${client_account-console}",
  801. "${client_broker}",
  802. "${client_account}",
  803. "${client_realm-management}",
  804. }, clients
  805. # Test create client
  806. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  807. assert client_id, client_id
  808. with pytest.raises(KeycloakPostError) as err:
  809. admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  810. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  811. client_id_2 = admin.create_client(
  812. payload={"name": "test-client", "clientId": "test-client"},
  813. skip_exists=True,
  814. )
  815. assert client_id == client_id_2, client_id_2
  816. # Test get client
  817. res = admin.get_client(client_id=client_id)
  818. assert res["clientId"] == "test-client", res
  819. assert res["name"] == "test-client", res
  820. assert res["id"] == client_id, res
  821. with pytest.raises(KeycloakGetError) as err:
  822. admin.get_client(client_id="does-not-exist")
  823. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  824. assert len(admin.get_clients()) == 7
  825. # Test get client id
  826. assert admin.get_client_id(client_id="test-client") == client_id
  827. assert admin.get_client_id(client_id="does-not-exist") is None
  828. # Test update client
  829. res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"})
  830. assert res == {}, res
  831. with pytest.raises(KeycloakPutError) as err:
  832. admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"})
  833. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  834. # Test client mappers
  835. res = admin.get_mappers_from_client(client_id=client_id)
  836. assert len(res) == 0
  837. with pytest.raises(KeycloakPostError) as err:
  838. admin.add_mapper_to_client(client_id="does-not-exist", payload={})
  839. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  840. res = admin.add_mapper_to_client(
  841. client_id=client_id,
  842. payload={
  843. "name": "test-mapper",
  844. "protocol": "openid-connect",
  845. "protocolMapper": "oidc-usermodel-attribute-mapper",
  846. },
  847. )
  848. assert res == b""
  849. assert len(admin.get_mappers_from_client(client_id=client_id)) == 1
  850. mapper = admin.get_mappers_from_client(client_id=client_id)[0]
  851. with pytest.raises(KeycloakPutError) as err:
  852. admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload={})
  853. assert err.match('404: b\'{"error":"Model not found".*}\'')
  854. mapper["config"]["user.attribute"] = "test"
  855. res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper)
  856. assert res == {}
  857. res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  858. assert res == {}
  859. with pytest.raises(KeycloakDeleteError) as err:
  860. admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  861. assert err.match('404: b\'{"error":"Model not found".*}\'')
  862. # Test client sessions
  863. with pytest.raises(KeycloakGetError) as err:
  864. admin.get_client_all_sessions(client_id="does-not-exist")
  865. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  866. assert admin.get_client_all_sessions(client_id=client_id) == []
  867. assert admin.get_client_sessions_stats() == []
  868. # Test authz
  869. auth_client_id = admin.create_client(
  870. payload={
  871. "name": "authz-client",
  872. "clientId": "authz-client",
  873. "authorizationServicesEnabled": True,
  874. "serviceAccountsEnabled": True,
  875. },
  876. )
  877. res = admin.get_client_authz_settings(client_id=auth_client_id)
  878. assert res["allowRemoteResourceManagement"]
  879. assert res["decisionStrategy"] == "UNANIMOUS"
  880. assert len(res["policies"]) >= 0
  881. with pytest.raises(KeycloakGetError) as err:
  882. admin.get_client_authz_settings(client_id=client_id)
  883. assert err.match(HTTP_404_REGEX)
  884. # Authz resources
  885. res = admin.get_client_authz_resources(client_id=auth_client_id)
  886. assert len(res) == 1
  887. assert res[0]["name"] == "Default Resource"
  888. with pytest.raises(KeycloakGetError) as err:
  889. admin.get_client_authz_resources(client_id=client_id)
  890. assert err.match(HTTP_404_REGEX)
  891. res = admin.create_client_authz_resource(
  892. client_id=auth_client_id,
  893. payload={"name": "test-resource"},
  894. )
  895. assert res["name"] == "test-resource", res
  896. test_resource_id = res["_id"]
  897. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=test_resource_id)
  898. assert res["_id"] == test_resource_id, res
  899. assert res["name"] == "test-resource", res
  900. with pytest.raises(KeycloakPostError) as err:
  901. admin.create_client_authz_resource(
  902. client_id=auth_client_id,
  903. payload={"name": "test-resource"},
  904. )
  905. assert err.match('409: b\'{"error":"invalid_request"')
  906. assert admin.create_client_authz_resource(
  907. client_id=auth_client_id,
  908. payload={"name": "test-resource"},
  909. skip_exists=True,
  910. ) == {"msg": "Already exists"}
  911. res = admin.get_client_authz_resources(client_id=auth_client_id)
  912. assert len(res) == 2
  913. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  914. res = admin.create_client_authz_resource(
  915. client_id=auth_client_id,
  916. payload={"name": "temp-resource"},
  917. )
  918. assert res["name"] == "temp-resource", res
  919. temp_resource_id: str = res["_id"]
  920. # Test update authz resources
  921. admin.update_client_authz_resource(
  922. client_id=auth_client_id,
  923. resource_id=temp_resource_id,
  924. payload={"name": "temp-updated-resource"},
  925. )
  926. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  927. assert res["name"] == "temp-updated-resource", res
  928. with pytest.raises(KeycloakPutError) as err:
  929. admin.update_client_authz_resource(
  930. client_id=auth_client_id,
  931. resource_id="invalid_resource_id",
  932. payload={"name": "temp-updated-resource"},
  933. )
  934. assert err.match("404: b''"), err
  935. admin.delete_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  936. with pytest.raises(KeycloakGetError) as err:
  937. admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  938. assert err.match("404: b''")
  939. # Authz policies
  940. res = admin.get_client_authz_policies(client_id=auth_client_id)
  941. assert len(res) == 1, res
  942. assert res[0]["name"] == "Default Policy"
  943. with pytest.raises(KeycloakGetError) as err:
  944. admin.get_client_authz_policies(client_id="does-not-exist")
  945. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  946. role_id = admin.get_realm_role(role_name="offline_access")["id"]
  947. res = admin.create_client_authz_role_based_policy(
  948. client_id=auth_client_id,
  949. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  950. )
  951. assert res["name"] == "test-authz-rb-policy", res
  952. role_based_policy_id = res["id"]
  953. role_based_policy_name = res["name"]
  954. with pytest.raises(KeycloakPostError) as err:
  955. admin.create_client_authz_role_based_policy(
  956. client_id=auth_client_id,
  957. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  958. )
  959. assert err.match('409: b\'{"error":"Policy with name')
  960. assert admin.create_client_authz_role_based_policy(
  961. client_id=auth_client_id,
  962. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  963. skip_exists=True,
  964. ) == {"msg": "Already exists"}
  965. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2
  966. res = admin.create_client_authz_role_based_policy(
  967. client_id=auth_client_id,
  968. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  969. )
  970. res2 = admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  971. assert res["id"] == res2["id"]
  972. admin.delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  973. with pytest.raises(KeycloakGetError) as err:
  974. admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  975. assert err.match("404: b''")
  976. res = admin.create_client_authz_policy(
  977. client_id=auth_client_id,
  978. payload={
  979. "name": "test-authz-policy",
  980. "type": "time",
  981. "config": {"hourEnd": "18", "hour": "9"},
  982. },
  983. )
  984. assert res["name"] == "test-authz-policy", res
  985. with pytest.raises(KeycloakPostError) as err:
  986. admin.create_client_authz_policy(
  987. client_id=auth_client_id,
  988. payload={
  989. "name": "test-authz-policy",
  990. "type": "time",
  991. "config": {"hourEnd": "18", "hour": "9"},
  992. },
  993. )
  994. assert err.match('409: b\'{"error":"Policy with name')
  995. assert admin.create_client_authz_policy(
  996. client_id=auth_client_id,
  997. payload={
  998. "name": "test-authz-policy",
  999. "type": "time",
  1000. "config": {"hourEnd": "18", "hour": "9"},
  1001. },
  1002. skip_exists=True,
  1003. ) == {"msg": "Already exists"}
  1004. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 3
  1005. # Test authz permissions
  1006. res = admin.get_client_authz_permissions(client_id=auth_client_id)
  1007. assert len(res) == 1, res
  1008. assert res[0]["name"] == "Default Permission"
  1009. with pytest.raises(KeycloakGetError) as err:
  1010. admin.get_client_authz_permissions(client_id="does-not-exist")
  1011. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1012. res = admin.create_client_authz_resource_based_permission(
  1013. client_id=auth_client_id,
  1014. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  1015. )
  1016. assert res, res
  1017. assert res["name"] == "test-permission-rb"
  1018. assert res["resources"] == [test_resource_id]
  1019. resource_based_permission_id = res["id"]
  1020. resource_based_permission_name = res["name"]
  1021. with pytest.raises(KeycloakPostError) as err:
  1022. admin.create_client_authz_resource_based_permission(
  1023. client_id=auth_client_id,
  1024. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  1025. )
  1026. assert err.match('409: b\'{"error":"Policy with name')
  1027. assert admin.create_client_authz_resource_based_permission(
  1028. client_id=auth_client_id,
  1029. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  1030. skip_exists=True,
  1031. ) == {"msg": "Already exists"}
  1032. assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2
  1033. # Test associating client policy with resource based permission
  1034. res = admin.update_client_authz_resource_permission(
  1035. client_id=auth_client_id,
  1036. resource_id=resource_based_permission_id,
  1037. payload={
  1038. "id": resource_based_permission_id,
  1039. "name": resource_based_permission_name,
  1040. "type": "resource",
  1041. "logic": "POSITIVE",
  1042. "decisionStrategy": "UNANIMOUS",
  1043. "resources": [test_resource_id],
  1044. "scopes": [],
  1045. "policies": [role_based_policy_id],
  1046. },
  1047. )
  1048. # Test getting associated policies for a permission
  1049. associated_policies = admin.get_client_authz_permission_associated_policies(
  1050. client_id=auth_client_id,
  1051. policy_id=resource_based_permission_id,
  1052. )
  1053. assert len(associated_policies) == 1
  1054. assert associated_policies[0]["name"].startswith(role_based_policy_name)
  1055. # Test authz scopes
  1056. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  1057. assert len(res) == 0, res
  1058. with pytest.raises(KeycloakGetError) as err:
  1059. admin.get_client_authz_scopes(client_id=client_id)
  1060. assert err.match(HTTP_404_REGEX)
  1061. res = admin.create_client_authz_scopes(
  1062. client_id=auth_client_id,
  1063. payload={"name": "test-authz-scope"},
  1064. )
  1065. assert res["name"] == "test-authz-scope", res
  1066. with pytest.raises(KeycloakPostError) as err:
  1067. admin.create_client_authz_scopes(
  1068. client_id="invalid_client_id",
  1069. payload={"name": "test-authz-scope"},
  1070. )
  1071. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1072. assert admin.create_client_authz_scopes(
  1073. client_id=auth_client_id,
  1074. payload={"name": "test-authz-scope"},
  1075. )
  1076. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  1077. assert len(res) == 1
  1078. assert {x["name"] for x in res} == {"test-authz-scope"}
  1079. # Test service account user
  1080. res = admin.get_client_service_account_user(client_id=auth_client_id)
  1081. assert res["username"] == "service-account-authz-client", res
  1082. with pytest.raises(KeycloakGetError) as err:
  1083. admin.get_client_service_account_user(client_id=client_id)
  1084. assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
  1085. UNKOWN_ERROR_REGEX,
  1086. )
  1087. # Test delete client
  1088. res = admin.delete_client(client_id=auth_client_id)
  1089. assert res == {}, res
  1090. with pytest.raises(KeycloakDeleteError) as err:
  1091. admin.delete_client(client_id=auth_client_id)
  1092. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1093. # Test client credentials
  1094. admin.create_client(
  1095. payload={
  1096. "name": "test-confidential",
  1097. "enabled": True,
  1098. "protocol": "openid-connect",
  1099. "publicClient": False,
  1100. "redirectUris": ["http://localhost/*"],
  1101. "webOrigins": ["+"],
  1102. "clientId": "test-confidential",
  1103. "secret": "test-secret",
  1104. "clientAuthenticatorType": "client-secret",
  1105. },
  1106. )
  1107. with pytest.raises(KeycloakGetError) as err:
  1108. admin.get_client_secrets(client_id="does-not-exist")
  1109. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1110. secrets = admin.get_client_secrets(
  1111. client_id=admin.get_client_id(client_id="test-confidential"),
  1112. )
  1113. assert secrets == {"type": "secret", "value": "test-secret"}
  1114. with pytest.raises(KeycloakPostError) as err:
  1115. admin.generate_client_secrets(client_id="does-not-exist")
  1116. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1117. res = admin.generate_client_secrets(
  1118. client_id=admin.get_client_id(client_id="test-confidential"),
  1119. )
  1120. assert res
  1121. assert (
  1122. admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
  1123. == res
  1124. )
  1125. def test_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  1126. """
  1127. Test realm roles.
  1128. :param admin: Keycloak Admin client
  1129. :type admin: KeycloakAdmin
  1130. :param realm: Keycloak realm
  1131. :type realm: str
  1132. """
  1133. admin.change_current_realm(realm)
  1134. # Test get realm roles
  1135. roles = admin.get_realm_roles()
  1136. assert len(roles) == 3, roles
  1137. role_names = [x["name"] for x in roles]
  1138. assert "uma_authorization" in role_names, role_names
  1139. assert "offline_access" in role_names, role_names
  1140. # Test get realm roles with search text
  1141. searched_roles = admin.get_realm_roles(search_text="uma_a")
  1142. searched_role_names = [x["name"] for x in searched_roles]
  1143. assert "uma_authorization" in searched_role_names, searched_role_names
  1144. assert "offline_access" not in searched_role_names, searched_role_names
  1145. # Test empty members
  1146. with pytest.raises(KeycloakGetError) as err:
  1147. admin.get_realm_role_members(role_name="does-not-exist")
  1148. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1149. members = admin.get_realm_role_members(role_name="offline_access")
  1150. assert members == [], members
  1151. # Test create realm role
  1152. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1153. assert role_id, role_id
  1154. with pytest.raises(KeycloakPostError) as err:
  1155. admin.create_realm_role(payload={"name": "test-realm-role"})
  1156. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  1157. role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1158. assert role_id == role_id_2
  1159. # Test get realm role by its id
  1160. role_id = admin.get_realm_role(role_name="test-realm-role")["id"]
  1161. res = admin.get_realm_role_by_id(role_id)
  1162. assert res["name"] == "test-realm-role"
  1163. # Test update realm role
  1164. res = admin.update_realm_role(
  1165. role_name="test-realm-role",
  1166. payload={"name": "test-realm-role-update"},
  1167. )
  1168. assert res == {}, res
  1169. with pytest.raises(KeycloakPutError) as err:
  1170. admin.update_realm_role(
  1171. role_name="test-realm-role",
  1172. payload={"name": "test-realm-role-update"},
  1173. )
  1174. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1175. # Test realm role user assignment
  1176. user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"})
  1177. with pytest.raises(KeycloakPostError) as err:
  1178. admin.assign_realm_roles(user_id=user_id, roles=["bad"])
  1179. assert err.match(UNKOWN_ERROR_REGEX), err
  1180. res = admin.assign_realm_roles(
  1181. user_id=user_id,
  1182. roles=[
  1183. admin.get_realm_role(role_name="offline_access"),
  1184. admin.get_realm_role(role_name="test-realm-role-update"),
  1185. ],
  1186. )
  1187. assert res == {}, res
  1188. assert admin.get_user(user_id=user_id)["username"] in [
  1189. x["username"] for x in admin.get_realm_role_members(role_name="offline_access")
  1190. ]
  1191. assert admin.get_user(user_id=user_id)["username"] in [
  1192. x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update")
  1193. ]
  1194. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1195. assert len(roles) == 3
  1196. assert "offline_access" in [x["name"] for x in roles]
  1197. assert "test-realm-role-update" in [x["name"] for x in roles]
  1198. with pytest.raises(KeycloakDeleteError) as err:
  1199. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  1200. assert err.match(UNKOWN_ERROR_REGEX), err
  1201. res = admin.delete_realm_roles_of_user(
  1202. user_id=user_id,
  1203. roles=[admin.get_realm_role(role_name="offline_access")],
  1204. )
  1205. assert res == {}, res
  1206. assert admin.get_realm_role_members(role_name="offline_access") == []
  1207. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1208. assert len(roles) == 2
  1209. assert "offline_access" not in [x["name"] for x in roles]
  1210. assert "test-realm-role-update" in [x["name"] for x in roles]
  1211. roles = admin.get_available_realm_roles_of_user(user_id=user_id)
  1212. assert len(roles) == 2
  1213. assert "offline_access" in [x["name"] for x in roles]
  1214. assert "uma_authorization" in [x["name"] for x in roles]
  1215. # Test realm role group assignment
  1216. group_id = admin.create_group(payload={"name": "test-group"})
  1217. with pytest.raises(KeycloakPostError) as err:
  1218. admin.assign_group_realm_roles(group_id=group_id, roles=["bad"])
  1219. assert err.match(UNKOWN_ERROR_REGEX), err
  1220. res = admin.assign_group_realm_roles(
  1221. group_id=group_id,
  1222. roles=[
  1223. admin.get_realm_role(role_name="offline_access"),
  1224. admin.get_realm_role(role_name="test-realm-role-update"),
  1225. ],
  1226. )
  1227. assert res == {}, res
  1228. roles = admin.get_group_realm_roles(group_id=group_id)
  1229. assert len(roles) == 2
  1230. assert "offline_access" in [x["name"] for x in roles]
  1231. assert "test-realm-role-update" in [x["name"] for x in roles]
  1232. with pytest.raises(KeycloakDeleteError) as err:
  1233. admin.delete_group_realm_roles(group_id=group_id, roles=["bad"])
  1234. assert err.match(UNKOWN_ERROR_REGEX)
  1235. res = admin.delete_group_realm_roles(
  1236. group_id=group_id,
  1237. roles=[admin.get_realm_role(role_name="offline_access")],
  1238. )
  1239. assert res == {}, res
  1240. roles = admin.get_group_realm_roles(group_id=group_id)
  1241. assert len(roles) == 1
  1242. assert "test-realm-role-update" in [x["name"] for x in roles]
  1243. # Test composite realm roles
  1244. composite_role = admin.create_realm_role(payload={"name": "test-composite-role"})
  1245. with pytest.raises(KeycloakPostError) as err:
  1246. admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1247. assert err.match(UNKOWN_ERROR_REGEX), err
  1248. res = admin.add_composite_realm_roles_to_role(
  1249. role_name=composite_role,
  1250. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  1251. )
  1252. assert res == {}, res
  1253. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1254. assert len(res) == 1
  1255. assert "test-realm-role-update" in res[0]["name"]
  1256. with pytest.raises(KeycloakGetError) as err:
  1257. admin.get_composite_realm_roles_of_role(role_name="bad")
  1258. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1259. res = admin.get_composite_realm_roles_of_user(user_id=user_id)
  1260. assert len(res) == 4
  1261. assert "offline_access" in {x["name"] for x in res}
  1262. assert "test-realm-role-update" in {x["name"] for x in res}
  1263. assert "uma_authorization" in {x["name"] for x in res}
  1264. with pytest.raises(KeycloakGetError) as err:
  1265. admin.get_composite_realm_roles_of_user(user_id="bad")
  1266. assert err.match(USER_NOT_FOUND_REGEX), err
  1267. with pytest.raises(KeycloakDeleteError) as err:
  1268. admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1269. assert err.match(UNKOWN_ERROR_REGEX), err
  1270. res = admin.remove_composite_realm_roles_to_role(
  1271. role_name=composite_role,
  1272. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  1273. )
  1274. assert res == {}, res
  1275. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1276. assert len(res) == 0
  1277. # Test realm role group list
  1278. res = admin.get_realm_role_groups(role_name="test-realm-role-update")
  1279. assert len(res) == 1
  1280. assert res[0]["id"] == group_id
  1281. with pytest.raises(KeycloakGetError) as err:
  1282. admin.get_realm_role_groups(role_name="non-existent-role")
  1283. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1284. # Test with query params
  1285. res = admin.get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
  1286. assert len(res) == 1
  1287. # Test delete realm role
  1288. res = admin.delete_realm_role(role_name=composite_role)
  1289. assert res == {}, res
  1290. with pytest.raises(KeycloakDeleteError) as err:
  1291. admin.delete_realm_role(role_name=composite_role)
  1292. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1293. @pytest.mark.parametrize(
  1294. ("testcase", "arg_brief_repr", "includes_attributes"),
  1295. [
  1296. ("brief True", {"brief_representation": True}, False),
  1297. ("brief False", {"brief_representation": False}, True),
  1298. ("default", {}, False),
  1299. ],
  1300. )
  1301. def test_role_attributes(
  1302. admin: KeycloakAdmin,
  1303. realm: str,
  1304. client: str,
  1305. arg_brief_repr: dict,
  1306. includes_attributes: bool,
  1307. testcase: str,
  1308. ) -> None:
  1309. """
  1310. Test getting role attributes for bulk calls.
  1311. :param admin: Keycloak admin
  1312. :type admin: KeycloakAdmin
  1313. :param realm: Keycloak realm
  1314. :type realm: str
  1315. :param client: Keycloak client
  1316. :type client: str
  1317. :param arg_brief_repr: Brief representation
  1318. :type arg_brief_repr: dict
  1319. :param includes_attributes: Indicator whether to include attributes
  1320. :type includes_attributes: bool
  1321. :param testcase: Test case
  1322. :type testcase: str
  1323. """
  1324. # setup
  1325. attribute_role = "test-realm-role-w-attr"
  1326. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  1327. role_id = admin.create_realm_role(
  1328. payload={"name": attribute_role, "attributes": test_attrs},
  1329. skip_exists=True,
  1330. )
  1331. assert role_id, role_id
  1332. cli_role_id = admin.create_client_role(
  1333. client,
  1334. payload={"name": attribute_role, "attributes": test_attrs},
  1335. skip_exists=True,
  1336. )
  1337. assert cli_role_id, cli_role_id
  1338. if not includes_attributes:
  1339. test_attrs = None
  1340. # tests
  1341. roles = admin.get_realm_roles(**arg_brief_repr)
  1342. roles_filtered = [role for role in roles if role["name"] == role_id]
  1343. assert roles_filtered, roles_filtered
  1344. role = roles_filtered[0]
  1345. assert role.get("attributes") == test_attrs, testcase
  1346. roles = admin.get_client_roles(client, **arg_brief_repr)
  1347. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  1348. assert roles_filtered, roles_filtered
  1349. role = roles_filtered[0]
  1350. assert role.get("attributes") == test_attrs, testcase
  1351. # cleanup
  1352. res = admin.delete_realm_role(role_name=attribute_role)
  1353. assert res == {}, res
  1354. res = admin.delete_client_role(client, role_name=attribute_role)
  1355. assert res == {}, res
  1356. def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  1357. """
  1358. Test client realm roles.
  1359. :param admin: Keycloak admin
  1360. :type admin: KeycloakAdmin
  1361. :param realm: Keycloak realm
  1362. :type realm: str
  1363. """
  1364. admin.change_current_realm(realm)
  1365. # Test get realm roles
  1366. roles = admin.get_realm_roles()
  1367. assert len(roles) == 3, roles
  1368. role_names = [x["name"] for x in roles]
  1369. assert "uma_authorization" in role_names, role_names
  1370. assert "offline_access" in role_names, role_names
  1371. # create realm role for test
  1372. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1373. assert role_id, role_id
  1374. # Test realm role client assignment
  1375. client_id = admin.create_client(
  1376. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  1377. )
  1378. with pytest.raises(KeycloakPostError) as err:
  1379. admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  1380. assert err.match(UNKOWN_ERROR_REGEX), err
  1381. res = admin.assign_realm_roles_to_client_scope(
  1382. client_id=client_id,
  1383. roles=[
  1384. admin.get_realm_role(role_name="offline_access"),
  1385. admin.get_realm_role(role_name="test-realm-role"),
  1386. ],
  1387. )
  1388. assert res == {}, res
  1389. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1390. assert len(roles) == 2
  1391. client_role_names = [x["name"] for x in roles]
  1392. assert "offline_access" in client_role_names, client_role_names
  1393. assert "test-realm-role" in client_role_names, client_role_names
  1394. assert "uma_authorization" not in client_role_names, client_role_names
  1395. # Test remove realm role of client
  1396. with pytest.raises(KeycloakDeleteError) as err:
  1397. admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  1398. assert err.match(UNKOWN_ERROR_REGEX), err
  1399. res = admin.delete_realm_roles_of_client_scope(
  1400. client_id=client_id,
  1401. roles=[admin.get_realm_role(role_name="offline_access")],
  1402. )
  1403. assert res == {}, res
  1404. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1405. assert len(roles) == 1
  1406. assert "test-realm-role" in [x["name"] for x in roles]
  1407. res = admin.delete_realm_roles_of_client_scope(
  1408. client_id=client_id,
  1409. roles=[admin.get_realm_role(role_name="test-realm-role")],
  1410. )
  1411. assert res == {}, res
  1412. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1413. assert len(roles) == 0
  1414. def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str) -> None:
  1415. """
  1416. Test client assignment of other client roles.
  1417. :param admin: Keycloak admin
  1418. :type admin: KeycloakAdmin
  1419. :param realm: Keycloak realm
  1420. :type realm: str
  1421. :param client: Keycloak client
  1422. :type client: str
  1423. """
  1424. admin.change_current_realm(realm)
  1425. client_id = admin.create_client(
  1426. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  1427. )
  1428. # Test get client roles
  1429. roles = admin.get_client_roles_of_client_scope(client_id, client)
  1430. assert len(roles) == 0, roles
  1431. # create client role for test
  1432. client_role_id = admin.create_client_role(
  1433. client_role_id=client,
  1434. payload={"name": "client-role-test"},
  1435. skip_exists=True,
  1436. )
  1437. assert client_role_id, client_role_id
  1438. # Test client role assignment to other client
  1439. with pytest.raises(KeycloakPostError) as err:
  1440. admin.assign_client_roles_to_client_scope(
  1441. client_id=client_id,
  1442. client_roles_owner_id=client,
  1443. roles=["bad"],
  1444. )
  1445. assert err.match(UNKOWN_ERROR_REGEX), err
  1446. res = admin.assign_client_roles_to_client_scope(
  1447. client_id=client_id,
  1448. client_roles_owner_id=client,
  1449. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1450. )
  1451. assert res == {}, res
  1452. roles = admin.get_client_roles_of_client_scope(
  1453. client_id=client_id,
  1454. client_roles_owner_id=client,
  1455. )
  1456. assert len(roles) == 1
  1457. client_role_names = [x["name"] for x in roles]
  1458. assert "client-role-test" in client_role_names, client_role_names
  1459. # Test remove realm role of client
  1460. with pytest.raises(KeycloakDeleteError) as err:
  1461. admin.delete_client_roles_of_client_scope(
  1462. client_id=client_id,
  1463. client_roles_owner_id=client,
  1464. roles=["bad"],
  1465. )
  1466. assert err.match(UNKOWN_ERROR_REGEX), err
  1467. res = admin.delete_client_roles_of_client_scope(
  1468. client_id=client_id,
  1469. client_roles_owner_id=client,
  1470. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1471. )
  1472. assert res == {}, res
  1473. roles = admin.get_client_roles_of_client_scope(
  1474. client_id=client_id,
  1475. client_roles_owner_id=client,
  1476. )
  1477. assert len(roles) == 0
  1478. def test_client_scope_mapping_client_roles(admin: KeycloakAdmin, realm: str, client: str) -> None:
  1479. """
  1480. Test client scope assignment of client roles.
  1481. :param admin: Keycloak admin
  1482. :type admin: KeycloakAdmin
  1483. :param realm: Keycloak realm
  1484. :type realm: str
  1485. :param client: Keycloak client owning roles
  1486. :type client: str
  1487. """
  1488. _client_role_name = "some-client-role"
  1489. admin.change_current_realm(realm)
  1490. client_name = admin.get_client(client)["name"]
  1491. client_scope = {
  1492. "name": "test_client_scope",
  1493. "description": "Test Client Scope",
  1494. "protocol": "openid-connect",
  1495. "attributes": {},
  1496. }
  1497. client_scope_id = admin.create_client_scope(client_scope, skip_exists=False)
  1498. # Test get client roles
  1499. client_specific_roles = admin.get_client_specific_roles_of_client_scope(
  1500. client_scope_id,
  1501. client,
  1502. )
  1503. assert len(client_specific_roles) == 0, client_specific_roles
  1504. all_roles = admin.get_all_roles_of_client_scope(client_scope_id)
  1505. assert len(all_roles) == 0, all_roles
  1506. # create client role for test
  1507. client_role_name = admin.create_client_role(
  1508. client_role_id=client,
  1509. payload={"name": _client_role_name},
  1510. skip_exists=True,
  1511. )
  1512. assert client_role_name, client_role_name
  1513. # Test client role assignment to other client
  1514. with pytest.raises(KeycloakPostError) as err:
  1515. admin.add_client_specific_roles_to_client_scope(
  1516. client_scope_id=client_scope_id,
  1517. client_roles_owner_id=client,
  1518. roles=["bad"],
  1519. )
  1520. assert err.match(UNKOWN_ERROR_REGEX), err
  1521. res = admin.add_client_specific_roles_to_client_scope(
  1522. client_scope_id=client_scope_id,
  1523. client_roles_owner_id=client,
  1524. roles=[admin.get_client_role(client_id=client, role_name=_client_role_name)],
  1525. )
  1526. assert res == {}, res
  1527. # Test when getting roles for the specific owner client
  1528. client_specific_roles = admin.get_client_specific_roles_of_client_scope(
  1529. client_scope_id=client_scope_id,
  1530. client_roles_owner_id=client,
  1531. )
  1532. assert len(client_specific_roles) == 1
  1533. client_role_names = [x["name"] for x in client_specific_roles]
  1534. assert _client_role_name in client_role_names, client_role_names
  1535. # Test when getting all roles for the client scope
  1536. all_roles = admin.get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  1537. assert "clientMappings" in all_roles, all_roles
  1538. all_roles_clients = all_roles["clientMappings"]
  1539. assert client_name in all_roles_clients, all_roles_clients
  1540. mappings = all_roles_clients[client_name]["mappings"]
  1541. client_role_names = [x["name"] for x in mappings]
  1542. assert _client_role_name in client_role_names, client_role_names
  1543. # Test remove realm role of client
  1544. with pytest.raises(KeycloakDeleteError) as err:
  1545. admin.remove_client_specific_roles_of_client_scope(
  1546. client_scope_id=client_scope_id,
  1547. client_roles_owner_id=client,
  1548. roles=["bad"],
  1549. )
  1550. assert err.match(UNKOWN_ERROR_REGEX), err
  1551. res = admin.remove_client_specific_roles_of_client_scope(
  1552. client_scope_id=client_scope_id,
  1553. client_roles_owner_id=client,
  1554. roles=[admin.get_client_role(client_id=client, role_name=_client_role_name)],
  1555. )
  1556. assert res == {}, res
  1557. all_roles = admin.get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  1558. assert len(all_roles) == 0
  1559. def test_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str) -> None:
  1560. """
  1561. Test client assignment of default client scopes.
  1562. :param admin: Keycloak admin
  1563. :type admin: KeycloakAdmin
  1564. :param realm: Keycloak realm
  1565. :type realm: str
  1566. :param client: Keycloak client
  1567. :type client: str
  1568. """
  1569. admin.change_current_realm(realm)
  1570. client_id = admin.create_client(
  1571. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  1572. )
  1573. # Test get client default scopes
  1574. # keycloak default roles: web-origins, acr, profile, roles, email
  1575. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1576. assert len(default_client_scopes) in [6, 5], default_client_scopes
  1577. # Test add a client scope to client default scopes
  1578. default_client_scope = "test-client-default-scope"
  1579. new_client_scope = {
  1580. "name": default_client_scope,
  1581. "description": f"Test Client Scope: {default_client_scope}",
  1582. "protocol": "openid-connect",
  1583. "attributes": {},
  1584. }
  1585. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1586. new_default_client_scope_data = {
  1587. "realm": realm,
  1588. "client": client_id,
  1589. "clientScopeId": new_client_scope_id,
  1590. }
  1591. admin.add_client_default_client_scope(
  1592. client_id,
  1593. new_client_scope_id,
  1594. new_default_client_scope_data,
  1595. )
  1596. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1597. assert len(default_client_scopes) in [6, 7], default_client_scopes
  1598. # Test remove a client default scope
  1599. admin.delete_client_default_client_scope(client_id, new_client_scope_id)
  1600. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1601. assert len(default_client_scopes) in [5, 6], default_client_scopes
  1602. def test_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str) -> None:
  1603. """
  1604. Test client assignment of optional client scopes.
  1605. :param admin: Keycloak admin
  1606. :type admin: KeycloakAdmin
  1607. :param realm: Keycloak realm
  1608. :type realm: str
  1609. :param client: Keycloak client
  1610. :type client: str
  1611. """
  1612. admin.change_current_realm(realm)
  1613. client_id = admin.create_client(
  1614. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  1615. )
  1616. # Test get client optional scopes
  1617. # keycloak optional roles: microprofile-jwt, offline_access, address, --> for versions < 26.0.0
  1618. # starting with Keycloak version 26.0.0 a new optional role is added: organization
  1619. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1620. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  1621. # Test add a client scope to client optional scopes
  1622. optional_client_scope = "test-client-optional-scope"
  1623. new_client_scope = {
  1624. "name": optional_client_scope,
  1625. "description": f"Test Client Scope: {optional_client_scope}",
  1626. "protocol": "openid-connect",
  1627. "attributes": {},
  1628. }
  1629. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1630. new_optional_client_scope_data = {
  1631. "realm": realm,
  1632. "client": client_id,
  1633. "clientScopeId": new_client_scope_id,
  1634. }
  1635. admin.add_client_optional_client_scope(
  1636. client_id,
  1637. new_client_scope_id,
  1638. new_optional_client_scope_data,
  1639. )
  1640. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1641. assert len(optional_client_scopes) in [5, 6], optional_client_scopes
  1642. # Test remove a client optional scope
  1643. admin.delete_client_optional_client_scope(client_id, new_client_scope_id)
  1644. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1645. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  1646. def test_client_roles(admin: KeycloakAdmin, client: str) -> None:
  1647. """
  1648. Test client roles.
  1649. :param admin: Keycloak Admin client
  1650. :type admin: KeycloakAdmin
  1651. :param client: Keycloak client
  1652. :type client: str
  1653. """
  1654. # Test get client roles
  1655. res = admin.get_client_roles(client_id=client)
  1656. assert len(res) == 0
  1657. with pytest.raises(KeycloakGetError) as err:
  1658. admin.get_client_roles(client_id="bad")
  1659. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1660. # Test create client role
  1661. client_role_id = admin.create_client_role(
  1662. client_role_id=client,
  1663. payload={"name": "client-role-test"},
  1664. skip_exists=True,
  1665. )
  1666. with pytest.raises(KeycloakPostError) as err:
  1667. admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"})
  1668. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  1669. client_role_id_2 = admin.create_client_role(
  1670. client_role_id=client,
  1671. payload={"name": "client-role-test"},
  1672. skip_exists=True,
  1673. )
  1674. assert client_role_id == client_role_id_2
  1675. # Test get client role
  1676. res = admin.get_client_role(client_id=client, role_name="client-role-test")
  1677. assert res["name"] == client_role_id
  1678. with pytest.raises(KeycloakGetError) as err:
  1679. admin.get_client_role(client_id=client, role_name="bad")
  1680. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1681. res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test")
  1682. assert res_ == res["id"]
  1683. with pytest.raises(KeycloakGetError) as err:
  1684. admin.get_client_role_id(client_id=client, role_name="bad")
  1685. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1686. assert len(admin.get_client_roles(client_id=client)) == 1
  1687. # Test update client role
  1688. res = admin.update_client_role(
  1689. client_id=client,
  1690. role_name="client-role-test",
  1691. payload={"name": "client-role-test-update"},
  1692. )
  1693. assert res == {}
  1694. with pytest.raises(KeycloakPutError) as err:
  1695. res = admin.update_client_role(
  1696. client_id=client,
  1697. role_name="client-role-test",
  1698. payload={"name": "client-role-test-update"},
  1699. )
  1700. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1701. # Test user with client role
  1702. res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update")
  1703. assert len(res) == 0
  1704. with pytest.raises(KeycloakGetError) as err:
  1705. admin.get_client_role_members(client_id=client, role_name="bad")
  1706. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1707. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  1708. with pytest.raises(KeycloakPostError) as err:
  1709. admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  1710. assert err.match(UNKOWN_ERROR_REGEX), err
  1711. res = admin.assign_client_role(
  1712. user_id=user_id,
  1713. client_id=client,
  1714. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1715. )
  1716. assert res == {}
  1717. assert (
  1718. len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update"))
  1719. == 1
  1720. )
  1721. roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client)
  1722. assert len(roles) == 1, roles
  1723. with pytest.raises(KeycloakGetError) as err:
  1724. admin.get_client_roles_of_user(user_id=user_id, client_id="bad")
  1725. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1726. roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  1727. assert len(roles) == 1, roles
  1728. with pytest.raises(KeycloakGetError) as err:
  1729. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1730. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1731. roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client)
  1732. assert len(roles) == 0, roles
  1733. with pytest.raises(KeycloakGetError) as err:
  1734. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1735. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1736. with pytest.raises(KeycloakDeleteError) as err:
  1737. admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  1738. assert err.match(UNKOWN_ERROR_REGEX), err
  1739. admin.delete_client_roles_of_user(
  1740. user_id=user_id,
  1741. client_id=client,
  1742. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1743. )
  1744. assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  1745. # Test groups and client roles
  1746. res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")
  1747. assert len(res) == 0
  1748. with pytest.raises(KeycloakGetError) as err:
  1749. admin.get_client_role_groups(client_id=client, role_name="bad")
  1750. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1751. group_id = admin.create_group(payload={"name": "test-group"})
  1752. res = admin.get_group_client_roles(group_id=group_id, client_id=client)
  1753. assert len(res) == 0
  1754. with pytest.raises(KeycloakGetError) as err:
  1755. admin.get_group_client_roles(group_id=group_id, client_id="bad")
  1756. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1757. with pytest.raises(KeycloakPostError) as err:
  1758. admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1759. assert err.match(UNKOWN_ERROR_REGEX), err
  1760. res = admin.assign_group_client_roles(
  1761. group_id=group_id,
  1762. client_id=client,
  1763. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1764. )
  1765. assert res == {}
  1766. assert (
  1767. len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update"))
  1768. == 1
  1769. )
  1770. assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1
  1771. with pytest.raises(KeycloakDeleteError) as err:
  1772. admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1773. assert err.match(UNKOWN_ERROR_REGEX), err
  1774. res = admin.delete_group_client_roles(
  1775. group_id=group_id,
  1776. client_id=client,
  1777. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1778. )
  1779. assert res == {}
  1780. # Test composite client roles
  1781. with pytest.raises(KeycloakPostError) as err:
  1782. admin.add_composite_client_roles_to_role(
  1783. client_role_id=client,
  1784. role_name="client-role-test-update",
  1785. roles=["bad"],
  1786. )
  1787. assert err.match(UNKOWN_ERROR_REGEX), err
  1788. res = admin.add_composite_client_roles_to_role(
  1789. client_role_id=client,
  1790. role_name="client-role-test-update",
  1791. roles=[admin.get_realm_role(role_name="offline_access")],
  1792. )
  1793. assert res == {}
  1794. assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1795. "composite"
  1796. ]
  1797. # Test removal of composite client roles
  1798. with pytest.raises(KeycloakDeleteError) as err:
  1799. admin.remove_composite_client_roles_from_role(
  1800. client_role_id=client,
  1801. role_name="client-role-test-update",
  1802. roles=["bad"],
  1803. )
  1804. assert err.match(UNKOWN_ERROR_REGEX), err
  1805. res = admin.remove_composite_client_roles_from_role(
  1806. client_role_id=client,
  1807. role_name="client-role-test-update",
  1808. roles=[admin.get_realm_role(role_name="offline_access")],
  1809. )
  1810. assert res == {}
  1811. assert not admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1812. "composite"
  1813. ]
  1814. # Test delete of client role
  1815. res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1816. assert res == {}
  1817. with pytest.raises(KeycloakDeleteError) as err:
  1818. admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1819. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1820. # Test of roles by id - Get role
  1821. admin.create_client_role(
  1822. client_role_id=client,
  1823. payload={"name": "client-role-by-id-test"},
  1824. skip_exists=True,
  1825. )
  1826. role = admin.get_client_role(client_id=client, role_name="client-role-by-id-test")
  1827. res = admin.get_role_by_id(role_id=role["id"])
  1828. assert res["name"] == "client-role-by-id-test"
  1829. with pytest.raises(KeycloakGetError) as err:
  1830. admin.get_role_by_id(role_id="bad")
  1831. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1832. # Test of roles by id - Update role
  1833. res = admin.update_role_by_id(
  1834. role_id=role["id"],
  1835. payload={"name": "client-role-by-id-test-update"},
  1836. )
  1837. assert res == {}
  1838. with pytest.raises(KeycloakPutError) as err:
  1839. res = admin.update_role_by_id(
  1840. role_id="bad",
  1841. payload={"name": "client-role-by-id-test-update"},
  1842. )
  1843. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1844. # Test of roles by id - Delete role
  1845. res = admin.delete_role_by_id(role_id=role["id"])
  1846. assert res == {}
  1847. with pytest.raises(KeycloakDeleteError) as err:
  1848. admin.delete_role_by_id(role_id="bad")
  1849. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1850. def test_enable_token_exchange(admin: KeycloakAdmin, realm: str) -> None:
  1851. """
  1852. Test enable token exchange.
  1853. :param admin: Keycloak Admin client
  1854. :type admin: KeycloakAdmin
  1855. :param realm: Keycloak realm
  1856. :type realm: str
  1857. :raises AssertionError: In case of bad configuration
  1858. """
  1859. # Test enabling token exchange between two confidential clients
  1860. admin.change_current_realm(realm)
  1861. # Create test clients
  1862. source_client_id = admin.create_client(
  1863. payload={"name": "Source Client", "clientId": "source-client"},
  1864. )
  1865. target_client_id = admin.create_client(
  1866. payload={"name": "Target Client", "clientId": "target-client"},
  1867. )
  1868. for c in admin.get_clients():
  1869. if c["clientId"] == "realm-management":
  1870. realm_management_id = c["id"]
  1871. break
  1872. else:
  1873. pytest.fail("Missing realm management client")
  1874. # Enable permissions on the Superset client
  1875. admin.update_client_management_permissions(
  1876. payload={"enabled": True},
  1877. client_id=target_client_id,
  1878. )
  1879. # Fetch various IDs and strings needed when creating the permission
  1880. token_exchange_permission_id = admin.get_client_management_permissions(
  1881. client_id=target_client_id,
  1882. )["scopePermissions"]["token-exchange"]
  1883. scopes = admin.get_client_authz_policy_scopes(
  1884. client_id=realm_management_id,
  1885. policy_id=token_exchange_permission_id,
  1886. )
  1887. for s in scopes:
  1888. if s["name"] == "token-exchange":
  1889. token_exchange_scope_id = s["id"]
  1890. break
  1891. else:
  1892. pytest.fail("Missing token-exchange scope")
  1893. resources = admin.get_client_authz_policy_resources(
  1894. client_id=realm_management_id,
  1895. policy_id=token_exchange_permission_id,
  1896. )
  1897. for r in resources:
  1898. if r["name"] == f"client.resource.{target_client_id}":
  1899. token_exchange_resource_id = r["_id"]
  1900. break
  1901. else:
  1902. pytest.fail("Missing client resource")
  1903. # Create a client policy for source client
  1904. policy_name = "Exchange source client token with target client token"
  1905. client_policy_id = admin.create_client_authz_client_policy(
  1906. payload={
  1907. "type": "client",
  1908. "logic": "POSITIVE",
  1909. "decisionStrategy": "UNANIMOUS",
  1910. "name": policy_name,
  1911. "clients": [source_client_id],
  1912. },
  1913. client_id=realm_management_id,
  1914. )["id"]
  1915. policies = admin.get_client_authz_client_policies(client_id=realm_management_id)
  1916. for policy in policies:
  1917. if policy["name"] == policy_name:
  1918. assert policy["clients"] == [source_client_id]
  1919. break
  1920. else:
  1921. pytest.fail("Missing client policy")
  1922. # Update permissions on the target client to reference this policy
  1923. permission_name = admin.get_client_authz_scope_permission(
  1924. client_id=realm_management_id,
  1925. scope_id=token_exchange_permission_id,
  1926. )["name"]
  1927. admin.update_client_authz_scope_permission(
  1928. payload={
  1929. "id": token_exchange_permission_id,
  1930. "name": permission_name,
  1931. "type": "scope",
  1932. "logic": "POSITIVE",
  1933. "decisionStrategy": "UNANIMOUS",
  1934. "resources": [token_exchange_resource_id],
  1935. "scopes": [token_exchange_scope_id],
  1936. "policies": [client_policy_id],
  1937. },
  1938. client_id=realm_management_id,
  1939. scope_id=token_exchange_permission_id,
  1940. )
  1941. # Create permissions on the target client to reference this policy
  1942. admin.create_client_authz_scope_permission(
  1943. payload={
  1944. "id": "some-id",
  1945. "name": "test-permission",
  1946. "type": "scope",
  1947. "logic": "POSITIVE",
  1948. "decisionStrategy": "UNANIMOUS",
  1949. "resources": [token_exchange_resource_id],
  1950. "scopes": [token_exchange_scope_id],
  1951. "policies": [client_policy_id],
  1952. },
  1953. client_id=realm_management_id,
  1954. )
  1955. permission_name = admin.get_client_authz_scope_permission(
  1956. client_id=realm_management_id,
  1957. scope_id=token_exchange_permission_id,
  1958. )["name"]
  1959. assert permission_name.startswith("token-exchange.permission.client.")
  1960. with pytest.raises(KeycloakPostError) as err:
  1961. admin.create_client_authz_scope_permission(
  1962. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  1963. client_id="realm_management_id",
  1964. )
  1965. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1966. def test_email(admin: KeycloakAdmin, user: str) -> None:
  1967. """
  1968. Test email.
  1969. :param admin: Keycloak Admin client
  1970. :type admin: KeycloakAdmin
  1971. :param user: Keycloak user
  1972. :type user: str
  1973. """
  1974. # Emails will fail as we don't have SMTP test setup
  1975. with pytest.raises(KeycloakPutError) as err:
  1976. admin.send_update_account(user_id=user, payload={})
  1977. assert err.match(UNKOWN_ERROR_REGEX), err
  1978. admin.update_user(user_id=user, payload={"enabled": True})
  1979. with pytest.raises(KeycloakPutError) as err:
  1980. admin.send_verify_email(user_id=user)
  1981. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  1982. def test_get_sessions(admin: KeycloakAdmin) -> None:
  1983. """
  1984. Test get sessions.
  1985. :param admin: Keycloak Admin client
  1986. :type admin: KeycloakAdmin
  1987. """
  1988. sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.connection.username))
  1989. assert len(sessions) >= 1
  1990. with pytest.raises(KeycloakGetError) as err:
  1991. admin.get_sessions(user_id="bad")
  1992. assert err.match(USER_NOT_FOUND_REGEX)
  1993. def test_get_client_installation_provider(admin: KeycloakAdmin, client: str) -> None:
  1994. """
  1995. Test get client installation provider.
  1996. :param admin: Keycloak Admin client
  1997. :type admin: KeycloakAdmin
  1998. :param client: Keycloak client
  1999. :type client: str
  2000. """
  2001. with pytest.raises(KeycloakGetError) as err:
  2002. admin.get_client_installation_provider(client_id=client, provider_id="bad")
  2003. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  2004. installation = admin.get_client_installation_provider(
  2005. client_id=client,
  2006. provider_id="keycloak-oidc-keycloak-json",
  2007. )
  2008. assert set(installation.keys()) == {
  2009. "auth-server-url",
  2010. "confidential-port",
  2011. "credentials",
  2012. "realm",
  2013. "resource",
  2014. "ssl-required",
  2015. }
  2016. def test_auth_flows(admin: KeycloakAdmin, realm: str) -> None:
  2017. """
  2018. Test auth flows.
  2019. :param admin: Keycloak Admin client
  2020. :type admin: KeycloakAdmin
  2021. :param realm: Keycloak realm
  2022. :type realm: str
  2023. """
  2024. admin.change_current_realm(realm)
  2025. res = admin.get_authentication_flows()
  2026. assert len(res) <= 8, res
  2027. default_flows = len(res)
  2028. assert {x["alias"] for x in res}.issubset(
  2029. {
  2030. "reset credentials",
  2031. "browser",
  2032. "registration",
  2033. "http challenge",
  2034. "docker auth",
  2035. "direct grant",
  2036. "first broker login",
  2037. "clients",
  2038. },
  2039. )
  2040. assert set(res[0].keys()) == {
  2041. "alias",
  2042. "authenticationExecutions",
  2043. "builtIn",
  2044. "description",
  2045. "id",
  2046. "providerId",
  2047. "topLevel",
  2048. }
  2049. assert {x["alias"] for x in res}.issubset(
  2050. {
  2051. "reset credentials",
  2052. "browser",
  2053. "registration",
  2054. "docker auth",
  2055. "direct grant",
  2056. "first broker login",
  2057. "clients",
  2058. "http challenge",
  2059. },
  2060. )
  2061. with pytest.raises(KeycloakGetError) as err:
  2062. admin.get_authentication_flow_for_id(flow_id="bad")
  2063. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  2064. browser_flow_id = next(x for x in res if x["alias"] == "browser")["id"]
  2065. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  2066. assert res["alias"] == "browser"
  2067. # Test copying
  2068. with pytest.raises(KeycloakPostError) as err:
  2069. admin.copy_authentication_flow(payload={}, flow_alias="bad")
  2070. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  2071. res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser")
  2072. assert res == b"", res
  2073. assert len(admin.get_authentication_flows()) == (default_flows + 1)
  2074. # Test create
  2075. res = admin.create_authentication_flow(
  2076. payload={"alias": "test-create", "providerId": "basic-flow"},
  2077. )
  2078. assert res == b""
  2079. with pytest.raises(KeycloakPostError) as err:
  2080. admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False})
  2081. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  2082. assert admin.create_authentication_flow(
  2083. payload={"alias": "test-create"},
  2084. skip_exists=True,
  2085. ) == {"msg": "Already exists"}
  2086. # Update
  2087. res = admin.get_authentication_flows()
  2088. browser_flow_id = next(x for x in res if x["alias"] == "browser")["id"]
  2089. flow = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  2090. del flow["authenticationExecutions"]
  2091. del flow["id"]
  2092. flow["description"] = "test description"
  2093. res = admin.update_authentication_flow(flow_id=browser_flow_id, payload=flow)
  2094. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  2095. assert res["description"] == "test description"
  2096. # Test flow executions
  2097. res = admin.get_authentication_flow_executions(flow_alias="browser")
  2098. assert len(res) in [8, 12], res
  2099. with pytest.raises(KeycloakGetError) as err:
  2100. admin.get_authentication_flow_executions(flow_alias="bad")
  2101. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  2102. exec_id = res[0]["id"]
  2103. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  2104. assert set(res.keys()).issubset(
  2105. {
  2106. "alternative",
  2107. "authenticator",
  2108. "authenticatorFlow",
  2109. "autheticatorFlow",
  2110. "conditional",
  2111. "disabled",
  2112. "enabled",
  2113. "id",
  2114. "parentFlow",
  2115. "priority",
  2116. "required",
  2117. "requirement",
  2118. },
  2119. ), res.keys()
  2120. with pytest.raises(KeycloakGetError) as err:
  2121. admin.get_authentication_flow_execution(execution_id="bad")
  2122. assert err.match(ILLEGAL_EXECUTION_REGEX)
  2123. with pytest.raises(KeycloakPostError) as err:
  2124. admin.create_authentication_flow_execution(payload={}, flow_alias="browser")
  2125. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  2126. res = admin.create_authentication_flow_execution(
  2127. payload={"provider": "auth-cookie"},
  2128. flow_alias="test-create",
  2129. )
  2130. assert res == b""
  2131. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  2132. with pytest.raises(KeycloakPutError) as err:
  2133. admin.update_authentication_flow_executions(
  2134. payload={"required": "yes"},
  2135. flow_alias="test-create",
  2136. )
  2137. assert err.match("Unrecognized field")
  2138. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  2139. payload["displayName"] = "test"
  2140. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  2141. assert res or (res == {})
  2142. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  2143. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  2144. assert res == {}
  2145. with pytest.raises(KeycloakDeleteError) as err:
  2146. admin.delete_authentication_flow_execution(execution_id=exec_id)
  2147. assert err.match(ILLEGAL_EXECUTION_REGEX)
  2148. # Test subflows
  2149. res = admin.create_authentication_flow_subflow(
  2150. payload={
  2151. "alias": "test-subflow",
  2152. "provider": "basic-flow",
  2153. "type": "something",
  2154. "description": "something",
  2155. },
  2156. flow_alias="test-browser",
  2157. )
  2158. assert res == b""
  2159. with pytest.raises(KeycloakPostError) as err:
  2160. admin.create_authentication_flow_subflow(
  2161. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  2162. flow_alias="test-browser",
  2163. )
  2164. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  2165. res = admin.create_authentication_flow_subflow(
  2166. payload={
  2167. "alias": "test-subflow",
  2168. "provider": "basic-flow",
  2169. "type": "something",
  2170. "description": "something",
  2171. },
  2172. flow_alias="test-create",
  2173. skip_exists=True,
  2174. )
  2175. assert res == {"msg": "Already exists"}
  2176. # Test delete auth flow
  2177. flow_id = next(x for x in admin.get_authentication_flows() if x["alias"] == "test-browser")[
  2178. "id"
  2179. ]
  2180. res = admin.delete_authentication_flow(flow_id=flow_id)
  2181. assert res == {}
  2182. with pytest.raises(KeycloakDeleteError) as err:
  2183. admin.delete_authentication_flow(flow_id=flow_id)
  2184. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  2185. 'b\'{"error":"Flow not found"' in str(err)
  2186. )
  2187. def test_auth_flow_execution_priority(admin: KeycloakAdmin, realm: str) -> None:
  2188. """
  2189. Test execution priority.
  2190. :param admin: Keycloak Admin client
  2191. :type admin: KeycloakAdmin
  2192. :param realm: Keycloak realm
  2193. :type realm: str
  2194. """
  2195. admin.change_current_realm(realm)
  2196. _ = admin.create_authentication_flow(
  2197. payload={"alias": "test-create", "providerId": "basic-flow"},
  2198. )
  2199. _ = admin.create_authentication_flow_execution(
  2200. payload={"provider": "auth-cookie"},
  2201. flow_alias="test-create",
  2202. )
  2203. _ = admin.create_authentication_flow_execution(
  2204. payload={"provider": "auth-cookie"},
  2205. flow_alias="test-create",
  2206. )
  2207. executions = admin.get_authentication_flow_executions(flow_alias="test-create")
  2208. priority_list = [ex["id"] for ex in executions]
  2209. _ = admin.change_execution_priority(priority_list[1], 1)
  2210. new_executions = admin.get_authentication_flow_executions(flow_alias="test-create")
  2211. assert executions != new_executions
  2212. _ = admin.change_execution_priority(priority_list[1], -1)
  2213. new_executions = admin.get_authentication_flow_executions(flow_alias="test-create")
  2214. assert executions == new_executions
  2215. def test_authentication_configs(admin: KeycloakAdmin, realm: str) -> None:
  2216. """
  2217. Test authentication configs.
  2218. :param admin: Keycloak Admin client
  2219. :type admin: KeycloakAdmin
  2220. :param realm: Keycloak realm
  2221. :type realm: str
  2222. """
  2223. admin.change_current_realm(realm)
  2224. # Test list of auth providers
  2225. res = admin.get_authenticator_providers()
  2226. assert len(res) <= 40
  2227. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  2228. assert res == {
  2229. "helpText": "Validates the SSO cookie set by the auth server.",
  2230. "name": "Cookie",
  2231. "properties": [],
  2232. "providerId": "auth-cookie",
  2233. }
  2234. # Test authenticator config
  2235. executions = admin.get_authentication_flow_executions(flow_alias="browser")
  2236. execution = next(ex for ex in executions if ex["configurable"])
  2237. _ = admin.create_execution_config(
  2238. execution["id"],
  2239. {
  2240. "alias": "test.provisioning.property",
  2241. "config": {"test.provisioning.property": "value2"},
  2242. },
  2243. )
  2244. executions = admin.get_authentication_flow_executions(flow_alias="browser")
  2245. execution_config_id = next(ex for ex in executions if ex.get("id") == execution["id"])[
  2246. "authenticationConfig"
  2247. ]
  2248. res = admin.get_authenticator_config(config_id=execution_config_id)
  2249. assert res["config"]["test.provisioning.property"] == "value2"
  2250. with pytest.raises(KeycloakGetError) as err:
  2251. admin.get_authenticator_config(config_id="bad")
  2252. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2253. with pytest.raises(KeycloakPutError) as err:
  2254. admin.update_authenticator_config(payload={}, config_id="bad")
  2255. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2256. with pytest.raises(KeycloakDeleteError) as err:
  2257. admin.delete_authenticator_config(config_id="bad")
  2258. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2259. def test_sync_users(admin: KeycloakAdmin, realm: str) -> None:
  2260. """
  2261. Test sync users.
  2262. :param admin: Keycloak Admin client
  2263. :type admin: KeycloakAdmin
  2264. :param realm: Keycloak realm
  2265. :type realm: str
  2266. """
  2267. admin.change_current_realm(realm)
  2268. # Only testing the error message
  2269. with pytest.raises(KeycloakPostError) as err:
  2270. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  2271. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2272. def test_client_scopes(admin: KeycloakAdmin, realm: str) -> None:
  2273. """
  2274. Test client scopes.
  2275. :param admin: Keycloak Admin client
  2276. :type admin: KeycloakAdmin
  2277. :param realm: Keycloak realm
  2278. :type realm: str
  2279. """
  2280. admin.change_current_realm(realm)
  2281. # Test get client scopes
  2282. res = admin.get_client_scopes()
  2283. scope_names = {x["name"] for x in res}
  2284. assert len(res) in [10, 11, 13, 14]
  2285. assert "email" in scope_names
  2286. assert "profile" in scope_names
  2287. assert "offline_access" in scope_names
  2288. with pytest.raises(KeycloakGetError) as err:
  2289. admin.get_client_scope(client_scope_id="does-not-exist")
  2290. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2291. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  2292. assert res[0] == scope
  2293. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  2294. assert res[0] == scope
  2295. # Test create client scope
  2296. res = admin.create_client_scope(
  2297. payload={"name": "test-scope", "protocol": "openid-connect"},
  2298. skip_exists=True,
  2299. )
  2300. assert res
  2301. res2 = admin.create_client_scope(
  2302. payload={"name": "test-scope", "protocol": "openid-connect"},
  2303. skip_exists=True,
  2304. )
  2305. assert res == res2
  2306. with pytest.raises(KeycloakPostError) as err:
  2307. admin.create_client_scope(
  2308. payload={"name": "test-scope", "protocol": "openid-connect"},
  2309. skip_exists=False,
  2310. )
  2311. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  2312. # Test update client scope
  2313. with pytest.raises(KeycloakPutError) as err:
  2314. admin.update_client_scope(client_scope_id="does-not-exist", payload={})
  2315. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2316. res_update = admin.update_client_scope(
  2317. client_scope_id=res,
  2318. payload={"name": "test-scope-update"},
  2319. )
  2320. assert res_update == {}
  2321. assert admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  2322. # Test get mappers
  2323. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  2324. assert mappers == []
  2325. # Test add mapper
  2326. with pytest.raises(KeycloakPostError) as err:
  2327. admin.add_mapper_to_client_scope(client_scope_id=res, payload={})
  2328. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  2329. res_add = admin.add_mapper_to_client_scope(
  2330. client_scope_id=res,
  2331. payload={
  2332. "name": "test-mapper",
  2333. "protocol": "openid-connect",
  2334. "protocolMapper": "oidc-usermodel-attribute-mapper",
  2335. },
  2336. )
  2337. assert res_add == b""
  2338. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  2339. # Test update mapper
  2340. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  2341. with pytest.raises(KeycloakPutError) as err:
  2342. admin.update_mapper_in_client_scope(
  2343. client_scope_id="does-not-exist",
  2344. protocol_mapper_id=test_mapper["id"],
  2345. payload={},
  2346. )
  2347. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2348. test_mapper["config"]["user.attribute"] = "test"
  2349. res_update = admin.update_mapper_in_client_scope(
  2350. client_scope_id=res,
  2351. protocol_mapper_id=test_mapper["id"],
  2352. payload=test_mapper,
  2353. )
  2354. assert res_update == {}
  2355. assert (
  2356. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  2357. == "test"
  2358. )
  2359. # Test delete mapper
  2360. res_del = admin.delete_mapper_from_client_scope(
  2361. client_scope_id=res,
  2362. protocol_mapper_id=test_mapper["id"],
  2363. )
  2364. assert res_del == {}
  2365. with pytest.raises(KeycloakDeleteError) as err:
  2366. admin.delete_mapper_from_client_scope(
  2367. client_scope_id=res,
  2368. protocol_mapper_id=test_mapper["id"],
  2369. )
  2370. assert err.match('404: b\'{"error":"Model not found".*}\'')
  2371. # Test default default scopes
  2372. res_defaults = admin.get_default_default_client_scopes()
  2373. assert len(res_defaults) in [6, 7, 8]
  2374. with pytest.raises(KeycloakPutError) as err:
  2375. admin.add_default_default_client_scope(scope_id="does-not-exist")
  2376. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2377. res_add = admin.add_default_default_client_scope(scope_id=res)
  2378. assert res_add == {}
  2379. assert len(admin.get_default_default_client_scopes()) in [7, 8, 9]
  2380. with pytest.raises(KeycloakDeleteError) as err:
  2381. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  2382. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2383. res_del = admin.delete_default_default_client_scope(scope_id=res)
  2384. assert res_del == {}
  2385. assert len(admin.get_default_default_client_scopes()) in [6, 7, 8]
  2386. # Test default optional scopes
  2387. res_defaults = admin.get_default_optional_client_scopes()
  2388. assert len(res_defaults) in [4, 5]
  2389. with pytest.raises(KeycloakPutError) as err:
  2390. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  2391. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2392. res_add = admin.add_default_optional_client_scope(scope_id=res)
  2393. assert res_add == {}
  2394. assert len(admin.get_default_optional_client_scopes()) in [5, 6]
  2395. with pytest.raises(KeycloakDeleteError) as err:
  2396. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  2397. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2398. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  2399. assert res_del == {}
  2400. assert len(admin.get_default_optional_client_scopes()) in [4, 5]
  2401. # Test client scope delete
  2402. res_del = admin.delete_client_scope(client_scope_id=res)
  2403. assert res_del == {}
  2404. with pytest.raises(KeycloakDeleteError) as err:
  2405. admin.delete_client_scope(client_scope_id=res)
  2406. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2407. def test_components(admin: KeycloakAdmin, realm: str) -> None:
  2408. """
  2409. Test components.
  2410. :param admin: Keycloak Admin client
  2411. :type admin: KeycloakAdmin
  2412. :param realm: Keycloak realm
  2413. :type realm: str
  2414. """
  2415. admin.change_current_realm(realm)
  2416. # Test get components
  2417. res = admin.get_components()
  2418. assert len(res) == 12
  2419. with pytest.raises(KeycloakGetError) as err:
  2420. admin.get_component(component_id="does-not-exist")
  2421. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2422. res_get = admin.get_component(component_id=res[0]["id"])
  2423. assert res_get == res[0]
  2424. # Test create component
  2425. with pytest.raises(KeycloakPostError) as err:
  2426. admin.create_component(payload={"bad": "dict"})
  2427. assert err.match("Unrecognized field")
  2428. res = admin.create_component(
  2429. payload={
  2430. "name": "Test Component",
  2431. "providerId": "max-clients",
  2432. "providerType": "org.keycloak.services.clientregistration.policy."
  2433. "ClientRegistrationPolicy",
  2434. "config": {"max-clients": ["1000"]},
  2435. },
  2436. )
  2437. assert res
  2438. assert admin.get_component(component_id=res)["name"] == "Test Component"
  2439. # Test update component
  2440. component = admin.get_component(component_id=res)
  2441. component["name"] = "Test Component Update"
  2442. with pytest.raises(KeycloakPutError) as err:
  2443. admin.update_component(component_id="does-not-exist", payload={})
  2444. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2445. res_upd = admin.update_component(component_id=res, payload=component)
  2446. assert res_upd == {}
  2447. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  2448. # Test delete component
  2449. res_del = admin.delete_component(component_id=res)
  2450. assert res_del == {}
  2451. with pytest.raises(KeycloakDeleteError) as err:
  2452. admin.delete_component(component_id=res)
  2453. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2454. def test_keys(admin: KeycloakAdmin, realm: str) -> None:
  2455. """
  2456. Test keys.
  2457. :param admin: Keycloak Admin client
  2458. :type admin: KeycloakAdmin
  2459. :param realm: Keycloak realm
  2460. :type realm: str
  2461. """
  2462. admin.change_current_realm(realm)
  2463. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} or set(
  2464. admin.get_keys()["active"].keys(),
  2465. ) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  2466. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2467. "HS256",
  2468. "RSA-OAEP",
  2469. "AES",
  2470. "RS256",
  2471. } or {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2472. "HS512",
  2473. "RSA-OAEP",
  2474. "AES",
  2475. "RS256",
  2476. }
  2477. def test_admin_events(admin: KeycloakAdmin, realm: str) -> None:
  2478. """
  2479. Test events.
  2480. :param admin: Keycloak Admin client
  2481. :type admin: KeycloakAdmin
  2482. :param realm: Keycloak realm
  2483. :type realm: str
  2484. """
  2485. admin.change_current_realm(realm)
  2486. admin.create_client(payload={"name": "test", "clientId": "test"})
  2487. events = admin.get_admin_events()
  2488. assert events == []
  2489. def test_user_events(admin: KeycloakAdmin, realm: str) -> None:
  2490. """
  2491. Test events.
  2492. :param admin: Keycloak Admin client
  2493. :type admin: KeycloakAdmin
  2494. :param realm: Keycloak realm
  2495. :type realm: str
  2496. """
  2497. admin.change_current_realm(realm)
  2498. events = admin.get_events()
  2499. assert events == []
  2500. with pytest.raises(KeycloakPutError) as err:
  2501. admin.set_events(payload={"bad": "conf"})
  2502. assert err.match("Unrecognized field")
  2503. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  2504. assert res == {}
  2505. admin.create_client(payload={"name": "test", "clientId": "test"})
  2506. events = admin.get_events()
  2507. assert events == []
  2508. @freezegun.freeze_time("2023-02-25 10:00:00")
  2509. def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str) -> None:
  2510. """
  2511. Test auto refresh token.
  2512. :param admin_frozen: Keycloak Admin client with time frozen in place
  2513. :type admin_frozen: KeycloakAdmin
  2514. :param realm: Keycloak realm
  2515. :type realm: str
  2516. """
  2517. admin = admin_frozen
  2518. admin.get_realm(realm_name=realm)
  2519. # Test get refresh
  2520. admin.connection.custom_headers = {
  2521. "Authorization": "Bearer bad",
  2522. "Content-Type": "application/json",
  2523. }
  2524. res = admin.get_realm(realm_name=realm)
  2525. assert res["realm"] == realm
  2526. # Freeze time to simulate the access token expiring
  2527. with freezegun.freeze_time("2023-02-25 10:05:00"):
  2528. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:05:00Z")
  2529. assert admin.get_realm(realm_name=realm)
  2530. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:05:00Z")
  2531. # Test bad refresh token, but first make sure access token has expired again
  2532. with freezegun.freeze_time("2023-02-25 10:10:00"):
  2533. admin.connection.custom_headers = {"Content-Type": "application/json"}
  2534. admin.connection.token["refresh_token"] = "bad" # noqa: S105
  2535. with pytest.raises(KeycloakPostError) as err:
  2536. admin.get_realm(realm_name="test-refresh")
  2537. assert err.match(
  2538. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'',
  2539. )
  2540. admin.connection.get_token()
  2541. # Test post refresh
  2542. with freezegun.freeze_time("2023-02-25 10:15:00"):
  2543. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:15:00Z")
  2544. admin.connection.token = None
  2545. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  2546. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:15:00Z")
  2547. # Test update refresh
  2548. with freezegun.freeze_time("2023-02-25 10:25:00"):
  2549. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:25:00Z")
  2550. admin.connection.token = None
  2551. assert (
  2552. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"}) == {}
  2553. )
  2554. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:25:00Z")
  2555. # Test delete refresh
  2556. with freezegun.freeze_time("2023-02-25 10:35:00"):
  2557. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:35:00Z")
  2558. admin.connection.token = None
  2559. assert admin.delete_realm(realm_name="test-refresh") == {}
  2560. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:35:00Z")
  2561. def test_get_required_actions(admin: KeycloakAdmin, realm: str) -> None:
  2562. """
  2563. Test required actions.
  2564. :param admin: Keycloak Admin client
  2565. :type admin: KeycloakAdmin
  2566. :param realm: Keycloak realm
  2567. :type realm: str
  2568. """
  2569. admin.change_current_realm(realm)
  2570. ractions = admin.get_required_actions()
  2571. assert isinstance(ractions, list)
  2572. for ra in ractions:
  2573. for key in [
  2574. "alias",
  2575. "name",
  2576. "providerId",
  2577. "enabled",
  2578. "defaultAction",
  2579. "priority",
  2580. "config",
  2581. ]:
  2582. assert key in ra
  2583. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str) -> None:
  2584. """
  2585. Test get required action by alias.
  2586. :param admin: Keycloak Admin client
  2587. :type admin: KeycloakAdmin
  2588. :param realm: Keycloak realm
  2589. :type realm: str
  2590. """
  2591. admin.change_current_realm(realm)
  2592. ractions = admin.get_required_actions()
  2593. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2594. assert ra in ractions
  2595. assert ra["alias"] == "UPDATE_PASSWORD"
  2596. assert admin.get_required_action_by_alias("does-not-exist") is None
  2597. def test_update_required_action(admin: KeycloakAdmin, realm: str) -> None:
  2598. """
  2599. Test update required action.
  2600. :param admin: Keycloak Admin client
  2601. :type admin: KeycloakAdmin
  2602. :param realm: Keycloak realm
  2603. :type realm: str
  2604. """
  2605. admin.change_current_realm(realm)
  2606. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2607. old = copy.deepcopy(ra)
  2608. ra["enabled"] = False
  2609. admin.update_required_action("UPDATE_PASSWORD", ra)
  2610. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2611. assert old != newra
  2612. assert newra["enabled"] is False
  2613. def test_get_composite_client_roles_of_group(
  2614. admin: KeycloakAdmin,
  2615. realm: str,
  2616. client: str,
  2617. group: str,
  2618. composite_client_role: str,
  2619. ) -> None:
  2620. """
  2621. Test get composite client roles of group.
  2622. :param admin: Keycloak Admin client
  2623. :type admin: KeycloakAdmin
  2624. :param realm: Keycloak realm
  2625. :type realm: str
  2626. :param client: Keycloak client
  2627. :type client: str
  2628. :param group: Keycloak group
  2629. :type group: str
  2630. :param composite_client_role: Composite client role
  2631. :type composite_client_role: str
  2632. """
  2633. admin.change_current_realm(realm)
  2634. role = admin.get_client_role(client, composite_client_role)
  2635. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  2636. result = admin.get_composite_client_roles_of_group(client, group)
  2637. assert role["id"] in [x["id"] for x in result]
  2638. def test_get_role_client_level_children(
  2639. admin: KeycloakAdmin,
  2640. realm: str,
  2641. client: str,
  2642. composite_client_role: str,
  2643. client_role: str,
  2644. ) -> None:
  2645. """
  2646. Test get children of composite client role.
  2647. :param admin: Keycloak Admin client
  2648. :type admin: KeycloakAdmin
  2649. :param realm: Keycloak realm
  2650. :type realm: str
  2651. :param client: Keycloak client
  2652. :type client: str
  2653. :param composite_client_role: Composite client role
  2654. :type composite_client_role: str
  2655. :param client_role: Client role
  2656. :type client_role: str
  2657. """
  2658. admin.change_current_realm(realm)
  2659. child = admin.get_client_role(client, client_role)
  2660. parent = admin.get_client_role(client, composite_client_role)
  2661. res = admin.get_role_client_level_children(client, parent["id"])
  2662. assert child["id"] in [x["id"] for x in res]
  2663. def test_upload_certificate(
  2664. admin: KeycloakAdmin,
  2665. realm: str,
  2666. client: str,
  2667. selfsigned_cert: tuple,
  2668. ) -> None:
  2669. """
  2670. Test upload certificate.
  2671. :param admin: Keycloak Admin client
  2672. :type admin: KeycloakAdmin
  2673. :param realm: Keycloak realm
  2674. :type realm: str
  2675. :param client: Keycloak client
  2676. :type client: str
  2677. :param selfsigned_cert: Selfsigned certificates
  2678. :type selfsigned_cert: tuple
  2679. """
  2680. admin.change_current_realm(realm)
  2681. cert, _ = selfsigned_cert
  2682. cert = cert.decode("utf-8").strip()
  2683. admin.upload_certificate(client, cert)
  2684. cl = admin.get_client(client)
  2685. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  2686. def test_get_bruteforce_status_for_user(
  2687. admin: KeycloakAdmin,
  2688. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2689. realm: str,
  2690. ) -> None:
  2691. """
  2692. Test users.
  2693. :param admin: Keycloak Admin client
  2694. :type admin: KeycloakAdmin
  2695. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2696. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2697. :param realm: Keycloak realm
  2698. :type realm: str
  2699. """
  2700. oid, username, password = oid_with_credentials
  2701. admin.change_current_realm(realm)
  2702. # Turn on bruteforce protection
  2703. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2704. res = admin.get_realm(realm_name=realm)
  2705. assert res["bruteForceProtected"] is True
  2706. # Test login user with wrong credentials
  2707. with contextlib.suppress(KeycloakAuthenticationError):
  2708. oid.token(username=username, password="wrongpassword") # noqa: S106
  2709. user_id = admin.get_user_id(username)
  2710. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2711. assert bruteforce_status["numFailures"] == 1
  2712. # Cleanup
  2713. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2714. res = admin.get_realm(realm_name=realm)
  2715. assert res["bruteForceProtected"] is False
  2716. def test_clear_bruteforce_attempts_for_user(
  2717. admin: KeycloakAdmin,
  2718. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2719. realm: str,
  2720. ) -> None:
  2721. """
  2722. Test users.
  2723. :param admin: Keycloak Admin client
  2724. :type admin: KeycloakAdmin
  2725. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2726. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2727. :param realm: Keycloak realm
  2728. :type realm: str
  2729. """
  2730. oid, username, password = oid_with_credentials
  2731. admin.change_current_realm(realm)
  2732. # Turn on bruteforce protection
  2733. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2734. res = admin.get_realm(realm_name=realm)
  2735. assert res["bruteForceProtected"] is True
  2736. # Test login user with wrong credentials
  2737. with contextlib.suppress(KeycloakAuthenticationError):
  2738. oid.token(username=username, password="wrongpassword") # noqa: S106
  2739. user_id = admin.get_user_id(username)
  2740. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2741. assert bruteforce_status["numFailures"] == 1
  2742. res = admin.clear_bruteforce_attempts_for_user(user_id)
  2743. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2744. assert bruteforce_status["numFailures"] == 0
  2745. # Cleanup
  2746. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2747. res = admin.get_realm(realm_name=realm)
  2748. assert res["bruteForceProtected"] is False
  2749. def test_clear_bruteforce_attempts_for_all_users(
  2750. admin: KeycloakAdmin,
  2751. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2752. realm: str,
  2753. ) -> None:
  2754. """
  2755. Test users.
  2756. :param admin: Keycloak Admin client
  2757. :type admin: KeycloakAdmin
  2758. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2759. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2760. :param realm: Keycloak realm
  2761. :type realm: str
  2762. """
  2763. oid, username, password = oid_with_credentials
  2764. admin.change_current_realm(realm)
  2765. # Turn on bruteforce protection
  2766. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2767. res = admin.get_realm(realm_name=realm)
  2768. assert res["bruteForceProtected"] is True
  2769. # Test login user with wrong credentials
  2770. with contextlib.suppress(KeycloakAuthenticationError):
  2771. oid.token(username=username, password="wrongpassword") # noqa: S106
  2772. user_id = admin.get_user_id(username)
  2773. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2774. assert bruteforce_status["numFailures"] == 1
  2775. res = admin.clear_all_bruteforce_attempts()
  2776. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2777. assert bruteforce_status["numFailures"] == 0
  2778. # Cleanup
  2779. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2780. res = admin.get_realm(realm_name=realm)
  2781. assert res["bruteForceProtected"] is False
  2782. def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  2783. """
  2784. Test that the default realm role is present in a brand new realm.
  2785. :param realm: Realm name
  2786. :type realm: str
  2787. :param admin: Keycloak admin
  2788. :type admin: KeycloakAdmin
  2789. """
  2790. admin.change_current_realm(realm)
  2791. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  2792. assert (
  2793. len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"])
  2794. == 1
  2795. )
  2796. def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  2797. """
  2798. Test getter for the ID of the default realm role.
  2799. :param realm: Realm name
  2800. :type realm: str
  2801. :param admin: Keycloak admin
  2802. :type admin: KeycloakAdmin
  2803. """
  2804. admin.change_current_realm(realm)
  2805. assert admin.get_default_realm_role_id() == next(
  2806. x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"
  2807. )
  2808. def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  2809. """
  2810. Test getting, adding and deleting default realm roles.
  2811. :param realm: Realm name
  2812. :type realm: str
  2813. :param admin: Keycloak admin
  2814. :type admin: KeycloakAdmin
  2815. """
  2816. admin.change_current_realm(realm)
  2817. # Test listing all default realm roles
  2818. roles = admin.get_realm_default_roles()
  2819. assert len(roles) == 2
  2820. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  2821. admin.change_current_realm("doesnotexist")
  2822. with pytest.raises(KeycloakGetError) as err:
  2823. admin.get_realm_default_roles()
  2824. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2825. admin.change_current_realm(realm)
  2826. # Test removing a default realm role
  2827. res = admin.remove_realm_default_roles(payload=[roles[0]])
  2828. assert res == {}
  2829. assert roles[0] not in admin.get_realm_default_roles()
  2830. assert len(admin.get_realm_default_roles()) == 1
  2831. with pytest.raises(KeycloakDeleteError) as err:
  2832. admin.remove_realm_default_roles(payload=[{"id": "bad id"}])
  2833. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2834. # Test adding a default realm role
  2835. res = admin.add_realm_default_roles(payload=[roles[0]])
  2836. assert res == {}
  2837. assert roles[0] in admin.get_realm_default_roles()
  2838. assert len(admin.get_realm_default_roles()) == 2
  2839. with pytest.raises(KeycloakPostError) as err:
  2840. admin.add_realm_default_roles(payload=[{"id": "bad id"}])
  2841. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2842. def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  2843. """
  2844. Test clearing the keys cache.
  2845. :param realm: Realm name
  2846. :type realm: str
  2847. :param admin: Keycloak admin
  2848. :type admin: KeycloakAdmin
  2849. """
  2850. admin.change_current_realm(realm)
  2851. res = admin.clear_keys_cache()
  2852. assert res == {}
  2853. def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  2854. """
  2855. Test clearing the realm cache.
  2856. :param realm: Realm name
  2857. :type realm: str
  2858. :param admin: Keycloak admin
  2859. :type admin: KeycloakAdmin
  2860. """
  2861. admin.change_current_realm(realm)
  2862. res = admin.clear_realm_cache()
  2863. assert res == {}
  2864. def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  2865. """
  2866. Test clearing the user cache.
  2867. :param realm: Realm name
  2868. :type realm: str
  2869. :param admin: Keycloak admin
  2870. :type admin: KeycloakAdmin
  2871. """
  2872. admin.change_current_realm(realm)
  2873. res = admin.clear_user_cache()
  2874. assert res == {}
  2875. def test_initial_access_token(
  2876. admin: KeycloakAdmin,
  2877. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2878. ) -> None:
  2879. """
  2880. Test initial access token and client creation.
  2881. :param admin: Keycloak admin
  2882. :type admin: KeycloakAdmin
  2883. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2884. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2885. """
  2886. res = admin.create_initial_access_token(2, 3)
  2887. assert "token" in res
  2888. assert res["count"] == 2
  2889. assert res["expiration"] == 3
  2890. oid, username, password = oid_with_credentials
  2891. client = str(uuid.uuid4())
  2892. secret = str(uuid.uuid4())
  2893. res = oid.register_client(
  2894. token=res["token"],
  2895. payload={
  2896. "name": "DynamicRegisteredClient",
  2897. "clientId": client,
  2898. "enabled": True,
  2899. "publicClient": False,
  2900. "protocol": "openid-connect",
  2901. "secret": secret,
  2902. "clientAuthenticatorType": "client-secret",
  2903. },
  2904. )
  2905. assert res["clientId"] == client
  2906. new_secret = str(uuid.uuid4())
  2907. res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
  2908. assert res["secret"] == new_secret
  2909. def test_refresh_token(admin: KeycloakAdmin) -> None:
  2910. """
  2911. Test refresh token on connection even if it is expired.
  2912. :param admin: Keycloak admin
  2913. :type admin: KeycloakAdmin
  2914. """
  2915. admin.get_realms()
  2916. assert admin.connection.token is not None
  2917. admin.user_logout(admin.get_user_id(admin.connection.username))
  2918. admin.connection.refresh_token()
  2919. # async function start
  2920. @pytest.mark.asyncio
  2921. async def test_a_realms(admin: KeycloakAdmin) -> None:
  2922. """
  2923. Test realms.
  2924. :param admin: Keycloak Admin client
  2925. :type admin: KeycloakAdmin
  2926. """
  2927. # Get realms
  2928. realms = await admin.a_get_realms()
  2929. assert len(realms) == 1, realms
  2930. assert realms[0]["realm"] == "master"
  2931. # Create a test realm
  2932. res = await admin.a_create_realm(payload={"realm": "test"})
  2933. assert res == b"", res
  2934. # Create the same realm, should fail
  2935. with pytest.raises(KeycloakPostError) as err:
  2936. res = await admin.a_create_realm(payload={"realm": "test"})
  2937. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  2938. # Create the same realm, skip_exists true
  2939. res = await admin.a_create_realm(payload={"realm": "test"}, skip_exists=True)
  2940. assert res == {"msg": "Already exists"}, res
  2941. # Get a single realm
  2942. res = await admin.a_get_realm(realm_name="test")
  2943. assert res["realm"] == "test"
  2944. # Get non-existing realm
  2945. with pytest.raises(KeycloakGetError) as err:
  2946. await admin.a_get_realm(realm_name="non-existent")
  2947. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  2948. # Update realm
  2949. res = await admin.a_update_realm(realm_name="test", payload={"accountTheme": "test"})
  2950. assert res == {}, res
  2951. # Check that the update worked
  2952. res = await admin.a_get_realm(realm_name="test")
  2953. assert res["realm"] == "test"
  2954. assert res["accountTheme"] == "test"
  2955. # Update wrong payload
  2956. with pytest.raises(KeycloakPutError) as err:
  2957. await admin.a_update_realm(realm_name="test", payload={"wrong": "payload"})
  2958. assert err.match("Unrecognized field")
  2959. # Check that get realms returns both realms
  2960. realms = await admin.a_get_realms()
  2961. realm_names = [x["realm"] for x in realms]
  2962. assert len(realms) == 2, realms
  2963. assert "master" in realm_names, realm_names
  2964. assert "test" in realm_names, realm_names
  2965. # Get users profile, add an attribute and check
  2966. user_profile = await admin.a_get_realm_users_profile()
  2967. assert "attributes" in user_profile
  2968. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  2969. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  2970. ) >= Version("24"):
  2971. new_attribute = {
  2972. "name": "nickname",
  2973. "displayName": "",
  2974. "validations": {},
  2975. "annotations": {},
  2976. "permissions": {"view": [], "edit": ["admin"]},
  2977. "multivalued": False,
  2978. }
  2979. user_profile["attributes"].append(new_attribute)
  2980. res = await admin.a_update_realm_users_profile(user_profile)
  2981. # Check for new attribute in result
  2982. assert "nickname" in [x["name"] for x in res["attributes"]]
  2983. # Delete the realm
  2984. res = await admin.a_delete_realm(realm_name="test")
  2985. assert res == {}, res
  2986. # Check that the realm does not exist anymore
  2987. with pytest.raises(KeycloakGetError) as err:
  2988. await admin.a_get_realm(realm_name="test")
  2989. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2990. # Delete non-existing realm
  2991. with pytest.raises(KeycloakDeleteError) as err:
  2992. await admin.a_delete_realm(realm_name="non-existent")
  2993. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2994. @pytest.mark.asyncio
  2995. async def test_a_changing_of_realms(admin: KeycloakAdmin, realm: str) -> None:
  2996. """
  2997. Test changing of realms.
  2998. :param admin: Keycloak Admin client
  2999. :type admin: KeycloakAdmin
  3000. :param realm: Keycloak realm
  3001. :type realm: str
  3002. """
  3003. assert await admin.a_get_current_realm() == "master"
  3004. await admin.a_change_current_realm(realm)
  3005. assert await admin.a_get_current_realm() == realm
  3006. @pytest.mark.asyncio
  3007. async def test_a_import_export_realms(admin: KeycloakAdmin, realm: str) -> None:
  3008. """
  3009. Test import and export of realms.
  3010. :param admin: Keycloak Admin client
  3011. :type admin: KeycloakAdmin
  3012. :param realm: Keycloak realm
  3013. :type realm: str
  3014. """
  3015. await admin.a_change_current_realm(realm)
  3016. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=True)
  3017. assert realm_export != {}, realm_export
  3018. await admin.a_delete_realm(realm_name=realm)
  3019. admin.realm_name = "master"
  3020. res = await admin.a_import_realm(payload=realm_export)
  3021. assert res == b"", res
  3022. # Test bad import
  3023. with pytest.raises(KeycloakPostError) as err:
  3024. await admin.a_import_realm(payload={})
  3025. assert err.match(
  3026. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'', # noqa: E501
  3027. )
  3028. @pytest.mark.asyncio
  3029. async def test_a_partial_import_realm(admin: KeycloakAdmin, realm: str) -> None:
  3030. """
  3031. Test partial import of realm configuration.
  3032. :param admin: Keycloak Admin client
  3033. :type admin: KeycloakAdmin
  3034. :param realm: Keycloak realm
  3035. :type realm: str
  3036. """
  3037. test_realm_role = str(uuid.uuid4())
  3038. test_user = str(uuid.uuid4())
  3039. test_client = str(uuid.uuid4())
  3040. await admin.a_change_current_realm(realm)
  3041. client_id = await admin.a_create_client(payload={"name": test_client, "clientId": test_client})
  3042. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=False)
  3043. client_config = next(
  3044. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  3045. )
  3046. # delete before partial import
  3047. await admin.a_delete_client(client_id)
  3048. payload = {
  3049. "ifResourceExists": "SKIP",
  3050. "id": realm_export["id"],
  3051. "realm": realm,
  3052. "clients": [client_config],
  3053. "roles": {"realm": [{"name": test_realm_role}]},
  3054. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  3055. }
  3056. # check add
  3057. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3058. assert res["added"] == 3
  3059. # check skip
  3060. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3061. assert res["skipped"] == 3
  3062. # check overwrite
  3063. payload["ifResourceExists"] = "OVERWRITE"
  3064. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3065. assert res["overwritten"] == 3
  3066. @pytest.mark.asyncio
  3067. async def test_a_users(admin: KeycloakAdmin, realm: str) -> None:
  3068. """
  3069. Test users.
  3070. :param admin: Keycloak Admin client
  3071. :type admin: KeycloakAdmin
  3072. :param realm: Keycloak realm
  3073. :type realm: str
  3074. """
  3075. await admin.a_change_current_realm(realm)
  3076. # Check no users present
  3077. users = await admin.a_get_users()
  3078. assert users == [], users
  3079. # Test create user
  3080. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3081. assert user_id is not None, user_id
  3082. # Test create the same user
  3083. with pytest.raises(KeycloakPostError) as err:
  3084. await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3085. assert err.match(".*User exists with same.*")
  3086. # Test create the same user, exists_ok true
  3087. user_id_2 = await admin.a_create_user(
  3088. payload={"username": "test", "email": "test@test.test"},
  3089. exist_ok=True,
  3090. )
  3091. assert user_id == user_id_2
  3092. # Test get user
  3093. user = await admin.a_get_user(user_id=user_id)
  3094. assert user["username"] == "test", user["username"]
  3095. assert user["email"] == "test@test.test", user["email"]
  3096. # Test update user
  3097. res = await admin.a_update_user(user_id=user_id, payload={"firstName": "Test"})
  3098. assert res == {}, res
  3099. user = await admin.a_get_user(user_id=user_id)
  3100. assert user["firstName"] == "Test"
  3101. # Test update user fail
  3102. with pytest.raises(KeycloakPutError) as err:
  3103. await admin.a_update_user(user_id=user_id, payload={"wrong": "payload"})
  3104. assert err.match("Unrecognized field")
  3105. # Test disable user
  3106. res = await admin.a_disable_user(user_id=user_id)
  3107. assert res == {}, res
  3108. assert not (await admin.a_get_user(user_id=user_id))["enabled"]
  3109. # Test enable user
  3110. res = await admin.a_enable_user(user_id=user_id)
  3111. assert res == {}, res
  3112. assert (await admin.a_get_user(user_id=user_id))["enabled"]
  3113. # Test get users again
  3114. users = await admin.a_get_users()
  3115. usernames = [x["username"] for x in users]
  3116. assert "test" in usernames
  3117. # Test users counts
  3118. count = await admin.a_users_count()
  3119. assert count == 1, count
  3120. # Test users count with query
  3121. count = await admin.a_users_count(query={"username": "notpresent"})
  3122. assert count == 0
  3123. # Test user groups
  3124. groups = await admin.a_get_user_groups(user_id=user["id"])
  3125. assert len(groups) == 0
  3126. # Test user groups bad id
  3127. with pytest.raises(KeycloakGetError) as err:
  3128. await admin.a_get_user_groups(user_id="does-not-exist")
  3129. assert err.match(USER_NOT_FOUND_REGEX)
  3130. # Test logout
  3131. res = await admin.a_user_logout(user_id=user["id"])
  3132. assert res == {}, res
  3133. # Test logout fail
  3134. with pytest.raises(KeycloakPostError) as err:
  3135. await admin.a_user_logout(user_id="non-existent-id")
  3136. assert err.match(USER_NOT_FOUND_REGEX)
  3137. # Test consents
  3138. res = await admin.a_user_consents(user_id=user["id"])
  3139. assert len(res) == 0, res
  3140. # Test consents fail
  3141. with pytest.raises(KeycloakGetError) as err:
  3142. await admin.a_user_consents(user_id="non-existent-id")
  3143. assert err.match(USER_NOT_FOUND_REGEX)
  3144. # Test delete user
  3145. res = await admin.a_delete_user(user_id=user_id)
  3146. assert res == {}, res
  3147. with pytest.raises(KeycloakGetError) as err:
  3148. await admin.a_get_user(user_id=user_id)
  3149. err.match(USER_NOT_FOUND_REGEX)
  3150. # Test delete fail
  3151. with pytest.raises(KeycloakDeleteError) as err:
  3152. await admin.a_delete_user(user_id="non-existent-id")
  3153. assert err.match(USER_NOT_FOUND_REGEX)
  3154. @pytest.mark.asyncio
  3155. async def test_a_enable_disable_all_users(admin: KeycloakAdmin, realm: str) -> None:
  3156. """
  3157. Test enable and disable all users.
  3158. :param admin: Keycloak Admin client
  3159. :type admin: KeycloakAdmin
  3160. :param realm: Keycloak realm
  3161. :type realm: str
  3162. """
  3163. admin.change_current_realm(realm)
  3164. user_id_1 = await admin.a_create_user(
  3165. payload={"username": "test", "email": "test@test.test", "enabled": True},
  3166. )
  3167. user_id_2 = await admin.a_create_user(
  3168. payload={"username": "test2", "email": "test2@test.test", "enabled": True},
  3169. )
  3170. user_id_3 = await admin.a_create_user(
  3171. payload={"username": "test3", "email": "test3@test.test", "enabled": True},
  3172. )
  3173. assert (await admin.a_get_user(user_id_1))["enabled"]
  3174. assert (await admin.a_get_user(user_id_2))["enabled"]
  3175. assert (await admin.a_get_user(user_id_3))["enabled"]
  3176. await admin.a_disable_all_users()
  3177. assert not (await admin.a_get_user(user_id_1))["enabled"]
  3178. assert not (await admin.a_get_user(user_id_2))["enabled"]
  3179. assert not (await admin.a_get_user(user_id_3))["enabled"]
  3180. await admin.a_enable_all_users()
  3181. assert (await admin.a_get_user(user_id_1))["enabled"]
  3182. assert (await admin.a_get_user(user_id_2))["enabled"]
  3183. assert (await admin.a_get_user(user_id_3))["enabled"]
  3184. @pytest.mark.asyncio
  3185. async def test_a_users_roles(admin: KeycloakAdmin, realm: str) -> None:
  3186. """
  3187. Test users roles.
  3188. :param admin: Keycloak Admin client
  3189. :type admin: KeycloakAdmin
  3190. :param realm: Keycloak realm
  3191. :type realm: str
  3192. """
  3193. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3194. # Test all level user roles
  3195. client_id = await admin.a_create_client(
  3196. payload={"name": "test-client", "clientId": "test-client"},
  3197. )
  3198. await admin.a_create_client_role(client_role_id=client_id, payload={"name": "test-role"})
  3199. await admin.a_assign_client_role(
  3200. client_id=client_id,
  3201. user_id=user_id,
  3202. roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
  3203. )
  3204. all_roles = await admin.a_get_all_roles_of_user(user_id=user_id)
  3205. realm_roles = all_roles["realmMappings"]
  3206. assert len(realm_roles) == 1, realm_roles
  3207. client_roles = all_roles["clientMappings"]
  3208. assert len(client_roles) == 1, client_roles
  3209. # Test all level user roles fail
  3210. with pytest.raises(KeycloakGetError) as err:
  3211. await admin.a_get_all_roles_of_user(user_id="non-existent-id")
  3212. err.match('404: b\'{"error":"User not found"')
  3213. await admin.a_delete_user(user_id)
  3214. await admin.a_delete_client(client_id)
  3215. @pytest.mark.asyncio
  3216. async def test_a_users_pagination(admin: KeycloakAdmin, realm: str) -> None:
  3217. """
  3218. Test user pagination.
  3219. :param admin: Keycloak Admin client
  3220. :type admin: KeycloakAdmin
  3221. :param realm: Keycloak realm
  3222. :type realm: str
  3223. """
  3224. await admin.a_change_current_realm(realm)
  3225. for ind in range(admin.PAGE_SIZE + 50):
  3226. username = f"user_{ind}"
  3227. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  3228. users = await admin.a_get_users()
  3229. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  3230. users = await admin.a_get_users(query={"first": 100})
  3231. assert len(users) == 50, len(users)
  3232. users = await admin.a_get_users(query={"max": 20})
  3233. assert len(users) == 20, len(users)
  3234. @pytest.mark.asyncio
  3235. async def test_a_user_groups_pagination(admin: KeycloakAdmin, realm: str) -> None:
  3236. """
  3237. Test user groups pagination.
  3238. :param admin: Keycloak Admin client
  3239. :type admin: KeycloakAdmin
  3240. :param realm: Keycloak realm
  3241. :type realm: str
  3242. """
  3243. await admin.a_change_current_realm(realm)
  3244. user_id = await admin.a_create_user(
  3245. payload={"username": "username_1", "email": "username_1@test.test"},
  3246. )
  3247. for ind in range(admin.PAGE_SIZE + 50):
  3248. group_name = f"group_{ind}"
  3249. group_id = await admin.a_create_group(payload={"name": group_name})
  3250. await admin.a_group_user_add(user_id=user_id, group_id=group_id)
  3251. groups = await admin.a_get_user_groups(user_id=user_id)
  3252. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  3253. groups = await admin.a_get_user_groups(
  3254. user_id=user_id,
  3255. query={"first": 100, "max": -1, "search": ""},
  3256. )
  3257. assert len(groups) == 50, len(groups)
  3258. groups = await admin.a_get_user_groups(
  3259. user_id=user_id,
  3260. query={"max": 20, "first": -1, "search": ""},
  3261. )
  3262. assert len(groups) == 20, len(groups)
  3263. @pytest.mark.asyncio
  3264. async def test_a_idps(admin: KeycloakAdmin, realm: str) -> None:
  3265. """
  3266. Test IDPs.
  3267. :param admin: Keycloak Admin client
  3268. :type admin: KeycloakAdmin
  3269. :param realm: Keycloak realm
  3270. :type realm: str
  3271. """
  3272. await admin.a_change_current_realm(realm)
  3273. # Create IDP
  3274. res = await admin.a_create_idp(
  3275. payload={
  3276. "providerId": "github",
  3277. "alias": "github",
  3278. "config": {"clientId": "test", "clientSecret": "test"},
  3279. },
  3280. )
  3281. assert res == b"", res
  3282. # Test create idp fail
  3283. with pytest.raises(KeycloakPostError) as err:
  3284. await admin.a_create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  3285. assert err.match("Invalid identity provider id"), err
  3286. # Test listing
  3287. idps = await admin.a_get_idps()
  3288. assert len(idps) == 1
  3289. assert idps[0]["alias"] == "github"
  3290. # Test get idp
  3291. idp = await admin.a_get_idp("github")
  3292. assert idp["alias"] == "github"
  3293. assert idp.get("config")
  3294. assert idp["config"]["clientId"] == "test"
  3295. assert idp["config"]["clientSecret"] == "**********"
  3296. # Test get idp fail
  3297. with pytest.raises(KeycloakGetError) as err:
  3298. await admin.a_get_idp("does-not-exist")
  3299. assert err.match(HTTP_404_REGEX)
  3300. # Test IdP update
  3301. res = await admin.a_update_idp(idp_alias="github", payload=idps[0])
  3302. assert res == {}, res
  3303. # Test adding a mapper
  3304. res = await admin.a_add_mapper_to_idp(
  3305. idp_alias="github",
  3306. payload={
  3307. "identityProviderAlias": "github",
  3308. "identityProviderMapper": "github-user-attribute-mapper",
  3309. "name": "test",
  3310. },
  3311. )
  3312. assert res == b"", res
  3313. # Test mapper fail
  3314. with pytest.raises(KeycloakPostError) as err:
  3315. await admin.a_add_mapper_to_idp(idp_alias="does-no-texist", payload={})
  3316. assert err.match(HTTP_404_REGEX)
  3317. # Test IdP mappers listing
  3318. idp_mappers = await admin.a_get_idp_mappers(idp_alias="github")
  3319. assert len(idp_mappers) == 1
  3320. # Test IdP mapper update
  3321. res = await admin.a_update_mapper_in_idp(
  3322. idp_alias="github",
  3323. mapper_id=idp_mappers[0]["id"],
  3324. # For an obscure reason, keycloak expect all fields
  3325. payload={
  3326. "id": idp_mappers[0]["id"],
  3327. "identityProviderAlias": "github-alias",
  3328. "identityProviderMapper": "github-user-attribute-mapper",
  3329. "name": "test",
  3330. "config": idp_mappers[0]["config"],
  3331. },
  3332. )
  3333. assert res == {}, res
  3334. # Test delete
  3335. res = await admin.a_delete_idp(idp_alias="github")
  3336. assert res == {}, res
  3337. # Test delete fail
  3338. with pytest.raises(KeycloakDeleteError) as err:
  3339. await admin.a_delete_idp(idp_alias="does-not-exist")
  3340. assert err.match(HTTP_404_REGEX)
  3341. @pytest.mark.asyncio
  3342. async def test_a_user_credentials(admin: KeycloakAdmin, user: str) -> None:
  3343. """
  3344. Test user credentials.
  3345. :param admin: Keycloak Admin client
  3346. :type admin: KeycloakAdmin
  3347. :param user: Keycloak user
  3348. :type user: str
  3349. """
  3350. res = await admin.a_set_user_password(user_id=user, password="booya", temporary=True) # noqa: S106
  3351. assert res == {}, res
  3352. # Test user password set fail
  3353. with pytest.raises(KeycloakPutError) as err:
  3354. await admin.a_set_user_password(user_id="does-not-exist", password="")
  3355. assert err.match(USER_NOT_FOUND_REGEX)
  3356. credentials = await admin.a_get_credentials(user_id=user)
  3357. assert len(credentials) == 1
  3358. assert credentials[0]["type"] == "password", credentials
  3359. # Test get credentials fail
  3360. with pytest.raises(KeycloakGetError) as err:
  3361. await admin.a_get_credentials(user_id="does-not-exist")
  3362. assert err.match(USER_NOT_FOUND_REGEX)
  3363. res = await admin.a_delete_credential(user_id=user, credential_id=credentials[0]["id"])
  3364. assert res == {}, res
  3365. # Test delete fail
  3366. with pytest.raises(KeycloakDeleteError) as err:
  3367. await admin.a_delete_credential(user_id=user, credential_id="does-not-exist")
  3368. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  3369. @pytest.mark.asyncio
  3370. async def test_a_social_logins(admin: KeycloakAdmin, user: str) -> None:
  3371. """
  3372. Test social logins.
  3373. :param admin: Keycloak Admin client
  3374. :type admin: KeycloakAdmin
  3375. :param user: Keycloak user
  3376. :type user: str
  3377. """
  3378. res = await admin.a_add_user_social_login(
  3379. user_id=user,
  3380. provider_id="gitlab",
  3381. provider_userid="test",
  3382. provider_username="test",
  3383. )
  3384. assert res == {}, res
  3385. await admin.a_add_user_social_login(
  3386. user_id=user,
  3387. provider_id="github",
  3388. provider_userid="test",
  3389. provider_username="test",
  3390. )
  3391. assert res == {}, res
  3392. # Test add social login fail
  3393. with pytest.raises(KeycloakPostError) as err:
  3394. await admin.a_add_user_social_login(
  3395. user_id="does-not-exist",
  3396. provider_id="does-not-exist",
  3397. provider_userid="test",
  3398. provider_username="test",
  3399. )
  3400. assert err.match(USER_NOT_FOUND_REGEX)
  3401. res = await admin.a_get_user_social_logins(user_id=user)
  3402. assert res == [], res
  3403. # Test get social logins fail
  3404. with pytest.raises(KeycloakGetError) as err:
  3405. await admin.a_get_user_social_logins(user_id="does-not-exist")
  3406. assert err.match(USER_NOT_FOUND_REGEX)
  3407. res = await admin.a_delete_user_social_login(user_id=user, provider_id="gitlab")
  3408. assert res == {}, res
  3409. res = await admin.a_delete_user_social_login(user_id=user, provider_id="github")
  3410. assert res == {}, res
  3411. with pytest.raises(KeycloakDeleteError) as err:
  3412. await admin.a_delete_user_social_login(user_id=user, provider_id="instagram")
  3413. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  3414. @pytest.mark.asyncio
  3415. async def test_a_server_info(admin: KeycloakAdmin) -> None:
  3416. """
  3417. Test server info.
  3418. :param admin: Keycloak Admin client
  3419. :type admin: KeycloakAdmin
  3420. """
  3421. info = await admin.a_get_server_info()
  3422. assert set(info.keys()).issubset(
  3423. {
  3424. "systemInfo",
  3425. "memoryInfo",
  3426. "profileInfo",
  3427. "features",
  3428. "themes",
  3429. "socialProviders",
  3430. "identityProviders",
  3431. "providers",
  3432. "protocolMapperTypes",
  3433. "builtinProtocolMappers",
  3434. "clientInstallations",
  3435. "componentTypes",
  3436. "passwordPolicies",
  3437. "enums",
  3438. "cryptoInfo",
  3439. },
  3440. ), info.keys()
  3441. @pytest.mark.asyncio
  3442. async def test_a_groups(admin: KeycloakAdmin, user: str) -> None:
  3443. """
  3444. Test groups.
  3445. :param admin: Keycloak Admin client
  3446. :type admin: KeycloakAdmin
  3447. :param user: Keycloak user
  3448. :type user: str
  3449. """
  3450. # Test get groups
  3451. groups = await admin.a_get_groups()
  3452. assert len(groups) == 0
  3453. # Test create group
  3454. group_id = await admin.a_create_group(payload={"name": "main-group"})
  3455. assert group_id is not None, group_id
  3456. # Test group count
  3457. count = await admin.a_groups_count()
  3458. assert count.get("count") == 1, count
  3459. # Test group count with query
  3460. count = await admin.a_groups_count(query={"search": "notpresent"})
  3461. assert count.get("count") == 0
  3462. # Test create subgroups
  3463. subgroup_id_1 = await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3464. subgroup_id_2 = await admin.a_create_group(payload={"name": "subgroup-2"}, parent=group_id)
  3465. # Test create group fail
  3466. with pytest.raises(KeycloakPostError) as err:
  3467. await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3468. assert err.match("409"), err
  3469. # Test skip exists OK
  3470. subgroup_id_1_eq = await admin.a_create_group(
  3471. payload={"name": "subgroup-1"},
  3472. parent=group_id,
  3473. skip_exists=True,
  3474. )
  3475. assert subgroup_id_1_eq is None
  3476. # Test get groups again
  3477. groups = await admin.a_get_groups()
  3478. assert len(groups) == 1, groups
  3479. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3480. assert groups[0]["id"] == group_id
  3481. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3482. # Test get groups query
  3483. groups = await admin.a_get_groups(query={"max": 10})
  3484. assert len(groups) == 1, groups
  3485. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3486. assert groups[0]["id"] == group_id
  3487. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3488. # Test get group
  3489. res = await admin.a_get_group(group_id=subgroup_id_1)
  3490. assert res["id"] == subgroup_id_1, res
  3491. assert res["name"] == "subgroup-1"
  3492. assert res["path"] == "/main-group/subgroup-1"
  3493. # Test get group fail
  3494. with pytest.raises(KeycloakGetError) as err:
  3495. await admin.a_get_group(group_id="does-not-exist")
  3496. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3497. # Create 1 more subgroup
  3498. subsubgroup_id_1 = await admin.a_create_group(
  3499. payload={"name": "subsubgroup-1"},
  3500. parent=subgroup_id_2,
  3501. )
  3502. main_group = await admin.a_get_group(group_id=group_id)
  3503. # Test nested searches
  3504. subgroup_2 = await admin.a_get_group(group_id=subgroup_id_2)
  3505. res = await admin.a_get_subgroups(
  3506. group=subgroup_2,
  3507. path="/main-group/subgroup-2/subsubgroup-1",
  3508. )
  3509. assert res is not None, res
  3510. assert res["id"] == subsubgroup_id_1
  3511. # Test nested search from main group
  3512. res = await admin.a_get_subgroups(
  3513. group=await admin.a_get_group(group_id=group_id, full_hierarchy=True),
  3514. path="/main-group/subgroup-2/subsubgroup-1",
  3515. )
  3516. assert res["id"] == subsubgroup_id_1
  3517. # Test nested search from all groups
  3518. res = await admin.a_get_groups(full_hierarchy=True)
  3519. assert len(res) == 1
  3520. assert len(res[0]["subGroups"]) == 2
  3521. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1)["subGroups"]) == 0
  3522. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2)["subGroups"]) == 1
  3523. # Test that query params are not allowed for full hierarchy
  3524. with pytest.raises(ValueError) as err:
  3525. await admin.a_get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
  3526. # Test that query params are passed
  3527. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  3528. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  3529. ) >= Version("23"):
  3530. res = await admin.a_get_group_children(group_id=group_id, query={"max": 1})
  3531. assert len(res) == 1
  3532. assert err.match("Cannot use both query and full_hierarchy parameters")
  3533. main_group_id_2 = await admin.a_create_group(payload={"name": "main-group-2"})
  3534. assert len(await admin.a_get_groups(full_hierarchy=True)) == 2
  3535. # Test empty search
  3536. res = await admin.a_get_subgroups(group=main_group, path="/none")
  3537. assert res is None, res
  3538. # Test get group by path
  3539. res = await admin.a_get_group_by_path(path="/main-group/subgroup-1")
  3540. assert res is not None, res
  3541. assert res["id"] == subgroup_id_1, res
  3542. res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  3543. assert res["error"] == "Group path does not exist"
  3544. res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  3545. assert res is not None, res
  3546. assert res["id"] == subsubgroup_id_1
  3547. res = await admin.a_get_group_by_path(path="/main-group")
  3548. assert res is not None, res
  3549. assert res["id"] == group_id, res
  3550. # Test group members
  3551. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3552. assert len(res) == 0, res
  3553. # Test fail group members
  3554. with pytest.raises(KeycloakGetError) as err:
  3555. await admin.a_get_group_members(group_id="does-not-exist")
  3556. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  3557. res = await admin.a_group_user_add(user_id=user, group_id=subgroup_id_2)
  3558. assert res == {}, res
  3559. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3560. assert len(res) == 1, res
  3561. assert res[0]["id"] == user
  3562. # Test get group members query
  3563. res = await admin.a_get_group_members(group_id=subgroup_id_2, query={"max": 10})
  3564. assert len(res) == 1, res
  3565. assert res[0]["id"] == user
  3566. with pytest.raises(KeycloakDeleteError) as err:
  3567. await admin.a_group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  3568. assert err.match(USER_NOT_FOUND_REGEX), err
  3569. res = await admin.a_group_user_remove(user_id=user, group_id=subgroup_id_2)
  3570. assert res == {}, res
  3571. # Test set permissions
  3572. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=True)
  3573. assert res["enabled"], res
  3574. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=False)
  3575. assert not res["enabled"], res
  3576. with pytest.raises(KeycloakPutError) as err:
  3577. await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  3578. assert err.match(UNKOWN_ERROR_REGEX), err
  3579. # Test update group
  3580. res = await admin.a_update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  3581. assert res == {}, res
  3582. assert (await admin.a_get_group(group_id=subgroup_id_2))["name"] == "new-subgroup-2"
  3583. # test update fail
  3584. with pytest.raises(KeycloakPutError) as err:
  3585. await admin.a_update_group(group_id="does-not-exist", payload={})
  3586. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3587. # Test delete
  3588. res = await admin.a_delete_group(group_id=group_id)
  3589. assert res == {}, res
  3590. res = await admin.a_delete_group(group_id=main_group_id_2)
  3591. assert res == {}, res
  3592. assert len(await admin.a_get_groups()) == 0
  3593. # Test delete fail
  3594. with pytest.raises(KeycloakDeleteError) as err:
  3595. await admin.a_delete_group(group_id="does-not-exist")
  3596. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3597. @pytest.mark.asyncio
  3598. async def test_a_clients(admin: KeycloakAdmin, realm: str) -> None:
  3599. """
  3600. Test clients.
  3601. :param admin: Keycloak Admin client
  3602. :type admin: KeycloakAdmin
  3603. :param realm: Keycloak realm
  3604. :type realm: str
  3605. """
  3606. await admin.a_change_current_realm(realm)
  3607. # Test get clients
  3608. clients = await admin.a_get_clients()
  3609. assert len(clients) == 6, clients
  3610. assert {x["name"] for x in clients} == {
  3611. "${client_admin-cli}",
  3612. "${client_security-admin-console}",
  3613. "${client_account-console}",
  3614. "${client_broker}",
  3615. "${client_account}",
  3616. "${client_realm-management}",
  3617. }, clients
  3618. # Test create client
  3619. client_id = await admin.a_create_client(
  3620. payload={"name": "test-client", "clientId": "test-client"},
  3621. )
  3622. assert client_id, client_id
  3623. with pytest.raises(KeycloakPostError) as err:
  3624. await admin.a_create_client(payload={"name": "test-client", "clientId": "test-client"})
  3625. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  3626. client_id_2 = await admin.a_create_client(
  3627. payload={"name": "test-client", "clientId": "test-client"},
  3628. skip_exists=True,
  3629. )
  3630. assert client_id == client_id_2, client_id_2
  3631. # Test get client
  3632. res = await admin.a_get_client(client_id=client_id)
  3633. assert res["clientId"] == "test-client", res
  3634. assert res["name"] == "test-client", res
  3635. assert res["id"] == client_id, res
  3636. with pytest.raises(KeycloakGetError) as err:
  3637. await admin.a_get_client(client_id="does-not-exist")
  3638. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3639. assert len(await admin.a_get_clients()) == 7
  3640. # Test get client id
  3641. assert await admin.a_get_client_id(client_id="test-client") == client_id
  3642. assert await admin.a_get_client_id(client_id="does-not-exist") is None
  3643. # Test update client
  3644. res = await admin.a_update_client(client_id=client_id, payload={"name": "test-client-change"})
  3645. assert res == {}, res
  3646. with pytest.raises(KeycloakPutError) as err:
  3647. await admin.a_update_client(
  3648. client_id="does-not-exist",
  3649. payload={"name": "test-client-change"},
  3650. )
  3651. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3652. # Test client mappers
  3653. res = await admin.a_get_mappers_from_client(client_id=client_id)
  3654. assert len(res) == 0
  3655. with pytest.raises(KeycloakPostError) as err:
  3656. await admin.a_add_mapper_to_client(client_id="does-not-exist", payload={})
  3657. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3658. res = await admin.a_add_mapper_to_client(
  3659. client_id=client_id,
  3660. payload={
  3661. "name": "test-mapper",
  3662. "protocol": "openid-connect",
  3663. "protocolMapper": "oidc-usermodel-attribute-mapper",
  3664. },
  3665. )
  3666. assert res == b""
  3667. assert len(await admin.a_get_mappers_from_client(client_id=client_id)) == 1
  3668. mapper = (await admin.a_get_mappers_from_client(client_id=client_id))[0]
  3669. with pytest.raises(KeycloakPutError) as err:
  3670. await admin.a_update_client_mapper(
  3671. client_id=client_id,
  3672. mapper_id="does-not-exist",
  3673. payload={},
  3674. )
  3675. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3676. mapper["config"]["user.attribute"] = "test"
  3677. res = await admin.a_update_client_mapper(
  3678. client_id=client_id,
  3679. mapper_id=mapper["id"],
  3680. payload=mapper,
  3681. )
  3682. assert res == {}
  3683. res = await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3684. assert res == {}
  3685. with pytest.raises(KeycloakDeleteError) as err:
  3686. await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3687. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3688. # Test client sessions
  3689. with pytest.raises(KeycloakGetError) as err:
  3690. await admin.a_get_client_all_sessions(client_id="does-not-exist")
  3691. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3692. assert await admin.a_get_client_all_sessions(client_id=client_id) == []
  3693. assert await admin.a_get_client_sessions_stats() == []
  3694. # Test authz
  3695. auth_client_id = await admin.a_create_client(
  3696. payload={
  3697. "name": "authz-client",
  3698. "clientId": "authz-client",
  3699. "authorizationServicesEnabled": True,
  3700. "serviceAccountsEnabled": True,
  3701. },
  3702. )
  3703. res = await admin.a_get_client_authz_settings(client_id=auth_client_id)
  3704. assert res["allowRemoteResourceManagement"]
  3705. assert res["decisionStrategy"] == "UNANIMOUS"
  3706. assert len(res["policies"]) >= 0
  3707. with pytest.raises(KeycloakGetError) as err:
  3708. await admin.a_get_client_authz_settings(client_id=client_id)
  3709. assert err.match(HTTP_404_REGEX)
  3710. # Authz resources
  3711. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3712. assert len(res) == 1
  3713. assert res[0]["name"] == "Default Resource"
  3714. with pytest.raises(KeycloakGetError) as err:
  3715. await admin.a_get_client_authz_resources(client_id=client_id)
  3716. assert err.match(HTTP_404_REGEX)
  3717. res = await admin.a_create_client_authz_resource(
  3718. client_id=auth_client_id,
  3719. payload={"name": "test-resource"},
  3720. )
  3721. assert res["name"] == "test-resource", res
  3722. test_resource_id = res["_id"]
  3723. res = await admin.a_get_client_authz_resource(
  3724. client_id=auth_client_id,
  3725. resource_id=test_resource_id,
  3726. )
  3727. assert res["_id"] == test_resource_id, res
  3728. assert res["name"] == "test-resource", res
  3729. with pytest.raises(KeycloakPostError) as err:
  3730. await admin.a_create_client_authz_resource(
  3731. client_id=auth_client_id,
  3732. payload={"name": "test-resource"},
  3733. )
  3734. assert err.match('409: b\'{"error":"invalid_request"')
  3735. assert await admin.a_create_client_authz_resource(
  3736. client_id=auth_client_id,
  3737. payload={"name": "test-resource"},
  3738. skip_exists=True,
  3739. ) == {"msg": "Already exists"}
  3740. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3741. assert len(res) == 2
  3742. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  3743. res = await admin.a_create_client_authz_resource(
  3744. client_id=auth_client_id,
  3745. payload={"name": "temp-resource"},
  3746. )
  3747. assert res["name"] == "temp-resource", res
  3748. temp_resource_id: str = res["_id"]
  3749. # Test update authz resources
  3750. await admin.a_update_client_authz_resource(
  3751. client_id=auth_client_id,
  3752. resource_id=temp_resource_id,
  3753. payload={"name": "temp-updated-resource"},
  3754. )
  3755. res = await admin.a_get_client_authz_resource(
  3756. client_id=auth_client_id,
  3757. resource_id=temp_resource_id,
  3758. )
  3759. assert res["name"] == "temp-updated-resource", res
  3760. with pytest.raises(KeycloakPutError) as err:
  3761. await admin.a_update_client_authz_resource(
  3762. client_id=auth_client_id,
  3763. resource_id="invalid_resource_id",
  3764. payload={"name": "temp-updated-resource"},
  3765. )
  3766. assert err.match("404: b''"), err
  3767. await admin.a_delete_client_authz_resource(
  3768. client_id=auth_client_id,
  3769. resource_id=temp_resource_id,
  3770. )
  3771. with pytest.raises(KeycloakGetError) as err:
  3772. await admin.a_get_client_authz_resource(
  3773. client_id=auth_client_id,
  3774. resource_id=temp_resource_id,
  3775. )
  3776. assert err.match("404: b''")
  3777. # Authz policies
  3778. res = await admin.a_get_client_authz_policies(client_id=auth_client_id)
  3779. assert len(res) == 1, res
  3780. assert res[0]["name"] == "Default Policy"
  3781. with pytest.raises(KeycloakGetError) as err:
  3782. await admin.a_get_client_authz_policies(client_id="does-not-exist")
  3783. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3784. role_id = (await admin.a_get_realm_role(role_name="offline_access"))["id"]
  3785. res = await admin.a_create_client_authz_role_based_policy(
  3786. client_id=auth_client_id,
  3787. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3788. )
  3789. assert res["name"] == "test-authz-rb-policy", res
  3790. with pytest.raises(KeycloakPostError) as err:
  3791. await admin.a_create_client_authz_role_based_policy(
  3792. client_id=auth_client_id,
  3793. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3794. )
  3795. assert err.match('409: b\'{"error":"Policy with name')
  3796. assert await admin.a_create_client_authz_role_based_policy(
  3797. client_id=auth_client_id,
  3798. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3799. skip_exists=True,
  3800. ) == {"msg": "Already exists"}
  3801. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 2
  3802. role_based_policy_id = res["id"]
  3803. role_based_policy_name = res["name"]
  3804. res = await admin.a_create_client_authz_role_based_policy(
  3805. client_id=auth_client_id,
  3806. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  3807. )
  3808. res2 = await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3809. assert res["id"] == res2["id"]
  3810. await admin.a_delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3811. with pytest.raises(KeycloakGetError) as err:
  3812. await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3813. assert err.match("404: b''")
  3814. res = await admin.a_create_client_authz_policy(
  3815. client_id=auth_client_id,
  3816. payload={
  3817. "name": "test-authz-policy",
  3818. "type": "time",
  3819. "config": {"hourEnd": "18", "hour": "9"},
  3820. },
  3821. )
  3822. assert res["name"] == "test-authz-policy", res
  3823. with pytest.raises(KeycloakPostError) as err:
  3824. await admin.a_create_client_authz_policy(
  3825. client_id=auth_client_id,
  3826. payload={
  3827. "name": "test-authz-policy",
  3828. "type": "time",
  3829. "config": {"hourEnd": "18", "hour": "9"},
  3830. },
  3831. )
  3832. assert err.match('409: b\'{"error":"Policy with name')
  3833. assert await admin.a_create_client_authz_policy(
  3834. client_id=auth_client_id,
  3835. payload={
  3836. "name": "test-authz-policy",
  3837. "type": "time",
  3838. "config": {"hourEnd": "18", "hour": "9"},
  3839. },
  3840. skip_exists=True,
  3841. ) == {"msg": "Already exists"}
  3842. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 3
  3843. # Test authz permissions
  3844. res = await admin.a_get_client_authz_permissions(client_id=auth_client_id)
  3845. assert len(res) == 1, res
  3846. assert res[0]["name"] == "Default Permission"
  3847. with pytest.raises(KeycloakGetError) as err:
  3848. await admin.a_get_client_authz_permissions(client_id="does-not-exist")
  3849. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3850. res = await admin.a_create_client_authz_resource_based_permission(
  3851. client_id=auth_client_id,
  3852. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3853. )
  3854. assert res, res
  3855. assert res["name"] == "test-permission-rb"
  3856. assert res["resources"] == [test_resource_id]
  3857. resource_based_permission_id = res["id"]
  3858. resource_based_permission_name = res["name"]
  3859. with pytest.raises(KeycloakPostError) as err:
  3860. await admin.a_create_client_authz_resource_based_permission(
  3861. client_id=auth_client_id,
  3862. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3863. )
  3864. assert err.match('409: b\'{"error":"Policy with name')
  3865. assert await admin.a_create_client_authz_resource_based_permission(
  3866. client_id=auth_client_id,
  3867. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3868. skip_exists=True,
  3869. ) == {"msg": "Already exists"}
  3870. assert len(await admin.a_get_client_authz_permissions(client_id=auth_client_id)) == 2
  3871. # Test associating client policy with resource based permission
  3872. res = await admin.a_update_client_authz_resource_permission(
  3873. client_id=auth_client_id,
  3874. resource_id=resource_based_permission_id,
  3875. payload={
  3876. "id": resource_based_permission_id,
  3877. "name": resource_based_permission_name,
  3878. "type": "resource",
  3879. "logic": "POSITIVE",
  3880. "decisionStrategy": "UNANIMOUS",
  3881. "resources": [test_resource_id],
  3882. "scopes": [],
  3883. "policies": [role_based_policy_id],
  3884. },
  3885. )
  3886. # Test getting associated policies for a permission
  3887. associated_policies = await admin.a_get_client_authz_permission_associated_policies(
  3888. client_id=auth_client_id,
  3889. policy_id=resource_based_permission_id,
  3890. )
  3891. assert len(associated_policies) == 1
  3892. assert associated_policies[0]["name"].startswith(role_based_policy_name)
  3893. # Test authz scopes
  3894. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3895. assert len(res) == 0, res
  3896. with pytest.raises(KeycloakGetError) as err:
  3897. await admin.a_get_client_authz_scopes(client_id=client_id)
  3898. assert err.match(HTTP_404_REGEX)
  3899. res = await admin.a_create_client_authz_scopes(
  3900. client_id=auth_client_id,
  3901. payload={"name": "test-authz-scope"},
  3902. )
  3903. assert res["name"] == "test-authz-scope", res
  3904. with pytest.raises(KeycloakPostError) as err:
  3905. await admin.a_create_client_authz_scopes(
  3906. client_id="invalid_client_id",
  3907. payload={"name": "test-authz-scope"},
  3908. )
  3909. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3910. assert await admin.a_create_client_authz_scopes(
  3911. client_id=auth_client_id,
  3912. payload={"name": "test-authz-scope"},
  3913. )
  3914. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3915. assert len(res) == 1
  3916. assert {x["name"] for x in res} == {"test-authz-scope"}
  3917. # Test service account user
  3918. res = await admin.a_get_client_service_account_user(client_id=auth_client_id)
  3919. assert res["username"] == "service-account-authz-client", res
  3920. with pytest.raises(KeycloakGetError) as err:
  3921. await admin.a_get_client_service_account_user(client_id=client_id)
  3922. assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
  3923. UNKOWN_ERROR_REGEX,
  3924. )
  3925. # Test delete client
  3926. res = await admin.a_delete_client(client_id=auth_client_id)
  3927. assert res == {}, res
  3928. with pytest.raises(KeycloakDeleteError) as err:
  3929. await admin.a_delete_client(client_id=auth_client_id)
  3930. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3931. # Test client credentials
  3932. await admin.a_create_client(
  3933. payload={
  3934. "name": "test-confidential",
  3935. "enabled": True,
  3936. "protocol": "openid-connect",
  3937. "publicClient": False,
  3938. "redirectUris": ["http://localhost/*"],
  3939. "webOrigins": ["+"],
  3940. "clientId": "test-confidential",
  3941. "secret": "test-secret",
  3942. "clientAuthenticatorType": "client-secret",
  3943. },
  3944. )
  3945. with pytest.raises(KeycloakGetError) as err:
  3946. await admin.a_get_client_secrets(client_id="does-not-exist")
  3947. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3948. secrets = await admin.a_get_client_secrets(
  3949. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3950. )
  3951. assert secrets == {"type": "secret", "value": "test-secret"}
  3952. with pytest.raises(KeycloakPostError) as err:
  3953. await admin.a_generate_client_secrets(client_id="does-not-exist")
  3954. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3955. res = await admin.a_generate_client_secrets(
  3956. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3957. )
  3958. assert res
  3959. assert (
  3960. await admin.a_get_client_secrets(
  3961. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3962. )
  3963. == res
  3964. )
  3965. @pytest.mark.asyncio
  3966. async def test_a_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  3967. """
  3968. Test realm roles.
  3969. :param admin: Keycloak Admin client
  3970. :type admin: KeycloakAdmin
  3971. :param realm: Keycloak realm
  3972. :type realm: str
  3973. """
  3974. await admin.a_change_current_realm(realm)
  3975. # Test get realm roles
  3976. roles = await admin.a_get_realm_roles()
  3977. assert len(roles) == 3, roles
  3978. role_names = [x["name"] for x in roles]
  3979. assert "uma_authorization" in role_names, role_names
  3980. assert "offline_access" in role_names, role_names
  3981. # Test get realm roles with search text
  3982. searched_roles = await admin.a_get_realm_roles(search_text="uma_a")
  3983. searched_role_names = [x["name"] for x in searched_roles]
  3984. assert "uma_authorization" in searched_role_names, searched_role_names
  3985. assert "offline_access" not in searched_role_names, searched_role_names
  3986. # Test empty members
  3987. with pytest.raises(KeycloakGetError) as err:
  3988. await admin.a_get_realm_role_members(role_name="does-not-exist")
  3989. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3990. members = await admin.a_get_realm_role_members(role_name="offline_access")
  3991. assert members == [], members
  3992. # Test create realm role
  3993. role_id = await admin.a_create_realm_role(
  3994. payload={"name": "test-realm-role"},
  3995. skip_exists=True,
  3996. )
  3997. assert role_id, role_id
  3998. with pytest.raises(KeycloakPostError) as err:
  3999. await admin.a_create_realm_role(payload={"name": "test-realm-role"})
  4000. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  4001. role_id_2 = await admin.a_create_realm_role(
  4002. payload={"name": "test-realm-role"},
  4003. skip_exists=True,
  4004. )
  4005. assert role_id == role_id_2
  4006. # Test get realm role by its id
  4007. role_id = (await admin.a_get_realm_role(role_name="test-realm-role"))["id"]
  4008. res = await admin.a_get_realm_role_by_id(role_id)
  4009. assert res["name"] == "test-realm-role"
  4010. # Test update realm role
  4011. res = await admin.a_update_realm_role(
  4012. role_name="test-realm-role",
  4013. payload={"name": "test-realm-role-update"},
  4014. )
  4015. assert res == {}, res
  4016. with pytest.raises(KeycloakPutError) as err:
  4017. await admin.a_update_realm_role(
  4018. role_name="test-realm-role",
  4019. payload={"name": "test-realm-role-update"},
  4020. )
  4021. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4022. # Test realm role user assignment
  4023. user_id = await admin.a_create_user(
  4024. payload={"username": "role-testing", "email": "test@test.test"},
  4025. )
  4026. with pytest.raises(KeycloakPostError) as err:
  4027. await admin.a_assign_realm_roles(user_id=user_id, roles=["bad"])
  4028. assert err.match(UNKOWN_ERROR_REGEX), err
  4029. res = await admin.a_assign_realm_roles(
  4030. user_id=user_id,
  4031. roles=[
  4032. await admin.a_get_realm_role(role_name="offline_access"),
  4033. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  4034. ],
  4035. )
  4036. assert res == {}, res
  4037. assert admin.get_user(user_id=user_id)["username"] in [
  4038. x["username"] for x in await admin.a_get_realm_role_members(role_name="offline_access")
  4039. ]
  4040. assert admin.get_user(user_id=user_id)["username"] in [
  4041. x["username"]
  4042. for x in await admin.a_get_realm_role_members(role_name="test-realm-role-update")
  4043. ]
  4044. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  4045. assert len(roles) == 3
  4046. assert "offline_access" in [x["name"] for x in roles]
  4047. assert "test-realm-role-update" in [x["name"] for x in roles]
  4048. with pytest.raises(KeycloakDeleteError) as err:
  4049. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  4050. assert err.match(UNKOWN_ERROR_REGEX), err
  4051. res = await admin.a_delete_realm_roles_of_user(
  4052. user_id=user_id,
  4053. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4054. )
  4055. assert res == {}, res
  4056. assert await admin.a_get_realm_role_members(role_name="offline_access") == []
  4057. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  4058. assert len(roles) == 2
  4059. assert "offline_access" not in [x["name"] for x in roles]
  4060. assert "test-realm-role-update" in [x["name"] for x in roles]
  4061. roles = await admin.a_get_available_realm_roles_of_user(user_id=user_id)
  4062. assert len(roles) == 2
  4063. assert "offline_access" in [x["name"] for x in roles]
  4064. assert "uma_authorization" in [x["name"] for x in roles]
  4065. # Test realm role group assignment
  4066. group_id = await admin.a_create_group(payload={"name": "test-group"})
  4067. with pytest.raises(KeycloakPostError) as err:
  4068. await admin.a_assign_group_realm_roles(group_id=group_id, roles=["bad"])
  4069. assert err.match(UNKOWN_ERROR_REGEX), err
  4070. res = await admin.a_assign_group_realm_roles(
  4071. group_id=group_id,
  4072. roles=[
  4073. await admin.a_get_realm_role(role_name="offline_access"),
  4074. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  4075. ],
  4076. )
  4077. assert res == {}, res
  4078. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  4079. assert len(roles) == 2
  4080. assert "offline_access" in [x["name"] for x in roles]
  4081. assert "test-realm-role-update" in [x["name"] for x in roles]
  4082. with pytest.raises(KeycloakDeleteError) as err:
  4083. await admin.a_delete_group_realm_roles(group_id=group_id, roles=["bad"])
  4084. assert err.match(UNKOWN_ERROR_REGEX)
  4085. res = await admin.a_delete_group_realm_roles(
  4086. group_id=group_id,
  4087. roles=[admin.get_realm_role(role_name="offline_access")],
  4088. )
  4089. assert res == {}, res
  4090. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  4091. assert len(roles) == 1
  4092. assert "test-realm-role-update" in [x["name"] for x in roles]
  4093. # Test composite realm roles
  4094. composite_role = await admin.a_create_realm_role(payload={"name": "test-composite-role"})
  4095. with pytest.raises(KeycloakPostError) as err:
  4096. await admin.a_add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  4097. assert err.match(UNKOWN_ERROR_REGEX), err
  4098. res = await admin.a_add_composite_realm_roles_to_role(
  4099. role_name=composite_role,
  4100. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  4101. )
  4102. assert res == {}, res
  4103. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  4104. assert len(res) == 1
  4105. assert "test-realm-role-update" in res[0]["name"]
  4106. with pytest.raises(KeycloakGetError) as err:
  4107. await admin.a_get_composite_realm_roles_of_role(role_name="bad")
  4108. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4109. res = await admin.a_get_composite_realm_roles_of_user(user_id=user_id)
  4110. assert len(res) == 4
  4111. assert "offline_access" in {x["name"] for x in res}
  4112. assert "test-realm-role-update" in {x["name"] for x in res}
  4113. assert "uma_authorization" in {x["name"] for x in res}
  4114. with pytest.raises(KeycloakGetError) as err:
  4115. await admin.a_get_composite_realm_roles_of_user(user_id="bad")
  4116. assert err.match(USER_NOT_FOUND_REGEX), err
  4117. with pytest.raises(KeycloakDeleteError) as err:
  4118. await admin.a_remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  4119. assert err.match(UNKOWN_ERROR_REGEX), err
  4120. res = await admin.a_remove_composite_realm_roles_to_role(
  4121. role_name=composite_role,
  4122. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  4123. )
  4124. assert res == {}, res
  4125. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  4126. assert len(res) == 0
  4127. # Test realm role group list
  4128. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update")
  4129. assert len(res) == 1
  4130. assert res[0]["id"] == group_id
  4131. with pytest.raises(KeycloakGetError) as err:
  4132. await admin.a_get_realm_role_groups(role_name="non-existent-role")
  4133. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4134. # Test with query params
  4135. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
  4136. assert len(res) == 1
  4137. # Test delete realm role
  4138. res = await admin.a_delete_realm_role(role_name=composite_role)
  4139. assert res == {}, res
  4140. with pytest.raises(KeycloakDeleteError) as err:
  4141. await admin.a_delete_realm_role(role_name=composite_role)
  4142. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4143. @pytest.mark.asyncio
  4144. @pytest.mark.parametrize(
  4145. ("testcase", "arg_brief_repr", "includes_attributes"),
  4146. [
  4147. ("brief True", {"brief_representation": True}, False),
  4148. ("brief False", {"brief_representation": False}, True),
  4149. ("default", {}, False),
  4150. ],
  4151. )
  4152. async def test_a_role_attributes(
  4153. admin: KeycloakAdmin,
  4154. realm: str,
  4155. client: str,
  4156. arg_brief_repr: dict,
  4157. includes_attributes: bool,
  4158. testcase: str,
  4159. ) -> None:
  4160. """
  4161. Test getting role attributes for bulk calls.
  4162. :param admin: Keycloak admin
  4163. :type admin: KeycloakAdmin
  4164. :param realm: Keycloak realm
  4165. :type realm: str
  4166. :param client: Keycloak client
  4167. :type client: str
  4168. :param arg_brief_repr: Brief representation
  4169. :type arg_brief_repr: dict
  4170. :param includes_attributes: Indicator whether to include attributes
  4171. :type includes_attributes: bool
  4172. :param testcase: Test case
  4173. :type testcase: str
  4174. """
  4175. # setup
  4176. attribute_role = "test-realm-role-w-attr"
  4177. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  4178. role_id = await admin.a_create_realm_role(
  4179. payload={"name": attribute_role, "attributes": test_attrs},
  4180. skip_exists=True,
  4181. )
  4182. assert role_id, role_id
  4183. cli_role_id = await admin.a_create_client_role(
  4184. client,
  4185. payload={"name": attribute_role, "attributes": test_attrs},
  4186. skip_exists=True,
  4187. )
  4188. assert cli_role_id, cli_role_id
  4189. if not includes_attributes:
  4190. test_attrs = None
  4191. # tests
  4192. roles = await admin.a_get_realm_roles(**arg_brief_repr)
  4193. roles_filtered = [role for role in roles if role["name"] == role_id]
  4194. assert roles_filtered, roles_filtered
  4195. role = roles_filtered[0]
  4196. assert role.get("attributes") == test_attrs, testcase
  4197. roles = await admin.a_get_client_roles(client, **arg_brief_repr)
  4198. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  4199. assert roles_filtered, roles_filtered
  4200. role = roles_filtered[0]
  4201. assert role.get("attributes") == test_attrs, testcase
  4202. # cleanup
  4203. res = await admin.a_delete_realm_role(role_name=attribute_role)
  4204. assert res == {}, res
  4205. res = await admin.a_delete_client_role(client, role_name=attribute_role)
  4206. assert res == {}, res
  4207. @pytest.mark.asyncio
  4208. async def test_a_client_scope_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  4209. """
  4210. Test client realm roles.
  4211. :param admin: Keycloak admin
  4212. :type admin: KeycloakAdmin
  4213. :param realm: Keycloak realm
  4214. :type realm: str
  4215. """
  4216. await admin.a_change_current_realm(realm)
  4217. # Test get realm roles
  4218. roles = await admin.a_get_realm_roles()
  4219. assert len(roles) == 3, roles
  4220. role_names = [x["name"] for x in roles]
  4221. assert "uma_authorization" in role_names, role_names
  4222. assert "offline_access" in role_names, role_names
  4223. # create realm role for test
  4224. role_id = await admin.a_create_realm_role(
  4225. payload={"name": "test-realm-role"},
  4226. skip_exists=True,
  4227. )
  4228. assert role_id, role_id
  4229. # Test realm role client assignment
  4230. client_id = await admin.a_create_client(
  4231. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4232. )
  4233. with pytest.raises(KeycloakPostError) as err:
  4234. await admin.a_assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  4235. assert err.match(UNKOWN_ERROR_REGEX), err
  4236. res = await admin.a_assign_realm_roles_to_client_scope(
  4237. client_id=client_id,
  4238. roles=[
  4239. await admin.a_get_realm_role(role_name="offline_access"),
  4240. await admin.a_get_realm_role(role_name="test-realm-role"),
  4241. ],
  4242. )
  4243. assert res == {}, res
  4244. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4245. assert len(roles) == 2
  4246. client_role_names = [x["name"] for x in roles]
  4247. assert "offline_access" in client_role_names, client_role_names
  4248. assert "test-realm-role" in client_role_names, client_role_names
  4249. assert "uma_authorization" not in client_role_names, client_role_names
  4250. # Test remove realm role of client
  4251. with pytest.raises(KeycloakDeleteError) as err:
  4252. await admin.a_delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  4253. assert err.match(UNKOWN_ERROR_REGEX), err
  4254. res = await admin.a_delete_realm_roles_of_client_scope(
  4255. client_id=client_id,
  4256. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4257. )
  4258. assert res == {}, res
  4259. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4260. assert len(roles) == 1
  4261. assert "test-realm-role" in [x["name"] for x in roles]
  4262. res = await admin.a_delete_realm_roles_of_client_scope(
  4263. client_id=client_id,
  4264. roles=[await admin.a_get_realm_role(role_name="test-realm-role")],
  4265. )
  4266. assert res == {}, res
  4267. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4268. assert len(roles) == 0
  4269. @pytest.mark.asyncio
  4270. async def test_a_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str) -> None:
  4271. """
  4272. Test client assignment of other client roles.
  4273. :param admin: Keycloak admin
  4274. :type admin: KeycloakAdmin
  4275. :param realm: Keycloak realm
  4276. :type realm: str
  4277. :param client: Keycloak client
  4278. :type client: str
  4279. """
  4280. await admin.a_change_current_realm(realm)
  4281. client_id = await admin.a_create_client(
  4282. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4283. )
  4284. # Test get client roles
  4285. roles = await admin.a_get_client_roles_of_client_scope(client_id, client)
  4286. assert len(roles) == 0, roles
  4287. # create client role for test
  4288. client_role_id = await admin.a_create_client_role(
  4289. client_role_id=client,
  4290. payload={"name": "client-role-test"},
  4291. skip_exists=True,
  4292. )
  4293. assert client_role_id, client_role_id
  4294. # Test client role assignment to other client
  4295. with pytest.raises(KeycloakPostError) as err:
  4296. await admin.a_assign_client_roles_to_client_scope(
  4297. client_id=client_id,
  4298. client_roles_owner_id=client,
  4299. roles=["bad"],
  4300. )
  4301. assert err.match(UNKOWN_ERROR_REGEX), err
  4302. res = await admin.a_assign_client_roles_to_client_scope(
  4303. client_id=client_id,
  4304. client_roles_owner_id=client,
  4305. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  4306. )
  4307. assert res == {}, res
  4308. roles = await admin.a_get_client_roles_of_client_scope(
  4309. client_id=client_id,
  4310. client_roles_owner_id=client,
  4311. )
  4312. assert len(roles) == 1
  4313. client_role_names = [x["name"] for x in roles]
  4314. assert "client-role-test" in client_role_names, client_role_names
  4315. # Test remove realm role of client
  4316. with pytest.raises(KeycloakDeleteError) as err:
  4317. await admin.a_delete_client_roles_of_client_scope(
  4318. client_id=client_id,
  4319. client_roles_owner_id=client,
  4320. roles=["bad"],
  4321. )
  4322. assert err.match(UNKOWN_ERROR_REGEX), err
  4323. res = await admin.a_delete_client_roles_of_client_scope(
  4324. client_id=client_id,
  4325. client_roles_owner_id=client,
  4326. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  4327. )
  4328. assert res == {}, res
  4329. roles = await admin.a_get_client_roles_of_client_scope(
  4330. client_id=client_id,
  4331. client_roles_owner_id=client,
  4332. )
  4333. assert len(roles) == 0
  4334. @pytest.mark.asyncio
  4335. async def test_a_client_scope_mapping_client_roles(
  4336. admin: KeycloakAdmin,
  4337. realm: str,
  4338. client: str,
  4339. ) -> None:
  4340. """
  4341. Test client scope assignment of client roles.
  4342. :param admin: Keycloak admin
  4343. :type admin: KeycloakAdmin
  4344. :param realm: Keycloak realm
  4345. :type realm: str
  4346. :param client: Keycloak client owning roles
  4347. :type client: str
  4348. """
  4349. _client_role_name = "some-client-role"
  4350. await admin.a_change_current_realm(realm)
  4351. client_obj = await admin.a_get_client(client)
  4352. client_name = client_obj["name"]
  4353. client_scope = {
  4354. "name": "test_client_scope",
  4355. "description": "Test Client Scope",
  4356. "protocol": "openid-connect",
  4357. "attributes": {},
  4358. }
  4359. client_scope_id = await admin.a_create_client_scope(client_scope, skip_exists=False)
  4360. # Test get client roles
  4361. client_specific_roles = await admin.a_get_client_specific_roles_of_client_scope(
  4362. client_scope_id,
  4363. client,
  4364. )
  4365. assert len(client_specific_roles) == 0, client_specific_roles
  4366. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id)
  4367. assert len(all_roles) == 0, all_roles
  4368. # create client role for test
  4369. client_role_name = await admin.a_create_client_role(
  4370. client_role_id=client,
  4371. payload={"name": _client_role_name},
  4372. skip_exists=True,
  4373. )
  4374. assert client_role_name, client_role_name
  4375. # Test client role assignment to other client
  4376. with pytest.raises(KeycloakPostError) as err:
  4377. await admin.a_add_client_specific_roles_to_client_scope(
  4378. client_scope_id=client_scope_id,
  4379. client_roles_owner_id=client,
  4380. roles=["bad"],
  4381. )
  4382. assert err.match(UNKOWN_ERROR_REGEX), err
  4383. res = await admin.a_add_client_specific_roles_to_client_scope(
  4384. client_scope_id=client_scope_id,
  4385. client_roles_owner_id=client,
  4386. roles=[await admin.a_get_client_role(client_id=client, role_name=_client_role_name)],
  4387. )
  4388. assert res == {}, res
  4389. # Test when getting roles for the specific owner client
  4390. client_specific_roles = await admin.a_get_client_specific_roles_of_client_scope(
  4391. client_scope_id=client_scope_id,
  4392. client_roles_owner_id=client,
  4393. )
  4394. assert len(client_specific_roles) == 1
  4395. client_role_names = [x["name"] for x in client_specific_roles]
  4396. assert _client_role_name in client_role_names, client_role_names
  4397. # Test when getting all roles for the client scope
  4398. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  4399. assert "clientMappings" in all_roles, all_roles
  4400. all_roles_clients = all_roles["clientMappings"]
  4401. assert client_name in all_roles_clients, all_roles_clients
  4402. mappings = all_roles_clients[client_name]["mappings"]
  4403. client_role_names = [x["name"] for x in mappings]
  4404. assert _client_role_name in client_role_names, client_role_names
  4405. # Test remove realm role of client
  4406. with pytest.raises(KeycloakDeleteError) as err:
  4407. await admin.a_remove_client_specific_roles_of_client_scope(
  4408. client_scope_id=client_scope_id,
  4409. client_roles_owner_id=client,
  4410. roles=["bad"],
  4411. )
  4412. assert err.match(UNKOWN_ERROR_REGEX), err
  4413. res = await admin.a_remove_client_specific_roles_of_client_scope(
  4414. client_scope_id=client_scope_id,
  4415. client_roles_owner_id=client,
  4416. roles=[await admin.a_get_client_role(client_id=client, role_name=_client_role_name)],
  4417. )
  4418. assert res == {}, res
  4419. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  4420. assert len(all_roles) == 0
  4421. @pytest.mark.asyncio
  4422. async def test_a_client_default_client_scopes(
  4423. admin: KeycloakAdmin,
  4424. realm: str,
  4425. client: str,
  4426. ) -> None:
  4427. """
  4428. Test client assignment of default client scopes.
  4429. :param admin: Keycloak admin
  4430. :type admin: KeycloakAdmin
  4431. :param realm: Keycloak realm
  4432. :type realm: str
  4433. :param client: Keycloak client
  4434. :type client: str
  4435. """
  4436. await admin.a_change_current_realm(realm)
  4437. client_id = await admin.a_create_client(
  4438. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4439. )
  4440. # Test get client default scopes
  4441. # keycloak default roles: web-origins, acr, profile, roles, email
  4442. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4443. assert len(default_client_scopes) in [6, 5], default_client_scopes
  4444. # Test add a client scope to client default scopes
  4445. default_client_scope = "test-client-default-scope"
  4446. new_client_scope = {
  4447. "name": default_client_scope,
  4448. "description": f"Test Client Scope: {default_client_scope}",
  4449. "protocol": "openid-connect",
  4450. "attributes": {},
  4451. }
  4452. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  4453. new_default_client_scope_data = {
  4454. "realm": realm,
  4455. "client": client_id,
  4456. "clientScopeId": new_client_scope_id,
  4457. }
  4458. await admin.a_add_client_default_client_scope(
  4459. client_id,
  4460. new_client_scope_id,
  4461. new_default_client_scope_data,
  4462. )
  4463. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4464. assert len(default_client_scopes) in [6, 7], default_client_scopes
  4465. # Test remove a client default scope
  4466. await admin.a_delete_client_default_client_scope(client_id, new_client_scope_id)
  4467. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4468. assert len(default_client_scopes) in [5, 6], default_client_scopes
  4469. @pytest.mark.asyncio
  4470. async def test_a_client_optional_client_scopes(
  4471. admin: KeycloakAdmin,
  4472. realm: str,
  4473. client: str,
  4474. ) -> None:
  4475. """
  4476. Test client assignment of optional client scopes.
  4477. :param admin: Keycloak admin
  4478. :type admin: KeycloakAdmin
  4479. :param realm: Keycloak realm
  4480. :type realm: str
  4481. :param client: Keycloak client
  4482. :type client: str
  4483. """
  4484. await admin.a_change_current_realm(realm)
  4485. client_id = await admin.a_create_client(
  4486. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4487. )
  4488. # Test get client optional scopes
  4489. # keycloak optional roles: microprofile-jwt, offline_access, address, --> for versions < 26.0.0
  4490. # starting with Keycloak version 26.0.0 a new optional role is added: organization
  4491. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4492. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  4493. # Test add a client scope to client optional scopes
  4494. optional_client_scope = "test-client-optional-scope"
  4495. new_client_scope = {
  4496. "name": optional_client_scope,
  4497. "description": f"Test Client Scope: {optional_client_scope}",
  4498. "protocol": "openid-connect",
  4499. "attributes": {},
  4500. }
  4501. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  4502. new_optional_client_scope_data = {
  4503. "realm": realm,
  4504. "client": client_id,
  4505. "clientScopeId": new_client_scope_id,
  4506. }
  4507. await admin.a_add_client_optional_client_scope(
  4508. client_id,
  4509. new_client_scope_id,
  4510. new_optional_client_scope_data,
  4511. )
  4512. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4513. assert len(optional_client_scopes) in [5, 6], optional_client_scopes
  4514. # Test remove a client optional scope
  4515. await admin.a_delete_client_optional_client_scope(client_id, new_client_scope_id)
  4516. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4517. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  4518. @pytest.mark.asyncio
  4519. async def test_a_client_roles(admin: KeycloakAdmin, client: str) -> None:
  4520. """
  4521. Test client roles.
  4522. :param admin: Keycloak Admin client
  4523. :type admin: KeycloakAdmin
  4524. :param client: Keycloak client
  4525. :type client: str
  4526. """
  4527. # Test get client roles
  4528. res = await admin.a_get_client_roles(client_id=client)
  4529. assert len(res) == 0
  4530. with pytest.raises(KeycloakGetError) as err:
  4531. await admin.a_get_client_roles(client_id="bad")
  4532. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  4533. # Test create client role
  4534. client_role_id = await admin.a_create_client_role(
  4535. client_role_id=client,
  4536. payload={"name": "client-role-test"},
  4537. skip_exists=True,
  4538. )
  4539. with pytest.raises(KeycloakPostError) as err:
  4540. await admin.a_create_client_role(
  4541. client_role_id=client,
  4542. payload={"name": "client-role-test"},
  4543. )
  4544. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  4545. client_role_id_2 = await admin.a_create_client_role(
  4546. client_role_id=client,
  4547. payload={"name": "client-role-test"},
  4548. skip_exists=True,
  4549. )
  4550. assert client_role_id == client_role_id_2
  4551. # Test get client role
  4552. res = await admin.a_get_client_role(client_id=client, role_name="client-role-test")
  4553. assert res["name"] == client_role_id
  4554. with pytest.raises(KeycloakGetError) as err:
  4555. await admin.a_get_client_role(client_id=client, role_name="bad")
  4556. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4557. res_ = await admin.a_get_client_role_id(client_id=client, role_name="client-role-test")
  4558. assert res_ == res["id"]
  4559. with pytest.raises(KeycloakGetError) as err:
  4560. await admin.a_get_client_role_id(client_id=client, role_name="bad")
  4561. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4562. assert len(await admin.a_get_client_roles(client_id=client)) == 1
  4563. # Test update client role
  4564. res = await admin.a_update_client_role(
  4565. client_id=client,
  4566. role_name="client-role-test",
  4567. payload={"name": "client-role-test-update"},
  4568. )
  4569. assert res == {}
  4570. with pytest.raises(KeycloakPutError) as err:
  4571. res = await admin.a_update_client_role(
  4572. client_id=client,
  4573. role_name="client-role-test",
  4574. payload={"name": "client-role-test-update"},
  4575. )
  4576. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4577. # Test user with client role
  4578. res = await admin.a_get_client_role_members(
  4579. client_id=client,
  4580. role_name="client-role-test-update",
  4581. )
  4582. assert len(res) == 0
  4583. with pytest.raises(KeycloakGetError) as err:
  4584. await admin.a_get_client_role_members(client_id=client, role_name="bad")
  4585. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4586. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  4587. with pytest.raises(KeycloakPostError) as err:
  4588. await admin.a_assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  4589. assert err.match(UNKOWN_ERROR_REGEX), err
  4590. res = await admin.a_assign_client_role(
  4591. user_id=user_id,
  4592. client_id=client,
  4593. roles=[
  4594. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4595. ],
  4596. )
  4597. assert res == {}
  4598. assert (
  4599. len(
  4600. await admin.a_get_client_role_members(
  4601. client_id=client,
  4602. role_name="client-role-test-update",
  4603. ),
  4604. )
  4605. == 1
  4606. )
  4607. roles = await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)
  4608. assert len(roles) == 1, roles
  4609. with pytest.raises(KeycloakGetError) as err:
  4610. await admin.a_get_client_roles_of_user(user_id=user_id, client_id="bad")
  4611. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4612. roles = await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  4613. assert len(roles) == 1, roles
  4614. with pytest.raises(KeycloakGetError) as err:
  4615. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4616. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4617. roles = await admin.a_get_available_client_roles_of_user(user_id=user_id, client_id=client)
  4618. assert len(roles) == 0, roles
  4619. with pytest.raises(KeycloakGetError) as err:
  4620. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4621. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4622. with pytest.raises(KeycloakDeleteError) as err:
  4623. await admin.a_delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  4624. assert err.match(UNKOWN_ERROR_REGEX), err
  4625. await admin.a_delete_client_roles_of_user(
  4626. user_id=user_id,
  4627. client_id=client,
  4628. roles=[
  4629. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4630. ],
  4631. )
  4632. assert len(await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  4633. # Test groups and client roles
  4634. res = await admin.a_get_client_role_groups(
  4635. client_id=client,
  4636. role_name="client-role-test-update",
  4637. )
  4638. assert len(res) == 0
  4639. with pytest.raises(KeycloakGetError) as err:
  4640. await admin.a_get_client_role_groups(client_id=client, role_name="bad")
  4641. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4642. group_id = await admin.a_create_group(payload={"name": "test-group"})
  4643. res = await admin.a_get_group_client_roles(group_id=group_id, client_id=client)
  4644. assert len(res) == 0
  4645. with pytest.raises(KeycloakGetError) as err:
  4646. await admin.a_get_group_client_roles(group_id=group_id, client_id="bad")
  4647. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4648. with pytest.raises(KeycloakPostError) as err:
  4649. await admin.a_assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4650. assert err.match(UNKOWN_ERROR_REGEX), err
  4651. res = await admin.a_assign_group_client_roles(
  4652. group_id=group_id,
  4653. client_id=client,
  4654. roles=[
  4655. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4656. ],
  4657. )
  4658. assert res == {}
  4659. assert (
  4660. len(
  4661. await admin.a_get_client_role_groups(
  4662. client_id=client,
  4663. role_name="client-role-test-update",
  4664. ),
  4665. )
  4666. == 1
  4667. )
  4668. assert len(await admin.a_get_group_client_roles(group_id=group_id, client_id=client)) == 1
  4669. with pytest.raises(KeycloakDeleteError) as err:
  4670. await admin.a_delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4671. assert err.match(UNKOWN_ERROR_REGEX), err
  4672. res = await admin.a_delete_group_client_roles(
  4673. group_id=group_id,
  4674. client_id=client,
  4675. roles=[
  4676. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4677. ],
  4678. )
  4679. assert res == {}
  4680. # Test composite client roles
  4681. with pytest.raises(KeycloakPostError) as err:
  4682. await admin.a_add_composite_client_roles_to_role(
  4683. client_role_id=client,
  4684. role_name="client-role-test-update",
  4685. roles=["bad"],
  4686. )
  4687. assert err.match(UNKOWN_ERROR_REGEX), err
  4688. res = await admin.a_add_composite_client_roles_to_role(
  4689. client_role_id=client,
  4690. role_name="client-role-test-update",
  4691. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4692. )
  4693. assert res == {}
  4694. assert (await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"))[
  4695. "composite"
  4696. ]
  4697. # Test removal of composite client roles
  4698. with pytest.raises(KeycloakDeleteError) as err:
  4699. await admin.a_remove_composite_client_roles_from_role(
  4700. client_role_id=client,
  4701. role_name="client-role-test-update",
  4702. roles=["bad"],
  4703. )
  4704. assert err.match(UNKOWN_ERROR_REGEX), err
  4705. res = await admin.a_remove_composite_client_roles_from_role(
  4706. client_role_id=client,
  4707. role_name="client-role-test-update",
  4708. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4709. )
  4710. assert res == {}
  4711. assert not (
  4712. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4713. )["composite"]
  4714. # Test delete of client role
  4715. res = await admin.a_delete_client_role(
  4716. client_role_id=client,
  4717. role_name="client-role-test-update",
  4718. )
  4719. assert res == {}
  4720. with pytest.raises(KeycloakDeleteError) as err:
  4721. await admin.a_delete_client_role(
  4722. client_role_id=client,
  4723. role_name="client-role-test-update",
  4724. )
  4725. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4726. # Test of roles by id - Get role
  4727. await admin.a_create_client_role(
  4728. client_role_id=client,
  4729. payload={"name": "client-role-by-id-test"},
  4730. skip_exists=True,
  4731. )
  4732. role = await admin.a_get_client_role(client_id=client, role_name="client-role-by-id-test")
  4733. res = await admin.a_get_role_by_id(role_id=role["id"])
  4734. assert res["name"] == "client-role-by-id-test"
  4735. with pytest.raises(KeycloakGetError) as err:
  4736. await admin.a_get_role_by_id(role_id="bad")
  4737. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4738. # Test of roles by id - Update role
  4739. res = await admin.a_update_role_by_id(
  4740. role_id=role["id"],
  4741. payload={"name": "client-role-by-id-test-update"},
  4742. )
  4743. assert res == {}
  4744. with pytest.raises(KeycloakPutError) as err:
  4745. res = await admin.a_update_role_by_id(
  4746. role_id="bad",
  4747. payload={"name": "client-role-by-id-test-update"},
  4748. )
  4749. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4750. # Test of roles by id - Delete role
  4751. res = await admin.a_delete_role_by_id(role_id=role["id"])
  4752. assert res == {}
  4753. with pytest.raises(KeycloakDeleteError) as err:
  4754. await admin.a_delete_role_by_id(role_id="bad")
  4755. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4756. @pytest.mark.asyncio
  4757. async def test_a_enable_token_exchange(admin: KeycloakAdmin, realm: str) -> None:
  4758. """
  4759. Test enable token exchange.
  4760. :param admin: Keycloak Admin client
  4761. :type admin: KeycloakAdmin
  4762. :param realm: Keycloak realm
  4763. :type realm: str
  4764. :raises AssertionError: In case of bad configuration
  4765. """
  4766. # Test enabling token exchange between two confidential clients
  4767. await admin.a_change_current_realm(realm)
  4768. # Create test clients
  4769. source_client_id = await admin.a_create_client(
  4770. payload={"name": "Source Client", "clientId": "source-client"},
  4771. )
  4772. target_client_id = await admin.a_create_client(
  4773. payload={"name": "Target Client", "clientId": "target-client"},
  4774. )
  4775. for c in await admin.a_get_clients():
  4776. if c["clientId"] == "realm-management":
  4777. realm_management_id = c["id"]
  4778. break
  4779. else:
  4780. pytest.fail("Missing realm management client")
  4781. # Enable permissions on the Superset client
  4782. await admin.a_update_client_management_permissions(
  4783. payload={"enabled": True},
  4784. client_id=target_client_id,
  4785. )
  4786. # Fetch various IDs and strings needed when creating the permission
  4787. token_exchange_permission_id = (
  4788. await admin.a_get_client_management_permissions(client_id=target_client_id)
  4789. )["scopePermissions"]["token-exchange"]
  4790. scopes = await admin.a_get_client_authz_policy_scopes(
  4791. client_id=realm_management_id,
  4792. policy_id=token_exchange_permission_id,
  4793. )
  4794. for s in scopes:
  4795. if s["name"] == "token-exchange":
  4796. token_exchange_scope_id = s["id"]
  4797. break
  4798. else:
  4799. pytest.fail("Missing token-exchange scope")
  4800. resources = await admin.a_get_client_authz_policy_resources(
  4801. client_id=realm_management_id,
  4802. policy_id=token_exchange_permission_id,
  4803. )
  4804. for r in resources:
  4805. if r["name"] == f"client.resource.{target_client_id}":
  4806. token_exchange_resource_id = r["_id"]
  4807. break
  4808. else:
  4809. pytest.fail("Missing client resource")
  4810. # Create a client policy for source client
  4811. policy_name = "Exchange source client token with target client token"
  4812. client_policy_id = (
  4813. await admin.a_create_client_authz_client_policy(
  4814. payload={
  4815. "type": "client",
  4816. "logic": "POSITIVE",
  4817. "decisionStrategy": "UNANIMOUS",
  4818. "name": policy_name,
  4819. "clients": [source_client_id],
  4820. },
  4821. client_id=realm_management_id,
  4822. )
  4823. )["id"]
  4824. policies = await admin.a_get_client_authz_client_policies(client_id=realm_management_id)
  4825. for policy in policies:
  4826. if policy["name"] == policy_name:
  4827. assert policy["clients"] == [source_client_id]
  4828. break
  4829. else:
  4830. pytest.fail("Missing client policy")
  4831. # Update permissions on the target client to reference this policy
  4832. permission_name = (
  4833. await admin.a_get_client_authz_scope_permission(
  4834. client_id=realm_management_id,
  4835. scope_id=token_exchange_permission_id,
  4836. )
  4837. )["name"]
  4838. await admin.a_update_client_authz_scope_permission(
  4839. payload={
  4840. "id": token_exchange_permission_id,
  4841. "name": permission_name,
  4842. "type": "scope",
  4843. "logic": "POSITIVE",
  4844. "decisionStrategy": "UNANIMOUS",
  4845. "resources": [token_exchange_resource_id],
  4846. "scopes": [token_exchange_scope_id],
  4847. "policies": [client_policy_id],
  4848. },
  4849. client_id=realm_management_id,
  4850. scope_id=token_exchange_permission_id,
  4851. )
  4852. # Create permissions on the target client to reference this policy
  4853. await admin.a_create_client_authz_scope_permission(
  4854. payload={
  4855. "id": "some-id",
  4856. "name": "test-permission",
  4857. "type": "scope",
  4858. "logic": "POSITIVE",
  4859. "decisionStrategy": "UNANIMOUS",
  4860. "resources": [token_exchange_resource_id],
  4861. "scopes": [token_exchange_scope_id],
  4862. "policies": [client_policy_id],
  4863. },
  4864. client_id=realm_management_id,
  4865. )
  4866. permission_name = (
  4867. await admin.a_get_client_authz_scope_permission(
  4868. client_id=realm_management_id,
  4869. scope_id=token_exchange_permission_id,
  4870. )
  4871. )["name"]
  4872. assert permission_name.startswith("token-exchange.permission.client.")
  4873. with pytest.raises(KeycloakPostError) as err:
  4874. await admin.a_create_client_authz_scope_permission(
  4875. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  4876. client_id="realm_management_id",
  4877. )
  4878. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  4879. @pytest.mark.asyncio
  4880. async def test_a_email(admin: KeycloakAdmin, user: str) -> None:
  4881. """
  4882. Test email.
  4883. :param admin: Keycloak Admin client
  4884. :type admin: KeycloakAdmin
  4885. :param user: Keycloak user
  4886. :type user: str
  4887. """
  4888. # Emails will fail as we don't have SMTP test setup
  4889. with pytest.raises(KeycloakPutError) as err:
  4890. await admin.a_send_update_account(user_id=user, payload={})
  4891. assert err.match(UNKOWN_ERROR_REGEX), err
  4892. admin.update_user(user_id=user, payload={"enabled": True})
  4893. with pytest.raises(KeycloakPutError) as err:
  4894. await admin.a_send_verify_email(user_id=user)
  4895. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  4896. @pytest.mark.asyncio
  4897. async def test_a_email_query_param_handling(admin: KeycloakAdmin, user: str) -> None:
  4898. """
  4899. Test that the optional parameters are correctly transformed into query params.
  4900. :param admin: Keycloak Admin client
  4901. :type admin: KeycloakAdmin
  4902. :param user: Keycloak user
  4903. :type user: str
  4904. """
  4905. with (
  4906. patch.object(
  4907. admin.connection.async_s,
  4908. "put",
  4909. side_effect=Exception("An expected error"),
  4910. ) as mock_put,
  4911. pytest.raises(KeycloakConnectionError),
  4912. ):
  4913. await admin.a_send_update_account(
  4914. user_id=user,
  4915. payload=["UPDATE_PASSWORD"],
  4916. client_id="update-account-client-id",
  4917. redirect_uri="https://example.com",
  4918. )
  4919. mock_put.assert_awaited_once_with(
  4920. ANY,
  4921. data='["UPDATE_PASSWORD"]',
  4922. params={"client_id": "update-account-client-id", "redirect_uri": "https://example.com"},
  4923. headers=ANY,
  4924. timeout=60,
  4925. )
  4926. with (
  4927. patch.object(
  4928. admin.connection.async_s,
  4929. "put",
  4930. side_effect=Exception("An expected error"),
  4931. ) as mock_put,
  4932. pytest.raises(KeycloakConnectionError),
  4933. ):
  4934. await admin.a_send_verify_email(
  4935. user_id=user,
  4936. client_id="verify-client-id",
  4937. redirect_uri="https://example.com",
  4938. )
  4939. mock_put.assert_awaited_once_with(
  4940. ANY,
  4941. data=ANY,
  4942. params={"client_id": "verify-client-id", "redirect_uri": "https://example.com"},
  4943. headers=ANY,
  4944. timeout=60,
  4945. )
  4946. @pytest.mark.asyncio
  4947. async def test_a_get_sessions(admin: KeycloakAdmin) -> None:
  4948. """
  4949. Test get sessions.
  4950. :param admin: Keycloak Admin client
  4951. :type admin: KeycloakAdmin
  4952. """
  4953. sessions = await admin.a_get_sessions(
  4954. user_id=admin.get_user_id(username=admin.connection.username),
  4955. )
  4956. assert len(sessions) >= 1
  4957. with pytest.raises(KeycloakGetError) as err:
  4958. await admin.a_get_sessions(user_id="bad")
  4959. assert err.match(USER_NOT_FOUND_REGEX)
  4960. @pytest.mark.asyncio
  4961. async def test_a_get_client_installation_provider(admin: KeycloakAdmin, client: str) -> None:
  4962. """
  4963. Test get client installation provider.
  4964. :param admin: Keycloak Admin client
  4965. :type admin: KeycloakAdmin
  4966. :param client: Keycloak client
  4967. :type client: str
  4968. """
  4969. with pytest.raises(KeycloakGetError) as err:
  4970. await admin.a_get_client_installation_provider(client_id=client, provider_id="bad")
  4971. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  4972. installation = await admin.a_get_client_installation_provider(
  4973. client_id=client,
  4974. provider_id="keycloak-oidc-keycloak-json",
  4975. )
  4976. assert set(installation.keys()) == {
  4977. "auth-server-url",
  4978. "confidential-port",
  4979. "credentials",
  4980. "realm",
  4981. "resource",
  4982. "ssl-required",
  4983. }
  4984. @pytest.mark.asyncio
  4985. async def test_a_auth_flows(admin: KeycloakAdmin, realm: str) -> None:
  4986. """
  4987. Test auth flows.
  4988. :param admin: Keycloak Admin client
  4989. :type admin: KeycloakAdmin
  4990. :param realm: Keycloak realm
  4991. :type realm: str
  4992. """
  4993. await admin.a_change_current_realm(realm)
  4994. res = await admin.a_get_authentication_flows()
  4995. assert len(res) <= 8, res
  4996. default_flows = len(res)
  4997. assert {x["alias"] for x in res}.issubset(
  4998. {
  4999. "reset credentials",
  5000. "browser",
  5001. "registration",
  5002. "http challenge",
  5003. "docker auth",
  5004. "direct grant",
  5005. "first broker login",
  5006. "clients",
  5007. },
  5008. )
  5009. assert set(res[0].keys()) == {
  5010. "alias",
  5011. "authenticationExecutions",
  5012. "builtIn",
  5013. "description",
  5014. "id",
  5015. "providerId",
  5016. "topLevel",
  5017. }
  5018. assert {x["alias"] for x in res}.issubset(
  5019. {
  5020. "reset credentials",
  5021. "browser",
  5022. "registration",
  5023. "docker auth",
  5024. "direct grant",
  5025. "first broker login",
  5026. "clients",
  5027. "http challenge",
  5028. },
  5029. )
  5030. with pytest.raises(KeycloakGetError) as err:
  5031. await admin.a_get_authentication_flow_for_id(flow_id="bad")
  5032. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  5033. browser_flow_id = next(x for x in res if x["alias"] == "browser")["id"]
  5034. res = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
  5035. assert res["alias"] == "browser"
  5036. # Test copying
  5037. with pytest.raises(KeycloakPostError) as err:
  5038. await admin.a_copy_authentication_flow(payload={}, flow_alias="bad")
  5039. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  5040. res = await admin.a_copy_authentication_flow(
  5041. payload={"newName": "test-browser"},
  5042. flow_alias="browser",
  5043. )
  5044. assert res == b"", res
  5045. assert len(await admin.a_get_authentication_flows()) == (default_flows + 1)
  5046. # Test create
  5047. res = await admin.a_create_authentication_flow(
  5048. payload={"alias": "test-create", "providerId": "basic-flow"},
  5049. )
  5050. assert res == b""
  5051. with pytest.raises(KeycloakPostError) as err:
  5052. await admin.a_create_authentication_flow(
  5053. payload={"alias": "test-create", "builtIn": False},
  5054. )
  5055. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  5056. assert await admin.a_create_authentication_flow(
  5057. payload={"alias": "test-create"},
  5058. skip_exists=True,
  5059. ) == {"msg": "Already exists"}
  5060. # Update
  5061. res = await admin.a_get_authentication_flows()
  5062. browser_flow_id = next(x for x in res if x["alias"] == "browser")["id"]
  5063. flow = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
  5064. del flow["authenticationExecutions"]
  5065. del flow["id"]
  5066. flow["description"] = "test description"
  5067. res = await admin.a_update_authentication_flow(flow_id=browser_flow_id, payload=flow)
  5068. res = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
  5069. assert res["description"] == "test description"
  5070. # Test flow executions
  5071. res = await admin.a_get_authentication_flow_executions(flow_alias="browser")
  5072. assert len(res) in [8, 12], res
  5073. with pytest.raises(KeycloakGetError) as err:
  5074. await admin.a_get_authentication_flow_executions(flow_alias="bad")
  5075. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  5076. exec_id = res[0]["id"]
  5077. res = await admin.a_get_authentication_flow_execution(execution_id=exec_id)
  5078. assert set(res.keys()).issubset(
  5079. {
  5080. "alternative",
  5081. "authenticator",
  5082. "authenticatorFlow",
  5083. "autheticatorFlow",
  5084. "conditional",
  5085. "disabled",
  5086. "enabled",
  5087. "id",
  5088. "parentFlow",
  5089. "priority",
  5090. "required",
  5091. "requirement",
  5092. },
  5093. ), res.keys()
  5094. with pytest.raises(KeycloakGetError) as err:
  5095. await admin.a_get_authentication_flow_execution(execution_id="bad")
  5096. assert err.match(ILLEGAL_EXECUTION_REGEX)
  5097. with pytest.raises(KeycloakPostError) as err:
  5098. await admin.a_create_authentication_flow_execution(payload={}, flow_alias="browser")
  5099. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  5100. res = await admin.a_create_authentication_flow_execution(
  5101. payload={"provider": "auth-cookie"},
  5102. flow_alias="test-create",
  5103. )
  5104. assert res == b""
  5105. assert len(await admin.a_get_authentication_flow_executions(flow_alias="test-create")) == 1
  5106. with pytest.raises(KeycloakPutError) as err:
  5107. await admin.a_update_authentication_flow_executions(
  5108. payload={"required": "yes"},
  5109. flow_alias="test-create",
  5110. )
  5111. assert err.match("Unrecognized field")
  5112. payload = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]
  5113. payload["displayName"] = "test"
  5114. res = await admin.a_update_authentication_flow_executions(
  5115. payload=payload,
  5116. flow_alias="test-create",
  5117. )
  5118. assert res or (res == {})
  5119. exec_id = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]["id"]
  5120. res = await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  5121. assert res == {}
  5122. with pytest.raises(KeycloakDeleteError) as err:
  5123. await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  5124. assert err.match(ILLEGAL_EXECUTION_REGEX)
  5125. # Test subflows
  5126. res = await admin.a_create_authentication_flow_subflow(
  5127. payload={
  5128. "alias": "test-subflow",
  5129. "provider": "basic-flow",
  5130. "type": "something",
  5131. "description": "something",
  5132. },
  5133. flow_alias="test-browser",
  5134. )
  5135. assert res == b""
  5136. with pytest.raises(KeycloakPostError) as err:
  5137. await admin.a_create_authentication_flow_subflow(
  5138. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  5139. flow_alias="test-browser",
  5140. )
  5141. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  5142. res = await admin.a_create_authentication_flow_subflow(
  5143. payload={
  5144. "alias": "test-subflow",
  5145. "provider": "basic-flow",
  5146. "type": "something",
  5147. "description": "something",
  5148. },
  5149. flow_alias="test-create",
  5150. skip_exists=True,
  5151. )
  5152. assert res == {"msg": "Already exists"}
  5153. # Test delete auth flow
  5154. flow_id = next(
  5155. x for x in await admin.a_get_authentication_flows() if x["alias"] == "test-browser"
  5156. )["id"]
  5157. res = await admin.a_delete_authentication_flow(flow_id=flow_id)
  5158. assert res == {}
  5159. with pytest.raises(KeycloakDeleteError) as err:
  5160. await admin.a_delete_authentication_flow(flow_id=flow_id)
  5161. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  5162. 'b\'{"error":"Flow not found"' in str(err)
  5163. )
  5164. @pytest.mark.asyncio
  5165. async def test_a_auth_flow_execution_priority(admin: KeycloakAdmin, realm: str) -> None:
  5166. """
  5167. Test execution priority.
  5168. :param admin: Keycloak Admin client
  5169. :type admin: KeycloakAdmin
  5170. :param realm: Keycloak realm
  5171. :type realm: str
  5172. """
  5173. await admin.a_change_current_realm(realm)
  5174. _ = await admin.a_create_authentication_flow(
  5175. payload={"alias": "test-create", "providerId": "basic-flow"},
  5176. )
  5177. _ = await admin.a_create_authentication_flow_execution(
  5178. payload={"provider": "auth-cookie"},
  5179. flow_alias="test-create",
  5180. )
  5181. _ = await admin.a_create_authentication_flow_execution(
  5182. payload={"provider": "auth-cookie"},
  5183. flow_alias="test-create",
  5184. )
  5185. executions = await admin.a_get_authentication_flow_executions(flow_alias="test-create")
  5186. priority_list = [ex["id"] for ex in executions]
  5187. _ = await admin.a_change_execution_priority(priority_list[1], 1)
  5188. new_executions = await admin.a_get_authentication_flow_executions(flow_alias="test-create")
  5189. assert executions != new_executions
  5190. _ = await admin.a_change_execution_priority(priority_list[1], -1)
  5191. new_executions = await admin.a_get_authentication_flow_executions(flow_alias="test-create")
  5192. assert executions == new_executions
  5193. @pytest.mark.asyncio
  5194. async def test_a_authentication_configs(admin: KeycloakAdmin, realm: str) -> None:
  5195. """
  5196. Test authentication configs.
  5197. :param admin: Keycloak Admin client
  5198. :type admin: KeycloakAdmin
  5199. :param realm: Keycloak realm
  5200. :type realm: str
  5201. """
  5202. admin.change_current_realm(realm)
  5203. # Test list of auth providers
  5204. res = await admin.a_get_authenticator_providers()
  5205. assert len(res) <= 40
  5206. res = await admin.a_get_authenticator_provider_config_description(provider_id="auth-cookie")
  5207. assert res == {
  5208. "helpText": "Validates the SSO cookie set by the auth server.",
  5209. "name": "Cookie",
  5210. "properties": [],
  5211. "providerId": "auth-cookie",
  5212. }
  5213. # Test authenticator config
  5214. executions = await admin.a_get_authentication_flow_executions(flow_alias="browser")
  5215. execution = next(ex for ex in executions if ex["configurable"])
  5216. _ = await admin.a_create_execution_config(
  5217. execution["id"],
  5218. {
  5219. "alias": "test.provisioning.property",
  5220. "config": {"test.provisioning.property": "value2"},
  5221. },
  5222. )
  5223. executions = await admin.a_get_authentication_flow_executions(flow_alias="browser")
  5224. execution_config_id = next(ex for ex in executions if ex.get("id") == execution["id"])[
  5225. "authenticationConfig"
  5226. ]
  5227. res = await admin.a_get_authenticator_config(config_id=execution_config_id)
  5228. assert res["config"]["test.provisioning.property"] == "value2"
  5229. # Test authenticator config
  5230. # Currently unable to find a sustainable way to fetch the config id,
  5231. # therefore testing only failures
  5232. with pytest.raises(KeycloakGetError) as err:
  5233. await admin.a_get_authenticator_config(config_id="bad")
  5234. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5235. with pytest.raises(KeycloakPutError) as err:
  5236. await admin.a_update_authenticator_config(payload={}, config_id="bad")
  5237. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5238. with pytest.raises(KeycloakDeleteError) as err:
  5239. await admin.a_delete_authenticator_config(config_id="bad")
  5240. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5241. @pytest.mark.asyncio
  5242. async def test_a_sync_users(admin: KeycloakAdmin, realm: str) -> None:
  5243. """
  5244. Test sync users.
  5245. :param admin: Keycloak Admin client
  5246. :type admin: KeycloakAdmin
  5247. :param realm: Keycloak realm
  5248. :type realm: str
  5249. """
  5250. await admin.a_change_current_realm(realm)
  5251. # Only testing the error message
  5252. with pytest.raises(KeycloakPostError) as err:
  5253. await admin.a_sync_users(storage_id="does-not-exist", action="triggerFullSync")
  5254. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5255. @pytest.mark.asyncio
  5256. async def test_a_client_scopes(admin: KeycloakAdmin, realm: str) -> None:
  5257. """
  5258. Test client scopes.
  5259. :param admin: Keycloak Admin client
  5260. :type admin: KeycloakAdmin
  5261. :param realm: Keycloak realm
  5262. :type realm: str
  5263. """
  5264. await admin.a_change_current_realm(realm)
  5265. # Test get client scopes
  5266. res = await admin.a_get_client_scopes()
  5267. scope_names = {x["name"] for x in res}
  5268. assert len(res) in [10, 11, 13, 14]
  5269. assert "email" in scope_names
  5270. assert "profile" in scope_names
  5271. assert "offline_access" in scope_names
  5272. with pytest.raises(KeycloakGetError) as err:
  5273. await admin.a_get_client_scope(client_scope_id="does-not-exist")
  5274. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5275. scope = await admin.a_get_client_scope(client_scope_id=res[0]["id"])
  5276. assert res[0] == scope
  5277. scope = await admin.a_get_client_scope_by_name(client_scope_name=res[0]["name"])
  5278. assert res[0] == scope
  5279. # Test create client scope
  5280. res = await admin.a_create_client_scope(
  5281. payload={"name": "test-scope", "protocol": "openid-connect"},
  5282. skip_exists=True,
  5283. )
  5284. assert res
  5285. res2 = await admin.a_create_client_scope(
  5286. payload={"name": "test-scope", "protocol": "openid-connect"},
  5287. skip_exists=True,
  5288. )
  5289. assert res == res2
  5290. with pytest.raises(KeycloakPostError) as err:
  5291. await admin.a_create_client_scope(
  5292. payload={"name": "test-scope", "protocol": "openid-connect"},
  5293. skip_exists=False,
  5294. )
  5295. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  5296. # Test update client scope
  5297. with pytest.raises(KeycloakPutError) as err:
  5298. await admin.a_update_client_scope(client_scope_id="does-not-exist", payload={})
  5299. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5300. res_update = await admin.a_update_client_scope(
  5301. client_scope_id=res,
  5302. payload={"name": "test-scope-update"},
  5303. )
  5304. assert res_update == {}
  5305. assert (await admin.a_get_client_scope(client_scope_id=res))["name"] == "test-scope-update"
  5306. # Test get mappers
  5307. mappers = await admin.a_get_mappers_from_client_scope(client_scope_id=res)
  5308. assert mappers == []
  5309. # Test add mapper
  5310. with pytest.raises(KeycloakPostError) as err:
  5311. await admin.a_add_mapper_to_client_scope(client_scope_id=res, payload={})
  5312. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  5313. res_add = await admin.a_add_mapper_to_client_scope(
  5314. client_scope_id=res,
  5315. payload={
  5316. "name": "test-mapper",
  5317. "protocol": "openid-connect",
  5318. "protocolMapper": "oidc-usermodel-attribute-mapper",
  5319. },
  5320. )
  5321. assert res_add == b""
  5322. assert len(await admin.a_get_mappers_from_client_scope(client_scope_id=res)) == 1
  5323. # Test update mapper
  5324. test_mapper = (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]
  5325. with pytest.raises(KeycloakPutError) as err:
  5326. await admin.a_update_mapper_in_client_scope(
  5327. client_scope_id="does-not-exist",
  5328. protocol_mapper_id=test_mapper["id"],
  5329. payload={},
  5330. )
  5331. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5332. test_mapper["config"]["user.attribute"] = "test"
  5333. res_update = await admin.a_update_mapper_in_client_scope(
  5334. client_scope_id=res,
  5335. protocol_mapper_id=test_mapper["id"],
  5336. payload=test_mapper,
  5337. )
  5338. assert res_update == {}
  5339. assert (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]["config"][
  5340. "user.attribute"
  5341. ] == "test"
  5342. # Test delete mapper
  5343. res_del = await admin.a_delete_mapper_from_client_scope(
  5344. client_scope_id=res,
  5345. protocol_mapper_id=test_mapper["id"],
  5346. )
  5347. assert res_del == {}
  5348. with pytest.raises(KeycloakDeleteError) as err:
  5349. await admin.a_delete_mapper_from_client_scope(
  5350. client_scope_id=res,
  5351. protocol_mapper_id=test_mapper["id"],
  5352. )
  5353. assert err.match('404: b\'{"error":"Model not found".*}\'')
  5354. # Test default default scopes
  5355. res_defaults = await admin.a_get_default_default_client_scopes()
  5356. assert len(res_defaults) in [6, 7, 8]
  5357. with pytest.raises(KeycloakPutError) as err:
  5358. await admin.a_add_default_default_client_scope(scope_id="does-not-exist")
  5359. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5360. res_add = await admin.a_add_default_default_client_scope(scope_id=res)
  5361. assert res_add == {}
  5362. assert len(admin.get_default_default_client_scopes()) in [7, 8, 9]
  5363. with pytest.raises(KeycloakDeleteError) as err:
  5364. await admin.a_delete_default_default_client_scope(scope_id="does-not-exist")
  5365. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5366. res_del = await admin.a_delete_default_default_client_scope(scope_id=res)
  5367. assert res_del == {}
  5368. assert len(admin.get_default_default_client_scopes()) in [6, 7, 8]
  5369. # Test default optional scopes
  5370. res_defaults = await admin.a_get_default_optional_client_scopes()
  5371. assert len(res_defaults) in [4, 5]
  5372. with pytest.raises(KeycloakPutError) as err:
  5373. await admin.a_add_default_optional_client_scope(scope_id="does-not-exist")
  5374. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5375. res_add = await admin.a_add_default_optional_client_scope(scope_id=res)
  5376. assert res_add == {}
  5377. assert len(await admin.a_get_default_optional_client_scopes()) in [5, 6]
  5378. with pytest.raises(KeycloakDeleteError) as err:
  5379. await admin.a_delete_default_optional_client_scope(scope_id="does-not-exist")
  5380. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5381. res_del = await admin.a_delete_default_optional_client_scope(scope_id=res)
  5382. assert res_del == {}
  5383. assert len(await admin.a_get_default_optional_client_scopes()) in [4, 5]
  5384. # Test client scope delete
  5385. res_del = await admin.a_delete_client_scope(client_scope_id=res)
  5386. assert res_del == {}
  5387. with pytest.raises(KeycloakDeleteError) as err:
  5388. await admin.a_delete_client_scope(client_scope_id=res)
  5389. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5390. @pytest.mark.asyncio
  5391. async def test_a_components(admin: KeycloakAdmin, realm: str) -> None:
  5392. """
  5393. Test components.
  5394. :param admin: Keycloak Admin client
  5395. :type admin: KeycloakAdmin
  5396. :param realm: Keycloak realm
  5397. :type realm: str
  5398. """
  5399. await admin.a_change_current_realm(realm)
  5400. # Test get components
  5401. res = await admin.a_get_components()
  5402. assert len(res) == 12
  5403. with pytest.raises(KeycloakGetError) as err:
  5404. await admin.a_get_component(component_id="does-not-exist")
  5405. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5406. res_get = await admin.a_get_component(component_id=res[0]["id"])
  5407. assert res_get == res[0]
  5408. # Test create component
  5409. with pytest.raises(KeycloakPostError) as err:
  5410. await admin.a_create_component(payload={"bad": "dict"})
  5411. assert err.match("Unrecognized field")
  5412. res = await admin.a_create_component(
  5413. payload={
  5414. "name": "Test Component",
  5415. "providerId": "max-clients",
  5416. "providerType": "org.keycloak.services.clientregistration."
  5417. "policy.ClientRegistrationPolicy",
  5418. "config": {"max-clients": ["1000"]},
  5419. },
  5420. )
  5421. assert res
  5422. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component"
  5423. # Test update component
  5424. component = await admin.a_get_component(component_id=res)
  5425. component["name"] = "Test Component Update"
  5426. with pytest.raises(KeycloakPutError) as err:
  5427. await admin.a_update_component(component_id="does-not-exist", payload={})
  5428. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5429. res_upd = await admin.a_update_component(component_id=res, payload=component)
  5430. assert res_upd == {}
  5431. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component Update"
  5432. # Test delete component
  5433. res_del = await admin.a_delete_component(component_id=res)
  5434. assert res_del == {}
  5435. with pytest.raises(KeycloakDeleteError) as err:
  5436. await admin.a_delete_component(component_id=res)
  5437. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5438. @pytest.mark.asyncio
  5439. async def test_a_keys(admin: KeycloakAdmin, realm: str) -> None:
  5440. """
  5441. Test keys.
  5442. :param admin: Keycloak Admin client
  5443. :type admin: KeycloakAdmin
  5444. :param realm: Keycloak realm
  5445. :type realm: str
  5446. """
  5447. await admin.a_change_current_realm(realm)
  5448. assert set((await admin.a_get_keys())["active"].keys()) == {
  5449. "AES",
  5450. "HS256",
  5451. "RS256",
  5452. "RSA-OAEP",
  5453. } or set((await admin.a_get_keys())["active"].keys()) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  5454. assert {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  5455. "HS256",
  5456. "RSA-OAEP",
  5457. "AES",
  5458. "RS256",
  5459. } or {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  5460. "HS512",
  5461. "RSA-OAEP",
  5462. "AES",
  5463. "RS256",
  5464. }
  5465. @pytest.mark.asyncio
  5466. async def test_a_admin_events(admin: KeycloakAdmin, realm: str) -> None:
  5467. """
  5468. Test events.
  5469. :param admin: Keycloak Admin client
  5470. :type admin: KeycloakAdmin
  5471. :param realm: Keycloak realm
  5472. :type realm: str
  5473. """
  5474. await admin.a_change_current_realm(realm)
  5475. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  5476. events = await admin.a_get_admin_events()
  5477. assert events == []
  5478. @pytest.mark.asyncio
  5479. async def test_a_user_events(admin: KeycloakAdmin, realm: str) -> None:
  5480. """
  5481. Test events.
  5482. :param admin: Keycloak Admin client
  5483. :type admin: KeycloakAdmin
  5484. :param realm: Keycloak realm
  5485. :type realm: str
  5486. """
  5487. await admin.a_change_current_realm(realm)
  5488. events = await admin.a_get_events()
  5489. assert events == []
  5490. with pytest.raises(KeycloakPutError) as err:
  5491. await admin.a_set_events(payload={"bad": "conf"})
  5492. assert err.match("Unrecognized field")
  5493. res = await admin.a_set_events(
  5494. payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True},
  5495. )
  5496. assert res == {}
  5497. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  5498. events = await admin.a_get_events()
  5499. assert events == []
  5500. @pytest.mark.asyncio
  5501. @freezegun.freeze_time("2023-02-25 10:00:00")
  5502. async def test_a_auto_refresh(admin_frozen: KeycloakAdmin, realm: str) -> None:
  5503. """
  5504. Test auto refresh token.
  5505. :param admin_frozen: Keycloak Admin client with time frozen in place
  5506. :type admin_frozen: KeycloakAdmin
  5507. :param realm: Keycloak realm
  5508. :type realm: str
  5509. """
  5510. admin = admin_frozen
  5511. admin.get_realm(realm)
  5512. # Test get refresh
  5513. admin.connection.custom_headers = {
  5514. "Authorization": "Bearer bad",
  5515. "Content-Type": "application/json",
  5516. }
  5517. res = await admin.a_get_realm(realm_name=realm)
  5518. assert res["realm"] == realm
  5519. # Freeze time to simulate the access token expiring
  5520. with freezegun.freeze_time("2023-02-25 10:05:00"):
  5521. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:05:00Z")
  5522. assert await admin.a_get_realm(realm_name=realm)
  5523. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:05:00Z")
  5524. # Test bad refresh token, but first make sure access token has expired again
  5525. with freezegun.freeze_time("2023-02-25 10:10:00"):
  5526. admin.connection.custom_headers = {"Content-Type": "application/json"}
  5527. admin.connection.token["refresh_token"] = "bad" # noqa: S105
  5528. with pytest.raises(KeycloakPostError) as err:
  5529. await admin.a_get_realm(realm_name="test-refresh")
  5530. assert err.match(
  5531. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'',
  5532. )
  5533. admin.connection.get_token()
  5534. # Test post refresh
  5535. with freezegun.freeze_time("2023-02-25 10:15:00"):
  5536. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:15:00Z")
  5537. admin.connection.token = None
  5538. assert await admin.a_create_realm(payload={"realm": "test-refresh"}) == b""
  5539. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:15:00Z")
  5540. # Test update refresh
  5541. with freezegun.freeze_time("2023-02-25 10:25:00"):
  5542. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:25:00Z")
  5543. admin.connection.token = None
  5544. assert (
  5545. await admin.a_update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  5546. == {}
  5547. )
  5548. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:25:00Z")
  5549. # Test delete refresh
  5550. with freezegun.freeze_time("2023-02-25 10:35:00"):
  5551. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:35:00Z")
  5552. admin.connection.token = None
  5553. assert await admin.a_delete_realm(realm_name="test-refresh") == {}
  5554. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:35:00Z")
  5555. @pytest.mark.asyncio
  5556. async def test_a_get_required_actions(admin: KeycloakAdmin, realm: str) -> None:
  5557. """
  5558. Test required actions.
  5559. :param admin: Keycloak Admin client
  5560. :type admin: KeycloakAdmin
  5561. :param realm: Keycloak realm
  5562. :type realm: str
  5563. """
  5564. await admin.a_change_current_realm(realm)
  5565. ractions = await admin.a_get_required_actions()
  5566. assert isinstance(ractions, list)
  5567. for ra in ractions:
  5568. for key in [
  5569. "alias",
  5570. "name",
  5571. "providerId",
  5572. "enabled",
  5573. "defaultAction",
  5574. "priority",
  5575. "config",
  5576. ]:
  5577. assert key in ra
  5578. @pytest.mark.asyncio
  5579. async def test_a_get_required_action_by_alias(admin: KeycloakAdmin, realm: str) -> None:
  5580. """
  5581. Test get required action by alias.
  5582. :param admin: Keycloak Admin client
  5583. :type admin: KeycloakAdmin
  5584. :param realm: Keycloak realm
  5585. :type realm: str
  5586. """
  5587. await admin.a_change_current_realm(realm)
  5588. ractions = await admin.a_get_required_actions()
  5589. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5590. assert ra in ractions
  5591. assert ra["alias"] == "UPDATE_PASSWORD"
  5592. assert await admin.a_get_required_action_by_alias("does-not-exist") is None
  5593. @pytest.mark.asyncio
  5594. async def test_a_update_required_action(admin: KeycloakAdmin, realm: str) -> None:
  5595. """
  5596. Test update required action.
  5597. :param admin: Keycloak Admin client
  5598. :type admin: KeycloakAdmin
  5599. :param realm: Keycloak realm
  5600. :type realm: str
  5601. """
  5602. await admin.a_change_current_realm(realm)
  5603. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5604. old = copy.deepcopy(ra)
  5605. ra["enabled"] = False
  5606. admin.update_required_action("UPDATE_PASSWORD", ra)
  5607. newra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5608. assert old != newra
  5609. assert newra["enabled"] is False
  5610. @pytest.mark.asyncio
  5611. async def test_a_get_composite_client_roles_of_group(
  5612. admin: KeycloakAdmin,
  5613. realm: str,
  5614. client: str,
  5615. group: str,
  5616. composite_client_role: str,
  5617. ) -> None:
  5618. """
  5619. Test get composite client roles of group.
  5620. :param admin: Keycloak Admin client
  5621. :type admin: KeycloakAdmin
  5622. :param realm: Keycloak realm
  5623. :type realm: str
  5624. :param client: Keycloak client
  5625. :type client: str
  5626. :param group: Keycloak group
  5627. :type group: str
  5628. :param composite_client_role: Composite client role
  5629. :type composite_client_role: str
  5630. """
  5631. await admin.a_change_current_realm(realm)
  5632. role = await admin.a_get_client_role(client, composite_client_role)
  5633. await admin.a_assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  5634. result = await admin.a_get_composite_client_roles_of_group(client, group)
  5635. assert role["id"] in [x["id"] for x in result]
  5636. @pytest.mark.asyncio
  5637. async def test_a_get_role_client_level_children(
  5638. admin: KeycloakAdmin,
  5639. realm: str,
  5640. client: str,
  5641. composite_client_role: str,
  5642. client_role: str,
  5643. ) -> None:
  5644. """
  5645. Test get children of composite client role.
  5646. :param admin: Keycloak Admin client
  5647. :type admin: KeycloakAdmin
  5648. :param realm: Keycloak realm
  5649. :type realm: str
  5650. :param client: Keycloak client
  5651. :type client: str
  5652. :param composite_client_role: Composite client role
  5653. :type composite_client_role: str
  5654. :param client_role: Client role
  5655. :type client_role: str
  5656. """
  5657. await admin.a_change_current_realm(realm)
  5658. child = await admin.a_get_client_role(client, client_role)
  5659. parent = await admin.a_get_client_role(client, composite_client_role)
  5660. res = await admin.a_get_role_client_level_children(client, parent["id"])
  5661. assert child["id"] in [x["id"] for x in res]
  5662. @pytest.mark.asyncio
  5663. async def test_a_upload_certificate(
  5664. admin: KeycloakAdmin,
  5665. realm: str,
  5666. client: str,
  5667. selfsigned_cert: tuple,
  5668. ) -> None:
  5669. """
  5670. Test upload certificate.
  5671. :param admin: Keycloak Admin client
  5672. :type admin: KeycloakAdmin
  5673. :param realm: Keycloak realm
  5674. :type realm: str
  5675. :param client: Keycloak client
  5676. :type client: str
  5677. :param selfsigned_cert: Selfsigned certificates
  5678. :type selfsigned_cert: tuple
  5679. """
  5680. await admin.a_change_current_realm(realm)
  5681. cert, _ = selfsigned_cert
  5682. cert = cert.decode("utf-8").strip()
  5683. admin.upload_certificate(client, cert)
  5684. cl = await admin.a_get_client(client)
  5685. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  5686. @pytest.mark.asyncio
  5687. async def test_a_get_bruteforce_status_for_user(
  5688. admin: KeycloakAdmin,
  5689. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5690. realm: str,
  5691. ) -> None:
  5692. """
  5693. Test users.
  5694. :param admin: Keycloak Admin client
  5695. :type admin: KeycloakAdmin
  5696. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5697. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5698. :param realm: Keycloak realm
  5699. :type realm: str
  5700. """
  5701. oid, username, password = oid_with_credentials
  5702. await admin.a_change_current_realm(realm)
  5703. # Turn on bruteforce protection
  5704. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5705. res = await admin.a_get_realm(realm_name=realm)
  5706. assert res["bruteForceProtected"] is True
  5707. # Test login user with wrong credentials
  5708. with contextlib.suppress(KeycloakAuthenticationError):
  5709. oid.token(username=username, password="wrongpassword") # noqa: S106
  5710. user_id = await admin.a_get_user_id(username)
  5711. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5712. assert bruteforce_status["numFailures"] == 1
  5713. # Cleanup
  5714. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5715. res = await admin.a_get_realm(realm_name=realm)
  5716. assert res["bruteForceProtected"] is False
  5717. @pytest.mark.asyncio
  5718. async def test_a_clear_bruteforce_attempts_for_user(
  5719. admin: KeycloakAdmin,
  5720. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5721. realm: str,
  5722. ) -> None:
  5723. """
  5724. Test users.
  5725. :param admin: Keycloak Admin client
  5726. :type admin: KeycloakAdmin
  5727. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5728. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5729. :param realm: Keycloak realm
  5730. :type realm: str
  5731. """
  5732. oid, username, password = oid_with_credentials
  5733. await admin.a_change_current_realm(realm)
  5734. # Turn on bruteforce protection
  5735. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5736. res = await admin.a_get_realm(realm_name=realm)
  5737. assert res["bruteForceProtected"] is True
  5738. # Test login user with wrong credentials
  5739. with contextlib.suppress(KeycloakAuthenticationError):
  5740. oid.token(username=username, password="wrongpassword") # noqa: S106
  5741. user_id = await admin.a_get_user_id(username)
  5742. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5743. assert bruteforce_status["numFailures"] == 1
  5744. res = await admin.a_clear_bruteforce_attempts_for_user(user_id)
  5745. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5746. assert bruteforce_status["numFailures"] == 0
  5747. # Cleanup
  5748. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5749. res = await admin.a_get_realm(realm_name=realm)
  5750. assert res["bruteForceProtected"] is False
  5751. @pytest.mark.asyncio
  5752. async def test_a_clear_bruteforce_attempts_for_all_users(
  5753. admin: KeycloakAdmin,
  5754. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5755. realm: str,
  5756. ) -> None:
  5757. """
  5758. Test users.
  5759. :param admin: Keycloak Admin client
  5760. :type admin: KeycloakAdmin
  5761. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5762. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5763. :param realm: Keycloak realm
  5764. :type realm: str
  5765. """
  5766. oid, username, password = oid_with_credentials
  5767. await admin.a_change_current_realm(realm)
  5768. # Turn on bruteforce protection
  5769. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5770. res = await admin.a_get_realm(realm_name=realm)
  5771. assert res["bruteForceProtected"] is True
  5772. # Test login user with wrong credentials
  5773. with contextlib.suppress(KeycloakAuthenticationError):
  5774. oid.token(username=username, password="wrongpassword") # noqa: S106
  5775. user_id = await admin.a_get_user_id(username)
  5776. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5777. assert bruteforce_status["numFailures"] == 1
  5778. res = await admin.a_clear_all_bruteforce_attempts()
  5779. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5780. assert bruteforce_status["numFailures"] == 0
  5781. # Cleanup
  5782. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5783. res = await admin.a_get_realm(realm_name=realm)
  5784. assert res["bruteForceProtected"] is False
  5785. @pytest.mark.asyncio
  5786. async def test_a_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  5787. """
  5788. Test that the default realm role is present in a brand new realm.
  5789. :param realm: Realm name
  5790. :type realm: str
  5791. :param admin: Keycloak admin
  5792. :type admin: KeycloakAdmin
  5793. """
  5794. await admin.a_change_current_realm(realm)
  5795. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  5796. assert (
  5797. len(
  5798. [
  5799. x["name"]
  5800. for x in await admin.a_get_realm_roles()
  5801. if x["name"] == f"default-roles-{realm}"
  5802. ],
  5803. )
  5804. == 1
  5805. )
  5806. @pytest.mark.asyncio
  5807. async def test_a_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  5808. """
  5809. Test getter for the ID of the default realm role.
  5810. :param realm: Realm name
  5811. :type realm: str
  5812. :param admin: Keycloak admin
  5813. :type admin: KeycloakAdmin
  5814. """
  5815. await admin.a_change_current_realm(realm)
  5816. assert await admin.a_get_default_realm_role_id() == next(
  5817. x["id"] for x in await admin.a_get_realm_roles() if x["name"] == f"default-roles-{realm}"
  5818. )
  5819. @pytest.mark.asyncio
  5820. async def test_a_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  5821. """
  5822. Test getting, adding and deleting default realm roles.
  5823. :param realm: Realm name
  5824. :type realm: str
  5825. :param admin: Keycloak admin
  5826. :type admin: KeycloakAdmin
  5827. """
  5828. await admin.a_change_current_realm(realm)
  5829. # Test listing all default realm roles
  5830. roles = await admin.a_get_realm_default_roles()
  5831. assert len(roles) == 2
  5832. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  5833. await admin.a_change_current_realm("doesnotexist")
  5834. with pytest.raises(KeycloakGetError) as err:
  5835. await admin.a_get_realm_default_roles()
  5836. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  5837. await admin.a_change_current_realm(realm)
  5838. # Test removing a default realm role
  5839. res = await admin.a_remove_realm_default_roles(payload=[roles[0]])
  5840. assert res == {}
  5841. assert roles[0] not in await admin.a_get_realm_default_roles()
  5842. assert len(await admin.a_get_realm_default_roles()) == 1
  5843. with pytest.raises(KeycloakDeleteError) as err:
  5844. await admin.a_remove_realm_default_roles(payload=[{"id": "bad id"}])
  5845. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5846. # Test adding a default realm role
  5847. res = await admin.a_add_realm_default_roles(payload=[roles[0]])
  5848. assert res == {}
  5849. assert roles[0] in await admin.a_get_realm_default_roles()
  5850. assert len(await admin.a_get_realm_default_roles()) == 2
  5851. with pytest.raises(KeycloakPostError) as err:
  5852. await admin.a_add_realm_default_roles(payload=[{"id": "bad id"}])
  5853. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5854. @pytest.mark.asyncio
  5855. async def test_a_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  5856. """
  5857. Test clearing the keys cache.
  5858. :param realm: Realm name
  5859. :type realm: str
  5860. :param admin: Keycloak admin
  5861. :type admin: KeycloakAdmin
  5862. """
  5863. await admin.a_change_current_realm(realm)
  5864. res = await admin.a_clear_keys_cache()
  5865. assert res == {}
  5866. @pytest.mark.asyncio
  5867. async def test_a_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  5868. """
  5869. Test clearing the realm cache.
  5870. :param realm: Realm name
  5871. :type realm: str
  5872. :param admin: Keycloak admin
  5873. :type admin: KeycloakAdmin
  5874. """
  5875. await admin.a_change_current_realm(realm)
  5876. res = await admin.a_clear_realm_cache()
  5877. assert res == {}
  5878. @pytest.mark.asyncio
  5879. async def test_a_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  5880. """
  5881. Test clearing the user cache.
  5882. :param realm: Realm name
  5883. :type realm: str
  5884. :param admin: Keycloak admin
  5885. :type admin: KeycloakAdmin
  5886. """
  5887. await admin.a_change_current_realm(realm)
  5888. res = await admin.a_clear_user_cache()
  5889. assert res == {}
  5890. @pytest.mark.asyncio
  5891. async def test_a_initial_access_token(
  5892. admin: KeycloakAdmin,
  5893. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5894. ) -> None:
  5895. """
  5896. Test initial access token and client creation.
  5897. :param admin: Keycloak admin
  5898. :type admin: KeycloakAdmin
  5899. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5900. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5901. """
  5902. res = await admin.a_create_initial_access_token(2, 3)
  5903. assert "token" in res
  5904. assert res["count"] == 2
  5905. assert res["expiration"] == 3
  5906. oid, username, password = oid_with_credentials
  5907. client = str(uuid.uuid4())
  5908. secret = str(uuid.uuid4())
  5909. res = await oid.a_register_client(
  5910. token=res["token"],
  5911. payload={
  5912. "name": "DynamicRegisteredClient",
  5913. "clientId": client,
  5914. "enabled": True,
  5915. "publicClient": False,
  5916. "protocol": "openid-connect",
  5917. "secret": secret,
  5918. "clientAuthenticatorType": "client-secret",
  5919. },
  5920. )
  5921. assert res["clientId"] == client
  5922. new_secret = str(uuid.uuid4())
  5923. res = await oid.a_update_client(
  5924. res["registrationAccessToken"],
  5925. client,
  5926. payload={"secret": new_secret},
  5927. )
  5928. assert res["secret"] == new_secret
  5929. @pytest.mark.asyncio
  5930. async def test_a_refresh_token(admin: KeycloakAdmin) -> None:
  5931. """
  5932. Test refresh token on connection even if it is expired.
  5933. :param admin: Keycloak admin
  5934. :type admin: KeycloakAdmin
  5935. """
  5936. admin.get_realms()
  5937. assert admin.connection.token is not None
  5938. await admin.a_user_logout(await admin.a_get_user_id(admin.connection.username))
  5939. admin.connection.refresh_token()
  5940. def test_counter_part() -> None:
  5941. """Test that each function has its async counter part."""
  5942. admin_methods = [func for func in dir(KeycloakAdmin) if callable(getattr(KeycloakAdmin, func))]
  5943. sync_methods = [
  5944. method
  5945. for method in admin_methods
  5946. if not method.startswith("a_") and not method.startswith("_")
  5947. ]
  5948. async_methods = [
  5949. method for method in admin_methods if iscoroutinefunction(getattr(KeycloakAdmin, method))
  5950. ]
  5951. for method in sync_methods:
  5952. async_method = f"a_{method}"
  5953. assert async_method in admin_methods
  5954. sync_sign = signature(getattr(KeycloakAdmin, method))
  5955. async_sign = signature(getattr(KeycloakAdmin, async_method))
  5956. assert sync_sign.parameters == async_sign.parameters, f"Parameters mismatch for {method}"
  5957. for async_method in async_methods:
  5958. if async_method[2:].startswith("_"):
  5959. continue
  5960. assert async_method[2:] in sync_methods