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.

6899 lines
238 KiB

10 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. # Test flow executions
  2087. res = admin.get_authentication_flow_executions(flow_alias="browser")
  2088. assert len(res) in [8, 12], res
  2089. with pytest.raises(KeycloakGetError) as err:
  2090. admin.get_authentication_flow_executions(flow_alias="bad")
  2091. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  2092. exec_id = res[0]["id"]
  2093. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  2094. assert set(res.keys()).issubset(
  2095. {
  2096. "alternative",
  2097. "authenticator",
  2098. "authenticatorFlow",
  2099. "autheticatorFlow",
  2100. "conditional",
  2101. "disabled",
  2102. "enabled",
  2103. "id",
  2104. "parentFlow",
  2105. "priority",
  2106. "required",
  2107. "requirement",
  2108. },
  2109. ), res.keys()
  2110. with pytest.raises(KeycloakGetError) as err:
  2111. admin.get_authentication_flow_execution(execution_id="bad")
  2112. assert err.match(ILLEGAL_EXECUTION_REGEX)
  2113. with pytest.raises(KeycloakPostError) as err:
  2114. admin.create_authentication_flow_execution(payload={}, flow_alias="browser")
  2115. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  2116. res = admin.create_authentication_flow_execution(
  2117. payload={"provider": "auth-cookie"},
  2118. flow_alias="test-create",
  2119. )
  2120. assert res == b""
  2121. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  2122. with pytest.raises(KeycloakPutError) as err:
  2123. admin.update_authentication_flow_executions(
  2124. payload={"required": "yes"},
  2125. flow_alias="test-create",
  2126. )
  2127. assert err.match("Unrecognized field")
  2128. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  2129. payload["displayName"] = "test"
  2130. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  2131. assert res or (res == {})
  2132. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  2133. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  2134. assert res == {}
  2135. with pytest.raises(KeycloakDeleteError) as err:
  2136. admin.delete_authentication_flow_execution(execution_id=exec_id)
  2137. assert err.match(ILLEGAL_EXECUTION_REGEX)
  2138. # Test subflows
  2139. res = admin.create_authentication_flow_subflow(
  2140. payload={
  2141. "alias": "test-subflow",
  2142. "provider": "basic-flow",
  2143. "type": "something",
  2144. "description": "something",
  2145. },
  2146. flow_alias="test-browser",
  2147. )
  2148. assert res == b""
  2149. with pytest.raises(KeycloakPostError) as err:
  2150. admin.create_authentication_flow_subflow(
  2151. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  2152. flow_alias="test-browser",
  2153. )
  2154. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  2155. res = admin.create_authentication_flow_subflow(
  2156. payload={
  2157. "alias": "test-subflow",
  2158. "provider": "basic-flow",
  2159. "type": "something",
  2160. "description": "something",
  2161. },
  2162. flow_alias="test-create",
  2163. skip_exists=True,
  2164. )
  2165. assert res == {"msg": "Already exists"}
  2166. # Test delete auth flow
  2167. flow_id = next(x for x in admin.get_authentication_flows() if x["alias"] == "test-browser")[
  2168. "id"
  2169. ]
  2170. res = admin.delete_authentication_flow(flow_id=flow_id)
  2171. assert res == {}
  2172. with pytest.raises(KeycloakDeleteError) as err:
  2173. admin.delete_authentication_flow(flow_id=flow_id)
  2174. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  2175. 'b\'{"error":"Flow not found"' in str(err)
  2176. )
  2177. def test_authentication_configs(admin: KeycloakAdmin, realm: str) -> None:
  2178. """
  2179. Test authentication configs.
  2180. :param admin: Keycloak Admin client
  2181. :type admin: KeycloakAdmin
  2182. :param realm: Keycloak realm
  2183. :type realm: str
  2184. """
  2185. admin.change_current_realm(realm)
  2186. # Test list of auth providers
  2187. res = admin.get_authenticator_providers()
  2188. assert len(res) <= 40
  2189. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  2190. assert res == {
  2191. "helpText": "Validates the SSO cookie set by the auth server.",
  2192. "name": "Cookie",
  2193. "properties": [],
  2194. "providerId": "auth-cookie",
  2195. }
  2196. # Test authenticator config
  2197. # Currently unable to find a sustainable way to fetch the config id,
  2198. # therefore testing only failures
  2199. with pytest.raises(KeycloakGetError) as err:
  2200. admin.get_authenticator_config(config_id="bad")
  2201. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2202. with pytest.raises(KeycloakPutError) as err:
  2203. admin.update_authenticator_config(payload={}, config_id="bad")
  2204. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2205. with pytest.raises(KeycloakDeleteError) as err:
  2206. admin.delete_authenticator_config(config_id="bad")
  2207. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  2208. def test_sync_users(admin: KeycloakAdmin, realm: str) -> None:
  2209. """
  2210. Test sync users.
  2211. :param admin: Keycloak Admin client
  2212. :type admin: KeycloakAdmin
  2213. :param realm: Keycloak realm
  2214. :type realm: str
  2215. """
  2216. admin.change_current_realm(realm)
  2217. # Only testing the error message
  2218. with pytest.raises(KeycloakPostError) as err:
  2219. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  2220. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2221. def test_client_scopes(admin: KeycloakAdmin, realm: str) -> None:
  2222. """
  2223. Test client scopes.
  2224. :param admin: Keycloak Admin client
  2225. :type admin: KeycloakAdmin
  2226. :param realm: Keycloak realm
  2227. :type realm: str
  2228. """
  2229. admin.change_current_realm(realm)
  2230. # Test get client scopes
  2231. res = admin.get_client_scopes()
  2232. scope_names = {x["name"] for x in res}
  2233. assert len(res) in [10, 11, 13, 14]
  2234. assert "email" in scope_names
  2235. assert "profile" in scope_names
  2236. assert "offline_access" in scope_names
  2237. with pytest.raises(KeycloakGetError) as err:
  2238. admin.get_client_scope(client_scope_id="does-not-exist")
  2239. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2240. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  2241. assert res[0] == scope
  2242. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  2243. assert res[0] == scope
  2244. # Test create client scope
  2245. res = admin.create_client_scope(
  2246. payload={"name": "test-scope", "protocol": "openid-connect"},
  2247. skip_exists=True,
  2248. )
  2249. assert res
  2250. res2 = admin.create_client_scope(
  2251. payload={"name": "test-scope", "protocol": "openid-connect"},
  2252. skip_exists=True,
  2253. )
  2254. assert res == res2
  2255. with pytest.raises(KeycloakPostError) as err:
  2256. admin.create_client_scope(
  2257. payload={"name": "test-scope", "protocol": "openid-connect"},
  2258. skip_exists=False,
  2259. )
  2260. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  2261. # Test update client scope
  2262. with pytest.raises(KeycloakPutError) as err:
  2263. admin.update_client_scope(client_scope_id="does-not-exist", payload={})
  2264. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2265. res_update = admin.update_client_scope(
  2266. client_scope_id=res,
  2267. payload={"name": "test-scope-update"},
  2268. )
  2269. assert res_update == {}
  2270. assert admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  2271. # Test get mappers
  2272. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  2273. assert mappers == []
  2274. # Test add mapper
  2275. with pytest.raises(KeycloakPostError) as err:
  2276. admin.add_mapper_to_client_scope(client_scope_id=res, payload={})
  2277. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  2278. res_add = admin.add_mapper_to_client_scope(
  2279. client_scope_id=res,
  2280. payload={
  2281. "name": "test-mapper",
  2282. "protocol": "openid-connect",
  2283. "protocolMapper": "oidc-usermodel-attribute-mapper",
  2284. },
  2285. )
  2286. assert res_add == b""
  2287. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  2288. # Test update mapper
  2289. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  2290. with pytest.raises(KeycloakPutError) as err:
  2291. admin.update_mapper_in_client_scope(
  2292. client_scope_id="does-not-exist",
  2293. protocol_mapper_id=test_mapper["id"],
  2294. payload={},
  2295. )
  2296. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2297. test_mapper["config"]["user.attribute"] = "test"
  2298. res_update = admin.update_mapper_in_client_scope(
  2299. client_scope_id=res,
  2300. protocol_mapper_id=test_mapper["id"],
  2301. payload=test_mapper,
  2302. )
  2303. assert res_update == {}
  2304. assert (
  2305. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  2306. == "test"
  2307. )
  2308. # Test delete mapper
  2309. res_del = admin.delete_mapper_from_client_scope(
  2310. client_scope_id=res,
  2311. protocol_mapper_id=test_mapper["id"],
  2312. )
  2313. assert res_del == {}
  2314. with pytest.raises(KeycloakDeleteError) as err:
  2315. admin.delete_mapper_from_client_scope(
  2316. client_scope_id=res,
  2317. protocol_mapper_id=test_mapper["id"],
  2318. )
  2319. assert err.match('404: b\'{"error":"Model not found".*}\'')
  2320. # Test default default scopes
  2321. res_defaults = admin.get_default_default_client_scopes()
  2322. assert len(res_defaults) in [6, 7, 8]
  2323. with pytest.raises(KeycloakPutError) as err:
  2324. admin.add_default_default_client_scope(scope_id="does-not-exist")
  2325. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2326. res_add = admin.add_default_default_client_scope(scope_id=res)
  2327. assert res_add == {}
  2328. assert len(admin.get_default_default_client_scopes()) in [7, 8, 9]
  2329. with pytest.raises(KeycloakDeleteError) as err:
  2330. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  2331. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2332. res_del = admin.delete_default_default_client_scope(scope_id=res)
  2333. assert res_del == {}
  2334. assert len(admin.get_default_default_client_scopes()) in [6, 7, 8]
  2335. # Test default optional scopes
  2336. res_defaults = admin.get_default_optional_client_scopes()
  2337. assert len(res_defaults) in [4, 5]
  2338. with pytest.raises(KeycloakPutError) as err:
  2339. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  2340. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2341. res_add = admin.add_default_optional_client_scope(scope_id=res)
  2342. assert res_add == {}
  2343. assert len(admin.get_default_optional_client_scopes()) in [5, 6]
  2344. with pytest.raises(KeycloakDeleteError) as err:
  2345. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  2346. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2347. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  2348. assert res_del == {}
  2349. assert len(admin.get_default_optional_client_scopes()) in [4, 5]
  2350. # Test client scope delete
  2351. res_del = admin.delete_client_scope(client_scope_id=res)
  2352. assert res_del == {}
  2353. with pytest.raises(KeycloakDeleteError) as err:
  2354. admin.delete_client_scope(client_scope_id=res)
  2355. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2356. def test_components(admin: KeycloakAdmin, realm: str) -> None:
  2357. """
  2358. Test components.
  2359. :param admin: Keycloak Admin client
  2360. :type admin: KeycloakAdmin
  2361. :param realm: Keycloak realm
  2362. :type realm: str
  2363. """
  2364. admin.change_current_realm(realm)
  2365. # Test get components
  2366. res = admin.get_components()
  2367. assert len(res) == 12
  2368. with pytest.raises(KeycloakGetError) as err:
  2369. admin.get_component(component_id="does-not-exist")
  2370. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2371. res_get = admin.get_component(component_id=res[0]["id"])
  2372. assert res_get == res[0]
  2373. # Test create component
  2374. with pytest.raises(KeycloakPostError) as err:
  2375. admin.create_component(payload={"bad": "dict"})
  2376. assert err.match("Unrecognized field")
  2377. res = admin.create_component(
  2378. payload={
  2379. "name": "Test Component",
  2380. "providerId": "max-clients",
  2381. "providerType": "org.keycloak.services.clientregistration.policy."
  2382. "ClientRegistrationPolicy",
  2383. "config": {"max-clients": ["1000"]},
  2384. },
  2385. )
  2386. assert res
  2387. assert admin.get_component(component_id=res)["name"] == "Test Component"
  2388. # Test update component
  2389. component = admin.get_component(component_id=res)
  2390. component["name"] = "Test Component Update"
  2391. with pytest.raises(KeycloakPutError) as err:
  2392. admin.update_component(component_id="does-not-exist", payload={})
  2393. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2394. res_upd = admin.update_component(component_id=res, payload=component)
  2395. assert res_upd == {}
  2396. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  2397. # Test delete component
  2398. res_del = admin.delete_component(component_id=res)
  2399. assert res_del == {}
  2400. with pytest.raises(KeycloakDeleteError) as err:
  2401. admin.delete_component(component_id=res)
  2402. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2403. def test_keys(admin: KeycloakAdmin, realm: str) -> None:
  2404. """
  2405. Test keys.
  2406. :param admin: Keycloak Admin client
  2407. :type admin: KeycloakAdmin
  2408. :param realm: Keycloak realm
  2409. :type realm: str
  2410. """
  2411. admin.change_current_realm(realm)
  2412. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} or set(
  2413. admin.get_keys()["active"].keys(),
  2414. ) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  2415. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2416. "HS256",
  2417. "RSA-OAEP",
  2418. "AES",
  2419. "RS256",
  2420. } or {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2421. "HS512",
  2422. "RSA-OAEP",
  2423. "AES",
  2424. "RS256",
  2425. }
  2426. def test_admin_events(admin: KeycloakAdmin, realm: str) -> None:
  2427. """
  2428. Test events.
  2429. :param admin: Keycloak Admin client
  2430. :type admin: KeycloakAdmin
  2431. :param realm: Keycloak realm
  2432. :type realm: str
  2433. """
  2434. admin.change_current_realm(realm)
  2435. admin.create_client(payload={"name": "test", "clientId": "test"})
  2436. events = admin.get_admin_events()
  2437. assert events == []
  2438. def test_user_events(admin: KeycloakAdmin, realm: str) -> None:
  2439. """
  2440. Test events.
  2441. :param admin: Keycloak Admin client
  2442. :type admin: KeycloakAdmin
  2443. :param realm: Keycloak realm
  2444. :type realm: str
  2445. """
  2446. admin.change_current_realm(realm)
  2447. events = admin.get_events()
  2448. assert events == []
  2449. with pytest.raises(KeycloakPutError) as err:
  2450. admin.set_events(payload={"bad": "conf"})
  2451. assert err.match("Unrecognized field")
  2452. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  2453. assert res == {}
  2454. admin.create_client(payload={"name": "test", "clientId": "test"})
  2455. events = admin.get_events()
  2456. assert events == []
  2457. @freezegun.freeze_time("2023-02-25 10:00:00")
  2458. def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str) -> None:
  2459. """
  2460. Test auto refresh token.
  2461. :param admin_frozen: Keycloak Admin client with time frozen in place
  2462. :type admin_frozen: KeycloakAdmin
  2463. :param realm: Keycloak realm
  2464. :type realm: str
  2465. """
  2466. admin = admin_frozen
  2467. admin.get_realm(realm_name=realm)
  2468. # Test get refresh
  2469. admin.connection.custom_headers = {
  2470. "Authorization": "Bearer bad",
  2471. "Content-Type": "application/json",
  2472. }
  2473. res = admin.get_realm(realm_name=realm)
  2474. assert res["realm"] == realm
  2475. # Freeze time to simulate the access token expiring
  2476. with freezegun.freeze_time("2023-02-25 10:05:00"):
  2477. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:05:00Z")
  2478. assert admin.get_realm(realm_name=realm)
  2479. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:05:00Z")
  2480. # Test bad refresh token, but first make sure access token has expired again
  2481. with freezegun.freeze_time("2023-02-25 10:10:00"):
  2482. admin.connection.custom_headers = {"Content-Type": "application/json"}
  2483. admin.connection.token["refresh_token"] = "bad" # noqa: S105
  2484. with pytest.raises(KeycloakPostError) as err:
  2485. admin.get_realm(realm_name="test-refresh")
  2486. assert err.match(
  2487. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'',
  2488. )
  2489. admin.connection.get_token()
  2490. # Test post refresh
  2491. with freezegun.freeze_time("2023-02-25 10:15:00"):
  2492. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:15:00Z")
  2493. admin.connection.token = None
  2494. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  2495. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:15:00Z")
  2496. # Test update refresh
  2497. with freezegun.freeze_time("2023-02-25 10:25:00"):
  2498. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:25:00Z")
  2499. admin.connection.token = None
  2500. assert (
  2501. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"}) == {}
  2502. )
  2503. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:25:00Z")
  2504. # Test delete refresh
  2505. with freezegun.freeze_time("2023-02-25 10:35:00"):
  2506. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:35:00Z")
  2507. admin.connection.token = None
  2508. assert admin.delete_realm(realm_name="test-refresh") == {}
  2509. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:35:00Z")
  2510. def test_get_required_actions(admin: KeycloakAdmin, realm: str) -> None:
  2511. """
  2512. Test required actions.
  2513. :param admin: Keycloak Admin client
  2514. :type admin: KeycloakAdmin
  2515. :param realm: Keycloak realm
  2516. :type realm: str
  2517. """
  2518. admin.change_current_realm(realm)
  2519. ractions = admin.get_required_actions()
  2520. assert isinstance(ractions, list)
  2521. for ra in ractions:
  2522. for key in [
  2523. "alias",
  2524. "name",
  2525. "providerId",
  2526. "enabled",
  2527. "defaultAction",
  2528. "priority",
  2529. "config",
  2530. ]:
  2531. assert key in ra
  2532. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str) -> None:
  2533. """
  2534. Test get required action by alias.
  2535. :param admin: Keycloak Admin client
  2536. :type admin: KeycloakAdmin
  2537. :param realm: Keycloak realm
  2538. :type realm: str
  2539. """
  2540. admin.change_current_realm(realm)
  2541. ractions = admin.get_required_actions()
  2542. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2543. assert ra in ractions
  2544. assert ra["alias"] == "UPDATE_PASSWORD"
  2545. assert admin.get_required_action_by_alias("does-not-exist") is None
  2546. def test_update_required_action(admin: KeycloakAdmin, realm: str) -> None:
  2547. """
  2548. Test update required action.
  2549. :param admin: Keycloak Admin client
  2550. :type admin: KeycloakAdmin
  2551. :param realm: Keycloak realm
  2552. :type realm: str
  2553. """
  2554. admin.change_current_realm(realm)
  2555. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2556. old = copy.deepcopy(ra)
  2557. ra["enabled"] = False
  2558. admin.update_required_action("UPDATE_PASSWORD", ra)
  2559. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2560. assert old != newra
  2561. assert newra["enabled"] is False
  2562. def test_get_composite_client_roles_of_group(
  2563. admin: KeycloakAdmin,
  2564. realm: str,
  2565. client: str,
  2566. group: str,
  2567. composite_client_role: str,
  2568. ) -> None:
  2569. """
  2570. Test get composite client roles of group.
  2571. :param admin: Keycloak Admin client
  2572. :type admin: KeycloakAdmin
  2573. :param realm: Keycloak realm
  2574. :type realm: str
  2575. :param client: Keycloak client
  2576. :type client: str
  2577. :param group: Keycloak group
  2578. :type group: str
  2579. :param composite_client_role: Composite client role
  2580. :type composite_client_role: str
  2581. """
  2582. admin.change_current_realm(realm)
  2583. role = admin.get_client_role(client, composite_client_role)
  2584. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  2585. result = admin.get_composite_client_roles_of_group(client, group)
  2586. assert role["id"] in [x["id"] for x in result]
  2587. def test_get_role_client_level_children(
  2588. admin: KeycloakAdmin,
  2589. realm: str,
  2590. client: str,
  2591. composite_client_role: str,
  2592. client_role: str,
  2593. ) -> None:
  2594. """
  2595. Test get children of composite client role.
  2596. :param admin: Keycloak Admin client
  2597. :type admin: KeycloakAdmin
  2598. :param realm: Keycloak realm
  2599. :type realm: str
  2600. :param client: Keycloak client
  2601. :type client: str
  2602. :param composite_client_role: Composite client role
  2603. :type composite_client_role: str
  2604. :param client_role: Client role
  2605. :type client_role: str
  2606. """
  2607. admin.change_current_realm(realm)
  2608. child = admin.get_client_role(client, client_role)
  2609. parent = admin.get_client_role(client, composite_client_role)
  2610. res = admin.get_role_client_level_children(client, parent["id"])
  2611. assert child["id"] in [x["id"] for x in res]
  2612. def test_upload_certificate(
  2613. admin: KeycloakAdmin,
  2614. realm: str,
  2615. client: str,
  2616. selfsigned_cert: tuple,
  2617. ) -> None:
  2618. """
  2619. Test upload certificate.
  2620. :param admin: Keycloak Admin client
  2621. :type admin: KeycloakAdmin
  2622. :param realm: Keycloak realm
  2623. :type realm: str
  2624. :param client: Keycloak client
  2625. :type client: str
  2626. :param selfsigned_cert: Selfsigned certificates
  2627. :type selfsigned_cert: tuple
  2628. """
  2629. admin.change_current_realm(realm)
  2630. cert, _ = selfsigned_cert
  2631. cert = cert.decode("utf-8").strip()
  2632. admin.upload_certificate(client, cert)
  2633. cl = admin.get_client(client)
  2634. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  2635. def test_get_bruteforce_status_for_user(
  2636. admin: KeycloakAdmin,
  2637. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2638. realm: str,
  2639. ) -> None:
  2640. """
  2641. Test users.
  2642. :param admin: Keycloak Admin client
  2643. :type admin: KeycloakAdmin
  2644. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2645. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2646. :param realm: Keycloak realm
  2647. :type realm: str
  2648. """
  2649. oid, username, password = oid_with_credentials
  2650. admin.change_current_realm(realm)
  2651. # Turn on bruteforce protection
  2652. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2653. res = admin.get_realm(realm_name=realm)
  2654. assert res["bruteForceProtected"] is True
  2655. # Test login user with wrong credentials
  2656. with contextlib.suppress(KeycloakAuthenticationError):
  2657. oid.token(username=username, password="wrongpassword") # noqa: S106
  2658. user_id = admin.get_user_id(username)
  2659. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2660. assert bruteforce_status["numFailures"] == 1
  2661. # Cleanup
  2662. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2663. res = admin.get_realm(realm_name=realm)
  2664. assert res["bruteForceProtected"] is False
  2665. def test_clear_bruteforce_attempts_for_user(
  2666. admin: KeycloakAdmin,
  2667. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2668. realm: str,
  2669. ) -> None:
  2670. """
  2671. Test users.
  2672. :param admin: Keycloak Admin client
  2673. :type admin: KeycloakAdmin
  2674. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2675. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2676. :param realm: Keycloak realm
  2677. :type realm: str
  2678. """
  2679. oid, username, password = oid_with_credentials
  2680. admin.change_current_realm(realm)
  2681. # Turn on bruteforce protection
  2682. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2683. res = admin.get_realm(realm_name=realm)
  2684. assert res["bruteForceProtected"] is True
  2685. # Test login user with wrong credentials
  2686. with contextlib.suppress(KeycloakAuthenticationError):
  2687. oid.token(username=username, password="wrongpassword") # noqa: S106
  2688. user_id = admin.get_user_id(username)
  2689. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2690. assert bruteforce_status["numFailures"] == 1
  2691. res = admin.clear_bruteforce_attempts_for_user(user_id)
  2692. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2693. assert bruteforce_status["numFailures"] == 0
  2694. # Cleanup
  2695. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2696. res = admin.get_realm(realm_name=realm)
  2697. assert res["bruteForceProtected"] is False
  2698. def test_clear_bruteforce_attempts_for_all_users(
  2699. admin: KeycloakAdmin,
  2700. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2701. realm: str,
  2702. ) -> None:
  2703. """
  2704. Test users.
  2705. :param admin: Keycloak Admin client
  2706. :type admin: KeycloakAdmin
  2707. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2708. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2709. :param realm: Keycloak realm
  2710. :type realm: str
  2711. """
  2712. oid, username, password = oid_with_credentials
  2713. admin.change_current_realm(realm)
  2714. # Turn on bruteforce protection
  2715. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2716. res = admin.get_realm(realm_name=realm)
  2717. assert res["bruteForceProtected"] is True
  2718. # Test login user with wrong credentials
  2719. with contextlib.suppress(KeycloakAuthenticationError):
  2720. oid.token(username=username, password="wrongpassword") # noqa: S106
  2721. user_id = admin.get_user_id(username)
  2722. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2723. assert bruteforce_status["numFailures"] == 1
  2724. res = admin.clear_all_bruteforce_attempts()
  2725. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2726. assert bruteforce_status["numFailures"] == 0
  2727. # Cleanup
  2728. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2729. res = admin.get_realm(realm_name=realm)
  2730. assert res["bruteForceProtected"] is False
  2731. def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  2732. """
  2733. Test that the default realm role is present in a brand new realm.
  2734. :param realm: Realm name
  2735. :type realm: str
  2736. :param admin: Keycloak admin
  2737. :type admin: KeycloakAdmin
  2738. """
  2739. admin.change_current_realm(realm)
  2740. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  2741. assert (
  2742. len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"])
  2743. == 1
  2744. )
  2745. def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  2746. """
  2747. Test getter for the ID of the default realm role.
  2748. :param realm: Realm name
  2749. :type realm: str
  2750. :param admin: Keycloak admin
  2751. :type admin: KeycloakAdmin
  2752. """
  2753. admin.change_current_realm(realm)
  2754. assert admin.get_default_realm_role_id() == next(
  2755. x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"
  2756. )
  2757. def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  2758. """
  2759. Test getting, adding and deleting default realm roles.
  2760. :param realm: Realm name
  2761. :type realm: str
  2762. :param admin: Keycloak admin
  2763. :type admin: KeycloakAdmin
  2764. """
  2765. admin.change_current_realm(realm)
  2766. # Test listing all default realm roles
  2767. roles = admin.get_realm_default_roles()
  2768. assert len(roles) == 2
  2769. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  2770. admin.change_current_realm("doesnotexist")
  2771. with pytest.raises(KeycloakGetError) as err:
  2772. admin.get_realm_default_roles()
  2773. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2774. admin.change_current_realm(realm)
  2775. # Test removing a default realm role
  2776. res = admin.remove_realm_default_roles(payload=[roles[0]])
  2777. assert res == {}
  2778. assert roles[0] not in admin.get_realm_default_roles()
  2779. assert len(admin.get_realm_default_roles()) == 1
  2780. with pytest.raises(KeycloakDeleteError) as err:
  2781. admin.remove_realm_default_roles(payload=[{"id": "bad id"}])
  2782. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2783. # Test adding a default realm role
  2784. res = admin.add_realm_default_roles(payload=[roles[0]])
  2785. assert res == {}
  2786. assert roles[0] in admin.get_realm_default_roles()
  2787. assert len(admin.get_realm_default_roles()) == 2
  2788. with pytest.raises(KeycloakPostError) as err:
  2789. admin.add_realm_default_roles(payload=[{"id": "bad id"}])
  2790. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2791. def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  2792. """
  2793. Test clearing the keys cache.
  2794. :param realm: Realm name
  2795. :type realm: str
  2796. :param admin: Keycloak admin
  2797. :type admin: KeycloakAdmin
  2798. """
  2799. admin.change_current_realm(realm)
  2800. res = admin.clear_keys_cache()
  2801. assert res == {}
  2802. def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  2803. """
  2804. Test clearing the realm cache.
  2805. :param realm: Realm name
  2806. :type realm: str
  2807. :param admin: Keycloak admin
  2808. :type admin: KeycloakAdmin
  2809. """
  2810. admin.change_current_realm(realm)
  2811. res = admin.clear_realm_cache()
  2812. assert res == {}
  2813. def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  2814. """
  2815. Test clearing the user cache.
  2816. :param realm: Realm name
  2817. :type realm: str
  2818. :param admin: Keycloak admin
  2819. :type admin: KeycloakAdmin
  2820. """
  2821. admin.change_current_realm(realm)
  2822. res = admin.clear_user_cache()
  2823. assert res == {}
  2824. def test_initial_access_token(
  2825. admin: KeycloakAdmin,
  2826. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  2827. ) -> None:
  2828. """
  2829. Test initial access token and client creation.
  2830. :param admin: Keycloak admin
  2831. :type admin: KeycloakAdmin
  2832. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2833. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2834. """
  2835. res = admin.create_initial_access_token(2, 3)
  2836. assert "token" in res
  2837. assert res["count"] == 2
  2838. assert res["expiration"] == 3
  2839. oid, username, password = oid_with_credentials
  2840. client = str(uuid.uuid4())
  2841. secret = str(uuid.uuid4())
  2842. res = oid.register_client(
  2843. token=res["token"],
  2844. payload={
  2845. "name": "DynamicRegisteredClient",
  2846. "clientId": client,
  2847. "enabled": True,
  2848. "publicClient": False,
  2849. "protocol": "openid-connect",
  2850. "secret": secret,
  2851. "clientAuthenticatorType": "client-secret",
  2852. },
  2853. )
  2854. assert res["clientId"] == client
  2855. new_secret = str(uuid.uuid4())
  2856. res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
  2857. assert res["secret"] == new_secret
  2858. def test_refresh_token(admin: KeycloakAdmin) -> None:
  2859. """
  2860. Test refresh token on connection even if it is expired.
  2861. :param admin: Keycloak admin
  2862. :type admin: KeycloakAdmin
  2863. """
  2864. admin.get_realms()
  2865. assert admin.connection.token is not None
  2866. admin.user_logout(admin.get_user_id(admin.connection.username))
  2867. admin.connection.refresh_token()
  2868. # async function start
  2869. @pytest.mark.asyncio
  2870. async def test_a_realms(admin: KeycloakAdmin) -> None:
  2871. """
  2872. Test realms.
  2873. :param admin: Keycloak Admin client
  2874. :type admin: KeycloakAdmin
  2875. """
  2876. # Get realms
  2877. realms = await admin.a_get_realms()
  2878. assert len(realms) == 1, realms
  2879. assert realms[0]["realm"] == "master"
  2880. # Create a test realm
  2881. res = await admin.a_create_realm(payload={"realm": "test"})
  2882. assert res == b"", res
  2883. # Create the same realm, should fail
  2884. with pytest.raises(KeycloakPostError) as err:
  2885. res = await admin.a_create_realm(payload={"realm": "test"})
  2886. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  2887. # Create the same realm, skip_exists true
  2888. res = await admin.a_create_realm(payload={"realm": "test"}, skip_exists=True)
  2889. assert res == {"msg": "Already exists"}, res
  2890. # Get a single realm
  2891. res = await admin.a_get_realm(realm_name="test")
  2892. assert res["realm"] == "test"
  2893. # Get non-existing realm
  2894. with pytest.raises(KeycloakGetError) as err:
  2895. await admin.a_get_realm(realm_name="non-existent")
  2896. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  2897. # Update realm
  2898. res = await admin.a_update_realm(realm_name="test", payload={"accountTheme": "test"})
  2899. assert res == {}, res
  2900. # Check that the update worked
  2901. res = await admin.a_get_realm(realm_name="test")
  2902. assert res["realm"] == "test"
  2903. assert res["accountTheme"] == "test"
  2904. # Update wrong payload
  2905. with pytest.raises(KeycloakPutError) as err:
  2906. await admin.a_update_realm(realm_name="test", payload={"wrong": "payload"})
  2907. assert err.match("Unrecognized field")
  2908. # Check that get realms returns both realms
  2909. realms = await admin.a_get_realms()
  2910. realm_names = [x["realm"] for x in realms]
  2911. assert len(realms) == 2, realms
  2912. assert "master" in realm_names, realm_names
  2913. assert "test" in realm_names, realm_names
  2914. # Get users profile, add an attribute and check
  2915. user_profile = await admin.a_get_realm_users_profile()
  2916. assert "attributes" in user_profile
  2917. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  2918. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  2919. ) >= Version("24"):
  2920. new_attribute = {
  2921. "name": "nickname",
  2922. "displayName": "",
  2923. "validations": {},
  2924. "annotations": {},
  2925. "permissions": {"view": [], "edit": ["admin"]},
  2926. "multivalued": False,
  2927. }
  2928. user_profile["attributes"].append(new_attribute)
  2929. res = await admin.a_update_realm_users_profile(user_profile)
  2930. # Check for new attribute in result
  2931. assert "nickname" in [x["name"] for x in res["attributes"]]
  2932. # Delete the realm
  2933. res = await admin.a_delete_realm(realm_name="test")
  2934. assert res == {}, res
  2935. # Check that the realm does not exist anymore
  2936. with pytest.raises(KeycloakGetError) as err:
  2937. await admin.a_get_realm(realm_name="test")
  2938. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2939. # Delete non-existing realm
  2940. with pytest.raises(KeycloakDeleteError) as err:
  2941. await admin.a_delete_realm(realm_name="non-existent")
  2942. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2943. @pytest.mark.asyncio
  2944. async def test_a_changing_of_realms(admin: KeycloakAdmin, realm: str) -> None:
  2945. """
  2946. Test changing of realms.
  2947. :param admin: Keycloak Admin client
  2948. :type admin: KeycloakAdmin
  2949. :param realm: Keycloak realm
  2950. :type realm: str
  2951. """
  2952. assert await admin.a_get_current_realm() == "master"
  2953. await admin.a_change_current_realm(realm)
  2954. assert await admin.a_get_current_realm() == realm
  2955. @pytest.mark.asyncio
  2956. async def test_a_import_export_realms(admin: KeycloakAdmin, realm: str) -> None:
  2957. """
  2958. Test import and export of realms.
  2959. :param admin: Keycloak Admin client
  2960. :type admin: KeycloakAdmin
  2961. :param realm: Keycloak realm
  2962. :type realm: str
  2963. """
  2964. await admin.a_change_current_realm(realm)
  2965. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=True)
  2966. assert realm_export != {}, realm_export
  2967. await admin.a_delete_realm(realm_name=realm)
  2968. admin.realm_name = "master"
  2969. res = await admin.a_import_realm(payload=realm_export)
  2970. assert res == b"", res
  2971. # Test bad import
  2972. with pytest.raises(KeycloakPostError) as err:
  2973. await admin.a_import_realm(payload={})
  2974. assert err.match(
  2975. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'', # noqa: E501
  2976. )
  2977. @pytest.mark.asyncio
  2978. async def test_a_partial_import_realm(admin: KeycloakAdmin, realm: str) -> None:
  2979. """
  2980. Test partial import of realm configuration.
  2981. :param admin: Keycloak Admin client
  2982. :type admin: KeycloakAdmin
  2983. :param realm: Keycloak realm
  2984. :type realm: str
  2985. """
  2986. test_realm_role = str(uuid.uuid4())
  2987. test_user = str(uuid.uuid4())
  2988. test_client = str(uuid.uuid4())
  2989. await admin.a_change_current_realm(realm)
  2990. client_id = await admin.a_create_client(payload={"name": test_client, "clientId": test_client})
  2991. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=False)
  2992. client_config = next(
  2993. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  2994. )
  2995. # delete before partial import
  2996. await admin.a_delete_client(client_id)
  2997. payload = {
  2998. "ifResourceExists": "SKIP",
  2999. "id": realm_export["id"],
  3000. "realm": realm,
  3001. "clients": [client_config],
  3002. "roles": {"realm": [{"name": test_realm_role}]},
  3003. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  3004. }
  3005. # check add
  3006. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3007. assert res["added"] == 3
  3008. # check skip
  3009. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3010. assert res["skipped"] == 3
  3011. # check overwrite
  3012. payload["ifResourceExists"] = "OVERWRITE"
  3013. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  3014. assert res["overwritten"] == 3
  3015. @pytest.mark.asyncio
  3016. async def test_a_users(admin: KeycloakAdmin, realm: str) -> None:
  3017. """
  3018. Test users.
  3019. :param admin: Keycloak Admin client
  3020. :type admin: KeycloakAdmin
  3021. :param realm: Keycloak realm
  3022. :type realm: str
  3023. """
  3024. await admin.a_change_current_realm(realm)
  3025. # Check no users present
  3026. users = await admin.a_get_users()
  3027. assert users == [], users
  3028. # Test create user
  3029. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3030. assert user_id is not None, user_id
  3031. # Test create the same user
  3032. with pytest.raises(KeycloakPostError) as err:
  3033. await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3034. assert err.match(".*User exists with same.*")
  3035. # Test create the same user, exists_ok true
  3036. user_id_2 = await admin.a_create_user(
  3037. payload={"username": "test", "email": "test@test.test"},
  3038. exist_ok=True,
  3039. )
  3040. assert user_id == user_id_2
  3041. # Test get user
  3042. user = await admin.a_get_user(user_id=user_id)
  3043. assert user["username"] == "test", user["username"]
  3044. assert user["email"] == "test@test.test", user["email"]
  3045. # Test update user
  3046. res = await admin.a_update_user(user_id=user_id, payload={"firstName": "Test"})
  3047. assert res == {}, res
  3048. user = await admin.a_get_user(user_id=user_id)
  3049. assert user["firstName"] == "Test"
  3050. # Test update user fail
  3051. with pytest.raises(KeycloakPutError) as err:
  3052. await admin.a_update_user(user_id=user_id, payload={"wrong": "payload"})
  3053. assert err.match("Unrecognized field")
  3054. # Test disable user
  3055. res = await admin.a_disable_user(user_id=user_id)
  3056. assert res == {}, res
  3057. assert not (await admin.a_get_user(user_id=user_id))["enabled"]
  3058. # Test enable user
  3059. res = await admin.a_enable_user(user_id=user_id)
  3060. assert res == {}, res
  3061. assert (await admin.a_get_user(user_id=user_id))["enabled"]
  3062. # Test get users again
  3063. users = await admin.a_get_users()
  3064. usernames = [x["username"] for x in users]
  3065. assert "test" in usernames
  3066. # Test users counts
  3067. count = await admin.a_users_count()
  3068. assert count == 1, count
  3069. # Test users count with query
  3070. count = await admin.a_users_count(query={"username": "notpresent"})
  3071. assert count == 0
  3072. # Test user groups
  3073. groups = await admin.a_get_user_groups(user_id=user["id"])
  3074. assert len(groups) == 0
  3075. # Test user groups bad id
  3076. with pytest.raises(KeycloakGetError) as err:
  3077. await admin.a_get_user_groups(user_id="does-not-exist")
  3078. assert err.match(USER_NOT_FOUND_REGEX)
  3079. # Test logout
  3080. res = await admin.a_user_logout(user_id=user["id"])
  3081. assert res == {}, res
  3082. # Test logout fail
  3083. with pytest.raises(KeycloakPostError) as err:
  3084. await admin.a_user_logout(user_id="non-existent-id")
  3085. assert err.match(USER_NOT_FOUND_REGEX)
  3086. # Test consents
  3087. res = await admin.a_user_consents(user_id=user["id"])
  3088. assert len(res) == 0, res
  3089. # Test consents fail
  3090. with pytest.raises(KeycloakGetError) as err:
  3091. await admin.a_user_consents(user_id="non-existent-id")
  3092. assert err.match(USER_NOT_FOUND_REGEX)
  3093. # Test delete user
  3094. res = await admin.a_delete_user(user_id=user_id)
  3095. assert res == {}, res
  3096. with pytest.raises(KeycloakGetError) as err:
  3097. await admin.a_get_user(user_id=user_id)
  3098. err.match(USER_NOT_FOUND_REGEX)
  3099. # Test delete fail
  3100. with pytest.raises(KeycloakDeleteError) as err:
  3101. await admin.a_delete_user(user_id="non-existent-id")
  3102. assert err.match(USER_NOT_FOUND_REGEX)
  3103. @pytest.mark.asyncio
  3104. async def test_a_enable_disable_all_users(admin: KeycloakAdmin, realm: str) -> None:
  3105. """
  3106. Test enable and disable all users.
  3107. :param admin: Keycloak Admin client
  3108. :type admin: KeycloakAdmin
  3109. :param realm: Keycloak realm
  3110. :type realm: str
  3111. """
  3112. admin.change_current_realm(realm)
  3113. user_id_1 = await admin.a_create_user(
  3114. payload={"username": "test", "email": "test@test.test", "enabled": True},
  3115. )
  3116. user_id_2 = await admin.a_create_user(
  3117. payload={"username": "test2", "email": "test2@test.test", "enabled": True},
  3118. )
  3119. user_id_3 = await admin.a_create_user(
  3120. payload={"username": "test3", "email": "test3@test.test", "enabled": True},
  3121. )
  3122. assert (await admin.a_get_user(user_id_1))["enabled"]
  3123. assert (await admin.a_get_user(user_id_2))["enabled"]
  3124. assert (await admin.a_get_user(user_id_3))["enabled"]
  3125. await admin.a_disable_all_users()
  3126. assert not (await admin.a_get_user(user_id_1))["enabled"]
  3127. assert not (await admin.a_get_user(user_id_2))["enabled"]
  3128. assert not (await admin.a_get_user(user_id_3))["enabled"]
  3129. await admin.a_enable_all_users()
  3130. assert (await admin.a_get_user(user_id_1))["enabled"]
  3131. assert (await admin.a_get_user(user_id_2))["enabled"]
  3132. assert (await admin.a_get_user(user_id_3))["enabled"]
  3133. @pytest.mark.asyncio
  3134. async def test_a_users_roles(admin: KeycloakAdmin, realm: str) -> None:
  3135. """
  3136. Test users roles.
  3137. :param admin: Keycloak Admin client
  3138. :type admin: KeycloakAdmin
  3139. :param realm: Keycloak realm
  3140. :type realm: str
  3141. """
  3142. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  3143. # Test all level user roles
  3144. client_id = await admin.a_create_client(
  3145. payload={"name": "test-client", "clientId": "test-client"},
  3146. )
  3147. await admin.a_create_client_role(client_role_id=client_id, payload={"name": "test-role"})
  3148. await admin.a_assign_client_role(
  3149. client_id=client_id,
  3150. user_id=user_id,
  3151. roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
  3152. )
  3153. all_roles = await admin.a_get_all_roles_of_user(user_id=user_id)
  3154. realm_roles = all_roles["realmMappings"]
  3155. assert len(realm_roles) == 1, realm_roles
  3156. client_roles = all_roles["clientMappings"]
  3157. assert len(client_roles) == 1, client_roles
  3158. # Test all level user roles fail
  3159. with pytest.raises(KeycloakGetError) as err:
  3160. await admin.a_get_all_roles_of_user(user_id="non-existent-id")
  3161. err.match('404: b\'{"error":"User not found"')
  3162. await admin.a_delete_user(user_id)
  3163. await admin.a_delete_client(client_id)
  3164. @pytest.mark.asyncio
  3165. async def test_a_users_pagination(admin: KeycloakAdmin, realm: str) -> None:
  3166. """
  3167. Test user pagination.
  3168. :param admin: Keycloak Admin client
  3169. :type admin: KeycloakAdmin
  3170. :param realm: Keycloak realm
  3171. :type realm: str
  3172. """
  3173. await admin.a_change_current_realm(realm)
  3174. for ind in range(admin.PAGE_SIZE + 50):
  3175. username = f"user_{ind}"
  3176. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  3177. users = await admin.a_get_users()
  3178. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  3179. users = await admin.a_get_users(query={"first": 100})
  3180. assert len(users) == 50, len(users)
  3181. users = await admin.a_get_users(query={"max": 20})
  3182. assert len(users) == 20, len(users)
  3183. @pytest.mark.asyncio
  3184. async def test_a_user_groups_pagination(admin: KeycloakAdmin, realm: str) -> None:
  3185. """
  3186. Test user groups pagination.
  3187. :param admin: Keycloak Admin client
  3188. :type admin: KeycloakAdmin
  3189. :param realm: Keycloak realm
  3190. :type realm: str
  3191. """
  3192. await admin.a_change_current_realm(realm)
  3193. user_id = await admin.a_create_user(
  3194. payload={"username": "username_1", "email": "username_1@test.test"},
  3195. )
  3196. for ind in range(admin.PAGE_SIZE + 50):
  3197. group_name = f"group_{ind}"
  3198. group_id = await admin.a_create_group(payload={"name": group_name})
  3199. await admin.a_group_user_add(user_id=user_id, group_id=group_id)
  3200. groups = await admin.a_get_user_groups(user_id=user_id)
  3201. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  3202. groups = await admin.a_get_user_groups(
  3203. user_id=user_id,
  3204. query={"first": 100, "max": -1, "search": ""},
  3205. )
  3206. assert len(groups) == 50, len(groups)
  3207. groups = await admin.a_get_user_groups(
  3208. user_id=user_id,
  3209. query={"max": 20, "first": -1, "search": ""},
  3210. )
  3211. assert len(groups) == 20, len(groups)
  3212. @pytest.mark.asyncio
  3213. async def test_a_idps(admin: KeycloakAdmin, realm: str) -> None:
  3214. """
  3215. Test IDPs.
  3216. :param admin: Keycloak Admin client
  3217. :type admin: KeycloakAdmin
  3218. :param realm: Keycloak realm
  3219. :type realm: str
  3220. """
  3221. await admin.a_change_current_realm(realm)
  3222. # Create IDP
  3223. res = await admin.a_create_idp(
  3224. payload={
  3225. "providerId": "github",
  3226. "alias": "github",
  3227. "config": {"clientId": "test", "clientSecret": "test"},
  3228. },
  3229. )
  3230. assert res == b"", res
  3231. # Test create idp fail
  3232. with pytest.raises(KeycloakPostError) as err:
  3233. await admin.a_create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  3234. assert err.match("Invalid identity provider id"), err
  3235. # Test listing
  3236. idps = await admin.a_get_idps()
  3237. assert len(idps) == 1
  3238. assert idps[0]["alias"] == "github"
  3239. # Test get idp
  3240. idp = await admin.a_get_idp("github")
  3241. assert idp["alias"] == "github"
  3242. assert idp.get("config")
  3243. assert idp["config"]["clientId"] == "test"
  3244. assert idp["config"]["clientSecret"] == "**********"
  3245. # Test get idp fail
  3246. with pytest.raises(KeycloakGetError) as err:
  3247. await admin.a_get_idp("does-not-exist")
  3248. assert err.match(HTTP_404_REGEX)
  3249. # Test IdP update
  3250. res = await admin.a_update_idp(idp_alias="github", payload=idps[0])
  3251. assert res == {}, res
  3252. # Test adding a mapper
  3253. res = await admin.a_add_mapper_to_idp(
  3254. idp_alias="github",
  3255. payload={
  3256. "identityProviderAlias": "github",
  3257. "identityProviderMapper": "github-user-attribute-mapper",
  3258. "name": "test",
  3259. },
  3260. )
  3261. assert res == b"", res
  3262. # Test mapper fail
  3263. with pytest.raises(KeycloakPostError) as err:
  3264. await admin.a_add_mapper_to_idp(idp_alias="does-no-texist", payload={})
  3265. assert err.match(HTTP_404_REGEX)
  3266. # Test IdP mappers listing
  3267. idp_mappers = await admin.a_get_idp_mappers(idp_alias="github")
  3268. assert len(idp_mappers) == 1
  3269. # Test IdP mapper update
  3270. res = await admin.a_update_mapper_in_idp(
  3271. idp_alias="github",
  3272. mapper_id=idp_mappers[0]["id"],
  3273. # For an obscure reason, keycloak expect all fields
  3274. payload={
  3275. "id": idp_mappers[0]["id"],
  3276. "identityProviderAlias": "github-alias",
  3277. "identityProviderMapper": "github-user-attribute-mapper",
  3278. "name": "test",
  3279. "config": idp_mappers[0]["config"],
  3280. },
  3281. )
  3282. assert res == {}, res
  3283. # Test delete
  3284. res = await admin.a_delete_idp(idp_alias="github")
  3285. assert res == {}, res
  3286. # Test delete fail
  3287. with pytest.raises(KeycloakDeleteError) as err:
  3288. await admin.a_delete_idp(idp_alias="does-not-exist")
  3289. assert err.match(HTTP_404_REGEX)
  3290. @pytest.mark.asyncio
  3291. async def test_a_user_credentials(admin: KeycloakAdmin, user: str) -> None:
  3292. """
  3293. Test user credentials.
  3294. :param admin: Keycloak Admin client
  3295. :type admin: KeycloakAdmin
  3296. :param user: Keycloak user
  3297. :type user: str
  3298. """
  3299. res = await admin.a_set_user_password(user_id=user, password="booya", temporary=True) # noqa: S106
  3300. assert res == {}, res
  3301. # Test user password set fail
  3302. with pytest.raises(KeycloakPutError) as err:
  3303. await admin.a_set_user_password(user_id="does-not-exist", password="")
  3304. assert err.match(USER_NOT_FOUND_REGEX)
  3305. credentials = await admin.a_get_credentials(user_id=user)
  3306. assert len(credentials) == 1
  3307. assert credentials[0]["type"] == "password", credentials
  3308. # Test get credentials fail
  3309. with pytest.raises(KeycloakGetError) as err:
  3310. await admin.a_get_credentials(user_id="does-not-exist")
  3311. assert err.match(USER_NOT_FOUND_REGEX)
  3312. res = await admin.a_delete_credential(user_id=user, credential_id=credentials[0]["id"])
  3313. assert res == {}, res
  3314. # Test delete fail
  3315. with pytest.raises(KeycloakDeleteError) as err:
  3316. await admin.a_delete_credential(user_id=user, credential_id="does-not-exist")
  3317. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  3318. @pytest.mark.asyncio
  3319. async def test_a_social_logins(admin: KeycloakAdmin, user: str) -> None:
  3320. """
  3321. Test social logins.
  3322. :param admin: Keycloak Admin client
  3323. :type admin: KeycloakAdmin
  3324. :param user: Keycloak user
  3325. :type user: str
  3326. """
  3327. res = await admin.a_add_user_social_login(
  3328. user_id=user,
  3329. provider_id="gitlab",
  3330. provider_userid="test",
  3331. provider_username="test",
  3332. )
  3333. assert res == {}, res
  3334. await admin.a_add_user_social_login(
  3335. user_id=user,
  3336. provider_id="github",
  3337. provider_userid="test",
  3338. provider_username="test",
  3339. )
  3340. assert res == {}, res
  3341. # Test add social login fail
  3342. with pytest.raises(KeycloakPostError) as err:
  3343. await admin.a_add_user_social_login(
  3344. user_id="does-not-exist",
  3345. provider_id="does-not-exist",
  3346. provider_userid="test",
  3347. provider_username="test",
  3348. )
  3349. assert err.match(USER_NOT_FOUND_REGEX)
  3350. res = await admin.a_get_user_social_logins(user_id=user)
  3351. assert res == [], res
  3352. # Test get social logins fail
  3353. with pytest.raises(KeycloakGetError) as err:
  3354. await admin.a_get_user_social_logins(user_id="does-not-exist")
  3355. assert err.match(USER_NOT_FOUND_REGEX)
  3356. res = await admin.a_delete_user_social_login(user_id=user, provider_id="gitlab")
  3357. assert res == {}, res
  3358. res = await admin.a_delete_user_social_login(user_id=user, provider_id="github")
  3359. assert res == {}, res
  3360. with pytest.raises(KeycloakDeleteError) as err:
  3361. await admin.a_delete_user_social_login(user_id=user, provider_id="instagram")
  3362. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  3363. @pytest.mark.asyncio
  3364. async def test_a_server_info(admin: KeycloakAdmin) -> None:
  3365. """
  3366. Test server info.
  3367. :param admin: Keycloak Admin client
  3368. :type admin: KeycloakAdmin
  3369. """
  3370. info = await admin.a_get_server_info()
  3371. assert set(info.keys()).issubset(
  3372. {
  3373. "systemInfo",
  3374. "memoryInfo",
  3375. "profileInfo",
  3376. "features",
  3377. "themes",
  3378. "socialProviders",
  3379. "identityProviders",
  3380. "providers",
  3381. "protocolMapperTypes",
  3382. "builtinProtocolMappers",
  3383. "clientInstallations",
  3384. "componentTypes",
  3385. "passwordPolicies",
  3386. "enums",
  3387. "cryptoInfo",
  3388. },
  3389. ), info.keys()
  3390. @pytest.mark.asyncio
  3391. async def test_a_groups(admin: KeycloakAdmin, user: str) -> None:
  3392. """
  3393. Test groups.
  3394. :param admin: Keycloak Admin client
  3395. :type admin: KeycloakAdmin
  3396. :param user: Keycloak user
  3397. :type user: str
  3398. """
  3399. # Test get groups
  3400. groups = await admin.a_get_groups()
  3401. assert len(groups) == 0
  3402. # Test create group
  3403. group_id = await admin.a_create_group(payload={"name": "main-group"})
  3404. assert group_id is not None, group_id
  3405. # Test group count
  3406. count = await admin.a_groups_count()
  3407. assert count.get("count") == 1, count
  3408. # Test group count with query
  3409. count = await admin.a_groups_count(query={"search": "notpresent"})
  3410. assert count.get("count") == 0
  3411. # Test create subgroups
  3412. subgroup_id_1 = await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3413. subgroup_id_2 = await admin.a_create_group(payload={"name": "subgroup-2"}, parent=group_id)
  3414. # Test create group fail
  3415. with pytest.raises(KeycloakPostError) as err:
  3416. await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3417. assert err.match("409"), err
  3418. # Test skip exists OK
  3419. subgroup_id_1_eq = await admin.a_create_group(
  3420. payload={"name": "subgroup-1"},
  3421. parent=group_id,
  3422. skip_exists=True,
  3423. )
  3424. assert subgroup_id_1_eq is None
  3425. # Test get groups again
  3426. groups = await admin.a_get_groups()
  3427. assert len(groups) == 1, groups
  3428. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3429. assert groups[0]["id"] == group_id
  3430. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3431. # Test get groups query
  3432. groups = await admin.a_get_groups(query={"max": 10})
  3433. assert len(groups) == 1, groups
  3434. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3435. assert groups[0]["id"] == group_id
  3436. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3437. # Test get group
  3438. res = await admin.a_get_group(group_id=subgroup_id_1)
  3439. assert res["id"] == subgroup_id_1, res
  3440. assert res["name"] == "subgroup-1"
  3441. assert res["path"] == "/main-group/subgroup-1"
  3442. # Test get group fail
  3443. with pytest.raises(KeycloakGetError) as err:
  3444. await admin.a_get_group(group_id="does-not-exist")
  3445. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3446. # Create 1 more subgroup
  3447. subsubgroup_id_1 = await admin.a_create_group(
  3448. payload={"name": "subsubgroup-1"},
  3449. parent=subgroup_id_2,
  3450. )
  3451. main_group = await admin.a_get_group(group_id=group_id)
  3452. # Test nested searches
  3453. subgroup_2 = await admin.a_get_group(group_id=subgroup_id_2)
  3454. res = await admin.a_get_subgroups(
  3455. group=subgroup_2,
  3456. path="/main-group/subgroup-2/subsubgroup-1",
  3457. )
  3458. assert res is not None, res
  3459. assert res["id"] == subsubgroup_id_1
  3460. # Test nested search from main group
  3461. res = await admin.a_get_subgroups(
  3462. group=await admin.a_get_group(group_id=group_id, full_hierarchy=True),
  3463. path="/main-group/subgroup-2/subsubgroup-1",
  3464. )
  3465. assert res["id"] == subsubgroup_id_1
  3466. # Test nested search from all groups
  3467. res = await admin.a_get_groups(full_hierarchy=True)
  3468. assert len(res) == 1
  3469. assert len(res[0]["subGroups"]) == 2
  3470. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1)["subGroups"]) == 0
  3471. assert len(next(x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2)["subGroups"]) == 1
  3472. # Test that query params are not allowed for full hierarchy
  3473. with pytest.raises(ValueError) as err:
  3474. await admin.a_get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
  3475. # Test that query params are passed
  3476. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  3477. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
  3478. ) >= Version("23"):
  3479. res = await admin.a_get_group_children(group_id=group_id, query={"max": 1})
  3480. assert len(res) == 1
  3481. assert err.match("Cannot use both query and full_hierarchy parameters")
  3482. main_group_id_2 = await admin.a_create_group(payload={"name": "main-group-2"})
  3483. assert len(await admin.a_get_groups(full_hierarchy=True)) == 2
  3484. # Test empty search
  3485. res = await admin.a_get_subgroups(group=main_group, path="/none")
  3486. assert res is None, res
  3487. # Test get group by path
  3488. res = await admin.a_get_group_by_path(path="/main-group/subgroup-1")
  3489. assert res is not None, res
  3490. assert res["id"] == subgroup_id_1, res
  3491. res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  3492. assert res["error"] == "Group path does not exist"
  3493. res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  3494. assert res is not None, res
  3495. assert res["id"] == subsubgroup_id_1
  3496. res = await admin.a_get_group_by_path(path="/main-group")
  3497. assert res is not None, res
  3498. assert res["id"] == group_id, res
  3499. # Test group members
  3500. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3501. assert len(res) == 0, res
  3502. # Test fail group members
  3503. with pytest.raises(KeycloakGetError) as err:
  3504. await admin.a_get_group_members(group_id="does-not-exist")
  3505. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  3506. res = await admin.a_group_user_add(user_id=user, group_id=subgroup_id_2)
  3507. assert res == {}, res
  3508. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3509. assert len(res) == 1, res
  3510. assert res[0]["id"] == user
  3511. # Test get group members query
  3512. res = await admin.a_get_group_members(group_id=subgroup_id_2, query={"max": 10})
  3513. assert len(res) == 1, res
  3514. assert res[0]["id"] == user
  3515. with pytest.raises(KeycloakDeleteError) as err:
  3516. await admin.a_group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  3517. assert err.match(USER_NOT_FOUND_REGEX), err
  3518. res = await admin.a_group_user_remove(user_id=user, group_id=subgroup_id_2)
  3519. assert res == {}, res
  3520. # Test set permissions
  3521. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=True)
  3522. assert res["enabled"], res
  3523. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=False)
  3524. assert not res["enabled"], res
  3525. with pytest.raises(KeycloakPutError) as err:
  3526. await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  3527. assert err.match(UNKOWN_ERROR_REGEX), err
  3528. # Test update group
  3529. res = await admin.a_update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  3530. assert res == {}, res
  3531. assert (await admin.a_get_group(group_id=subgroup_id_2))["name"] == "new-subgroup-2"
  3532. # test update fail
  3533. with pytest.raises(KeycloakPutError) as err:
  3534. await admin.a_update_group(group_id="does-not-exist", payload={})
  3535. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3536. # Test delete
  3537. res = await admin.a_delete_group(group_id=group_id)
  3538. assert res == {}, res
  3539. res = await admin.a_delete_group(group_id=main_group_id_2)
  3540. assert res == {}, res
  3541. assert len(await admin.a_get_groups()) == 0
  3542. # Test delete fail
  3543. with pytest.raises(KeycloakDeleteError) as err:
  3544. await admin.a_delete_group(group_id="does-not-exist")
  3545. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3546. @pytest.mark.asyncio
  3547. async def test_a_clients(admin: KeycloakAdmin, realm: str) -> None:
  3548. """
  3549. Test clients.
  3550. :param admin: Keycloak Admin client
  3551. :type admin: KeycloakAdmin
  3552. :param realm: Keycloak realm
  3553. :type realm: str
  3554. """
  3555. await admin.a_change_current_realm(realm)
  3556. # Test get clients
  3557. clients = await admin.a_get_clients()
  3558. assert len(clients) == 6, clients
  3559. assert {x["name"] for x in clients} == {
  3560. "${client_admin-cli}",
  3561. "${client_security-admin-console}",
  3562. "${client_account-console}",
  3563. "${client_broker}",
  3564. "${client_account}",
  3565. "${client_realm-management}",
  3566. }, clients
  3567. # Test create client
  3568. client_id = await admin.a_create_client(
  3569. payload={"name": "test-client", "clientId": "test-client"},
  3570. )
  3571. assert client_id, client_id
  3572. with pytest.raises(KeycloakPostError) as err:
  3573. await admin.a_create_client(payload={"name": "test-client", "clientId": "test-client"})
  3574. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  3575. client_id_2 = await admin.a_create_client(
  3576. payload={"name": "test-client", "clientId": "test-client"},
  3577. skip_exists=True,
  3578. )
  3579. assert client_id == client_id_2, client_id_2
  3580. # Test get client
  3581. res = await admin.a_get_client(client_id=client_id)
  3582. assert res["clientId"] == "test-client", res
  3583. assert res["name"] == "test-client", res
  3584. assert res["id"] == client_id, res
  3585. with pytest.raises(KeycloakGetError) as err:
  3586. await admin.a_get_client(client_id="does-not-exist")
  3587. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3588. assert len(await admin.a_get_clients()) == 7
  3589. # Test get client id
  3590. assert await admin.a_get_client_id(client_id="test-client") == client_id
  3591. assert await admin.a_get_client_id(client_id="does-not-exist") is None
  3592. # Test update client
  3593. res = await admin.a_update_client(client_id=client_id, payload={"name": "test-client-change"})
  3594. assert res == {}, res
  3595. with pytest.raises(KeycloakPutError) as err:
  3596. await admin.a_update_client(
  3597. client_id="does-not-exist",
  3598. payload={"name": "test-client-change"},
  3599. )
  3600. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3601. # Test client mappers
  3602. res = await admin.a_get_mappers_from_client(client_id=client_id)
  3603. assert len(res) == 0
  3604. with pytest.raises(KeycloakPostError) as err:
  3605. await admin.a_add_mapper_to_client(client_id="does-not-exist", payload={})
  3606. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3607. res = await admin.a_add_mapper_to_client(
  3608. client_id=client_id,
  3609. payload={
  3610. "name": "test-mapper",
  3611. "protocol": "openid-connect",
  3612. "protocolMapper": "oidc-usermodel-attribute-mapper",
  3613. },
  3614. )
  3615. assert res == b""
  3616. assert len(await admin.a_get_mappers_from_client(client_id=client_id)) == 1
  3617. mapper = (await admin.a_get_mappers_from_client(client_id=client_id))[0]
  3618. with pytest.raises(KeycloakPutError) as err:
  3619. await admin.a_update_client_mapper(
  3620. client_id=client_id,
  3621. mapper_id="does-not-exist",
  3622. payload={},
  3623. )
  3624. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3625. mapper["config"]["user.attribute"] = "test"
  3626. res = await admin.a_update_client_mapper(
  3627. client_id=client_id,
  3628. mapper_id=mapper["id"],
  3629. payload=mapper,
  3630. )
  3631. assert res == {}
  3632. res = await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3633. assert res == {}
  3634. with pytest.raises(KeycloakDeleteError) as err:
  3635. await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3636. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3637. # Test client sessions
  3638. with pytest.raises(KeycloakGetError) as err:
  3639. await admin.a_get_client_all_sessions(client_id="does-not-exist")
  3640. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3641. assert await admin.a_get_client_all_sessions(client_id=client_id) == []
  3642. assert await admin.a_get_client_sessions_stats() == []
  3643. # Test authz
  3644. auth_client_id = await admin.a_create_client(
  3645. payload={
  3646. "name": "authz-client",
  3647. "clientId": "authz-client",
  3648. "authorizationServicesEnabled": True,
  3649. "serviceAccountsEnabled": True,
  3650. },
  3651. )
  3652. res = await admin.a_get_client_authz_settings(client_id=auth_client_id)
  3653. assert res["allowRemoteResourceManagement"]
  3654. assert res["decisionStrategy"] == "UNANIMOUS"
  3655. assert len(res["policies"]) >= 0
  3656. with pytest.raises(KeycloakGetError) as err:
  3657. await admin.a_get_client_authz_settings(client_id=client_id)
  3658. assert err.match(HTTP_404_REGEX)
  3659. # Authz resources
  3660. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3661. assert len(res) == 1
  3662. assert res[0]["name"] == "Default Resource"
  3663. with pytest.raises(KeycloakGetError) as err:
  3664. await admin.a_get_client_authz_resources(client_id=client_id)
  3665. assert err.match(HTTP_404_REGEX)
  3666. res = await admin.a_create_client_authz_resource(
  3667. client_id=auth_client_id,
  3668. payload={"name": "test-resource"},
  3669. )
  3670. assert res["name"] == "test-resource", res
  3671. test_resource_id = res["_id"]
  3672. res = await admin.a_get_client_authz_resource(
  3673. client_id=auth_client_id,
  3674. resource_id=test_resource_id,
  3675. )
  3676. assert res["_id"] == test_resource_id, res
  3677. assert res["name"] == "test-resource", res
  3678. with pytest.raises(KeycloakPostError) as err:
  3679. await admin.a_create_client_authz_resource(
  3680. client_id=auth_client_id,
  3681. payload={"name": "test-resource"},
  3682. )
  3683. assert err.match('409: b\'{"error":"invalid_request"')
  3684. assert await admin.a_create_client_authz_resource(
  3685. client_id=auth_client_id,
  3686. payload={"name": "test-resource"},
  3687. skip_exists=True,
  3688. ) == {"msg": "Already exists"}
  3689. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3690. assert len(res) == 2
  3691. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  3692. res = await admin.a_create_client_authz_resource(
  3693. client_id=auth_client_id,
  3694. payload={"name": "temp-resource"},
  3695. )
  3696. assert res["name"] == "temp-resource", res
  3697. temp_resource_id: str = res["_id"]
  3698. # Test update authz resources
  3699. await admin.a_update_client_authz_resource(
  3700. client_id=auth_client_id,
  3701. resource_id=temp_resource_id,
  3702. payload={"name": "temp-updated-resource"},
  3703. )
  3704. res = await admin.a_get_client_authz_resource(
  3705. client_id=auth_client_id,
  3706. resource_id=temp_resource_id,
  3707. )
  3708. assert res["name"] == "temp-updated-resource", res
  3709. with pytest.raises(KeycloakPutError) as err:
  3710. await admin.a_update_client_authz_resource(
  3711. client_id=auth_client_id,
  3712. resource_id="invalid_resource_id",
  3713. payload={"name": "temp-updated-resource"},
  3714. )
  3715. assert err.match("404: b''"), err
  3716. await admin.a_delete_client_authz_resource(
  3717. client_id=auth_client_id,
  3718. resource_id=temp_resource_id,
  3719. )
  3720. with pytest.raises(KeycloakGetError) as err:
  3721. await admin.a_get_client_authz_resource(
  3722. client_id=auth_client_id,
  3723. resource_id=temp_resource_id,
  3724. )
  3725. assert err.match("404: b''")
  3726. # Authz policies
  3727. res = await admin.a_get_client_authz_policies(client_id=auth_client_id)
  3728. assert len(res) == 1, res
  3729. assert res[0]["name"] == "Default Policy"
  3730. with pytest.raises(KeycloakGetError) as err:
  3731. await admin.a_get_client_authz_policies(client_id="does-not-exist")
  3732. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3733. role_id = (await admin.a_get_realm_role(role_name="offline_access"))["id"]
  3734. res = await admin.a_create_client_authz_role_based_policy(
  3735. client_id=auth_client_id,
  3736. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3737. )
  3738. assert res["name"] == "test-authz-rb-policy", res
  3739. with pytest.raises(KeycloakPostError) as err:
  3740. await admin.a_create_client_authz_role_based_policy(
  3741. client_id=auth_client_id,
  3742. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3743. )
  3744. assert err.match('409: b\'{"error":"Policy with name')
  3745. assert await admin.a_create_client_authz_role_based_policy(
  3746. client_id=auth_client_id,
  3747. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3748. skip_exists=True,
  3749. ) == {"msg": "Already exists"}
  3750. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 2
  3751. role_based_policy_id = res["id"]
  3752. role_based_policy_name = res["name"]
  3753. res = await admin.a_create_client_authz_role_based_policy(
  3754. client_id=auth_client_id,
  3755. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  3756. )
  3757. res2 = await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3758. assert res["id"] == res2["id"]
  3759. await admin.a_delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3760. with pytest.raises(KeycloakGetError) as err:
  3761. await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3762. assert err.match("404: b''")
  3763. res = await admin.a_create_client_authz_policy(
  3764. client_id=auth_client_id,
  3765. payload={
  3766. "name": "test-authz-policy",
  3767. "type": "time",
  3768. "config": {"hourEnd": "18", "hour": "9"},
  3769. },
  3770. )
  3771. assert res["name"] == "test-authz-policy", res
  3772. with pytest.raises(KeycloakPostError) as err:
  3773. await admin.a_create_client_authz_policy(
  3774. client_id=auth_client_id,
  3775. payload={
  3776. "name": "test-authz-policy",
  3777. "type": "time",
  3778. "config": {"hourEnd": "18", "hour": "9"},
  3779. },
  3780. )
  3781. assert err.match('409: b\'{"error":"Policy with name')
  3782. assert await admin.a_create_client_authz_policy(
  3783. client_id=auth_client_id,
  3784. payload={
  3785. "name": "test-authz-policy",
  3786. "type": "time",
  3787. "config": {"hourEnd": "18", "hour": "9"},
  3788. },
  3789. skip_exists=True,
  3790. ) == {"msg": "Already exists"}
  3791. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 3
  3792. # Test authz permissions
  3793. res = await admin.a_get_client_authz_permissions(client_id=auth_client_id)
  3794. assert len(res) == 1, res
  3795. assert res[0]["name"] == "Default Permission"
  3796. with pytest.raises(KeycloakGetError) as err:
  3797. await admin.a_get_client_authz_permissions(client_id="does-not-exist")
  3798. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3799. res = await admin.a_create_client_authz_resource_based_permission(
  3800. client_id=auth_client_id,
  3801. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3802. )
  3803. assert res, res
  3804. assert res["name"] == "test-permission-rb"
  3805. assert res["resources"] == [test_resource_id]
  3806. resource_based_permission_id = res["id"]
  3807. resource_based_permission_name = res["name"]
  3808. with pytest.raises(KeycloakPostError) as err:
  3809. await admin.a_create_client_authz_resource_based_permission(
  3810. client_id=auth_client_id,
  3811. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3812. )
  3813. assert err.match('409: b\'{"error":"Policy with name')
  3814. assert await admin.a_create_client_authz_resource_based_permission(
  3815. client_id=auth_client_id,
  3816. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3817. skip_exists=True,
  3818. ) == {"msg": "Already exists"}
  3819. assert len(await admin.a_get_client_authz_permissions(client_id=auth_client_id)) == 2
  3820. # Test associating client policy with resource based permission
  3821. res = await admin.a_update_client_authz_resource_permission(
  3822. client_id=auth_client_id,
  3823. resource_id=resource_based_permission_id,
  3824. payload={
  3825. "id": resource_based_permission_id,
  3826. "name": resource_based_permission_name,
  3827. "type": "resource",
  3828. "logic": "POSITIVE",
  3829. "decisionStrategy": "UNANIMOUS",
  3830. "resources": [test_resource_id],
  3831. "scopes": [],
  3832. "policies": [role_based_policy_id],
  3833. },
  3834. )
  3835. # Test getting associated policies for a permission
  3836. associated_policies = await admin.a_get_client_authz_permission_associated_policies(
  3837. client_id=auth_client_id,
  3838. policy_id=resource_based_permission_id,
  3839. )
  3840. assert len(associated_policies) == 1
  3841. assert associated_policies[0]["name"].startswith(role_based_policy_name)
  3842. # Test authz scopes
  3843. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3844. assert len(res) == 0, res
  3845. with pytest.raises(KeycloakGetError) as err:
  3846. await admin.a_get_client_authz_scopes(client_id=client_id)
  3847. assert err.match(HTTP_404_REGEX)
  3848. res = await admin.a_create_client_authz_scopes(
  3849. client_id=auth_client_id,
  3850. payload={"name": "test-authz-scope"},
  3851. )
  3852. assert res["name"] == "test-authz-scope", res
  3853. with pytest.raises(KeycloakPostError) as err:
  3854. await admin.a_create_client_authz_scopes(
  3855. client_id="invalid_client_id",
  3856. payload={"name": "test-authz-scope"},
  3857. )
  3858. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3859. assert await admin.a_create_client_authz_scopes(
  3860. client_id=auth_client_id,
  3861. payload={"name": "test-authz-scope"},
  3862. )
  3863. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3864. assert len(res) == 1
  3865. assert {x["name"] for x in res} == {"test-authz-scope"}
  3866. # Test service account user
  3867. res = await admin.a_get_client_service_account_user(client_id=auth_client_id)
  3868. assert res["username"] == "service-account-authz-client", res
  3869. with pytest.raises(KeycloakGetError) as err:
  3870. await admin.a_get_client_service_account_user(client_id=client_id)
  3871. assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
  3872. UNKOWN_ERROR_REGEX,
  3873. )
  3874. # Test delete client
  3875. res = await admin.a_delete_client(client_id=auth_client_id)
  3876. assert res == {}, res
  3877. with pytest.raises(KeycloakDeleteError) as err:
  3878. await admin.a_delete_client(client_id=auth_client_id)
  3879. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3880. # Test client credentials
  3881. await admin.a_create_client(
  3882. payload={
  3883. "name": "test-confidential",
  3884. "enabled": True,
  3885. "protocol": "openid-connect",
  3886. "publicClient": False,
  3887. "redirectUris": ["http://localhost/*"],
  3888. "webOrigins": ["+"],
  3889. "clientId": "test-confidential",
  3890. "secret": "test-secret",
  3891. "clientAuthenticatorType": "client-secret",
  3892. },
  3893. )
  3894. with pytest.raises(KeycloakGetError) as err:
  3895. await admin.a_get_client_secrets(client_id="does-not-exist")
  3896. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3897. secrets = await admin.a_get_client_secrets(
  3898. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3899. )
  3900. assert secrets == {"type": "secret", "value": "test-secret"}
  3901. with pytest.raises(KeycloakPostError) as err:
  3902. await admin.a_generate_client_secrets(client_id="does-not-exist")
  3903. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3904. res = await admin.a_generate_client_secrets(
  3905. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3906. )
  3907. assert res
  3908. assert (
  3909. await admin.a_get_client_secrets(
  3910. client_id=await admin.a_get_client_id(client_id="test-confidential"),
  3911. )
  3912. == res
  3913. )
  3914. @pytest.mark.asyncio
  3915. async def test_a_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  3916. """
  3917. Test realm roles.
  3918. :param admin: Keycloak Admin client
  3919. :type admin: KeycloakAdmin
  3920. :param realm: Keycloak realm
  3921. :type realm: str
  3922. """
  3923. await admin.a_change_current_realm(realm)
  3924. # Test get realm roles
  3925. roles = await admin.a_get_realm_roles()
  3926. assert len(roles) == 3, roles
  3927. role_names = [x["name"] for x in roles]
  3928. assert "uma_authorization" in role_names, role_names
  3929. assert "offline_access" in role_names, role_names
  3930. # Test get realm roles with search text
  3931. searched_roles = await admin.a_get_realm_roles(search_text="uma_a")
  3932. searched_role_names = [x["name"] for x in searched_roles]
  3933. assert "uma_authorization" in searched_role_names, searched_role_names
  3934. assert "offline_access" not in searched_role_names, searched_role_names
  3935. # Test empty members
  3936. with pytest.raises(KeycloakGetError) as err:
  3937. await admin.a_get_realm_role_members(role_name="does-not-exist")
  3938. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3939. members = await admin.a_get_realm_role_members(role_name="offline_access")
  3940. assert members == [], members
  3941. # Test create realm role
  3942. role_id = await admin.a_create_realm_role(
  3943. payload={"name": "test-realm-role"},
  3944. skip_exists=True,
  3945. )
  3946. assert role_id, role_id
  3947. with pytest.raises(KeycloakPostError) as err:
  3948. await admin.a_create_realm_role(payload={"name": "test-realm-role"})
  3949. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  3950. role_id_2 = await admin.a_create_realm_role(
  3951. payload={"name": "test-realm-role"},
  3952. skip_exists=True,
  3953. )
  3954. assert role_id == role_id_2
  3955. # Test get realm role by its id
  3956. role_id = (await admin.a_get_realm_role(role_name="test-realm-role"))["id"]
  3957. res = await admin.a_get_realm_role_by_id(role_id)
  3958. assert res["name"] == "test-realm-role"
  3959. # Test update realm role
  3960. res = await admin.a_update_realm_role(
  3961. role_name="test-realm-role",
  3962. payload={"name": "test-realm-role-update"},
  3963. )
  3964. assert res == {}, res
  3965. with pytest.raises(KeycloakPutError) as err:
  3966. await admin.a_update_realm_role(
  3967. role_name="test-realm-role",
  3968. payload={"name": "test-realm-role-update"},
  3969. )
  3970. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3971. # Test realm role user assignment
  3972. user_id = await admin.a_create_user(
  3973. payload={"username": "role-testing", "email": "test@test.test"},
  3974. )
  3975. with pytest.raises(KeycloakPostError) as err:
  3976. await admin.a_assign_realm_roles(user_id=user_id, roles=["bad"])
  3977. assert err.match(UNKOWN_ERROR_REGEX), err
  3978. res = await admin.a_assign_realm_roles(
  3979. user_id=user_id,
  3980. roles=[
  3981. await admin.a_get_realm_role(role_name="offline_access"),
  3982. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  3983. ],
  3984. )
  3985. assert res == {}, res
  3986. assert admin.get_user(user_id=user_id)["username"] in [
  3987. x["username"] for x in await admin.a_get_realm_role_members(role_name="offline_access")
  3988. ]
  3989. assert admin.get_user(user_id=user_id)["username"] in [
  3990. x["username"]
  3991. for x in await admin.a_get_realm_role_members(role_name="test-realm-role-update")
  3992. ]
  3993. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  3994. assert len(roles) == 3
  3995. assert "offline_access" in [x["name"] for x in roles]
  3996. assert "test-realm-role-update" in [x["name"] for x in roles]
  3997. with pytest.raises(KeycloakDeleteError) as err:
  3998. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  3999. assert err.match(UNKOWN_ERROR_REGEX), err
  4000. res = await admin.a_delete_realm_roles_of_user(
  4001. user_id=user_id,
  4002. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4003. )
  4004. assert res == {}, res
  4005. assert await admin.a_get_realm_role_members(role_name="offline_access") == []
  4006. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  4007. assert len(roles) == 2
  4008. assert "offline_access" not in [x["name"] for x in roles]
  4009. assert "test-realm-role-update" in [x["name"] for x in roles]
  4010. roles = await admin.a_get_available_realm_roles_of_user(user_id=user_id)
  4011. assert len(roles) == 2
  4012. assert "offline_access" in [x["name"] for x in roles]
  4013. assert "uma_authorization" in [x["name"] for x in roles]
  4014. # Test realm role group assignment
  4015. group_id = await admin.a_create_group(payload={"name": "test-group"})
  4016. with pytest.raises(KeycloakPostError) as err:
  4017. await admin.a_assign_group_realm_roles(group_id=group_id, roles=["bad"])
  4018. assert err.match(UNKOWN_ERROR_REGEX), err
  4019. res = await admin.a_assign_group_realm_roles(
  4020. group_id=group_id,
  4021. roles=[
  4022. await admin.a_get_realm_role(role_name="offline_access"),
  4023. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  4024. ],
  4025. )
  4026. assert res == {}, res
  4027. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  4028. assert len(roles) == 2
  4029. assert "offline_access" in [x["name"] for x in roles]
  4030. assert "test-realm-role-update" in [x["name"] for x in roles]
  4031. with pytest.raises(KeycloakDeleteError) as err:
  4032. await admin.a_delete_group_realm_roles(group_id=group_id, roles=["bad"])
  4033. assert err.match(UNKOWN_ERROR_REGEX)
  4034. res = await admin.a_delete_group_realm_roles(
  4035. group_id=group_id,
  4036. roles=[admin.get_realm_role(role_name="offline_access")],
  4037. )
  4038. assert res == {}, res
  4039. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  4040. assert len(roles) == 1
  4041. assert "test-realm-role-update" in [x["name"] for x in roles]
  4042. # Test composite realm roles
  4043. composite_role = await admin.a_create_realm_role(payload={"name": "test-composite-role"})
  4044. with pytest.raises(KeycloakPostError) as err:
  4045. await admin.a_add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  4046. assert err.match(UNKOWN_ERROR_REGEX), err
  4047. res = await admin.a_add_composite_realm_roles_to_role(
  4048. role_name=composite_role,
  4049. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  4050. )
  4051. assert res == {}, res
  4052. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  4053. assert len(res) == 1
  4054. assert "test-realm-role-update" in res[0]["name"]
  4055. with pytest.raises(KeycloakGetError) as err:
  4056. await admin.a_get_composite_realm_roles_of_role(role_name="bad")
  4057. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4058. res = await admin.a_get_composite_realm_roles_of_user(user_id=user_id)
  4059. assert len(res) == 4
  4060. assert "offline_access" in {x["name"] for x in res}
  4061. assert "test-realm-role-update" in {x["name"] for x in res}
  4062. assert "uma_authorization" in {x["name"] for x in res}
  4063. with pytest.raises(KeycloakGetError) as err:
  4064. await admin.a_get_composite_realm_roles_of_user(user_id="bad")
  4065. assert err.match(USER_NOT_FOUND_REGEX), err
  4066. with pytest.raises(KeycloakDeleteError) as err:
  4067. await admin.a_remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  4068. assert err.match(UNKOWN_ERROR_REGEX), err
  4069. res = await admin.a_remove_composite_realm_roles_to_role(
  4070. role_name=composite_role,
  4071. roles=[admin.get_realm_role(role_name="test-realm-role-update")],
  4072. )
  4073. assert res == {}, res
  4074. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  4075. assert len(res) == 0
  4076. # Test realm role group list
  4077. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update")
  4078. assert len(res) == 1
  4079. assert res[0]["id"] == group_id
  4080. with pytest.raises(KeycloakGetError) as err:
  4081. await admin.a_get_realm_role_groups(role_name="non-existent-role")
  4082. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4083. # Test with query params
  4084. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
  4085. assert len(res) == 1
  4086. # Test delete realm role
  4087. res = await admin.a_delete_realm_role(role_name=composite_role)
  4088. assert res == {}, res
  4089. with pytest.raises(KeycloakDeleteError) as err:
  4090. await admin.a_delete_realm_role(role_name=composite_role)
  4091. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4092. @pytest.mark.asyncio
  4093. @pytest.mark.parametrize(
  4094. ("testcase", "arg_brief_repr", "includes_attributes"),
  4095. [
  4096. ("brief True", {"brief_representation": True}, False),
  4097. ("brief False", {"brief_representation": False}, True),
  4098. ("default", {}, False),
  4099. ],
  4100. )
  4101. async def test_a_role_attributes(
  4102. admin: KeycloakAdmin,
  4103. realm: str,
  4104. client: str,
  4105. arg_brief_repr: dict,
  4106. includes_attributes: bool,
  4107. testcase: str,
  4108. ) -> None:
  4109. """
  4110. Test getting role attributes for bulk calls.
  4111. :param admin: Keycloak admin
  4112. :type admin: KeycloakAdmin
  4113. :param realm: Keycloak realm
  4114. :type realm: str
  4115. :param client: Keycloak client
  4116. :type client: str
  4117. :param arg_brief_repr: Brief representation
  4118. :type arg_brief_repr: dict
  4119. :param includes_attributes: Indicator whether to include attributes
  4120. :type includes_attributes: bool
  4121. :param testcase: Test case
  4122. :type testcase: str
  4123. """
  4124. # setup
  4125. attribute_role = "test-realm-role-w-attr"
  4126. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  4127. role_id = await admin.a_create_realm_role(
  4128. payload={"name": attribute_role, "attributes": test_attrs},
  4129. skip_exists=True,
  4130. )
  4131. assert role_id, role_id
  4132. cli_role_id = await admin.a_create_client_role(
  4133. client,
  4134. payload={"name": attribute_role, "attributes": test_attrs},
  4135. skip_exists=True,
  4136. )
  4137. assert cli_role_id, cli_role_id
  4138. if not includes_attributes:
  4139. test_attrs = None
  4140. # tests
  4141. roles = await admin.a_get_realm_roles(**arg_brief_repr)
  4142. roles_filtered = [role for role in roles if role["name"] == role_id]
  4143. assert roles_filtered, roles_filtered
  4144. role = roles_filtered[0]
  4145. assert role.get("attributes") == test_attrs, testcase
  4146. roles = await admin.a_get_client_roles(client, **arg_brief_repr)
  4147. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  4148. assert roles_filtered, roles_filtered
  4149. role = roles_filtered[0]
  4150. assert role.get("attributes") == test_attrs, testcase
  4151. # cleanup
  4152. res = await admin.a_delete_realm_role(role_name=attribute_role)
  4153. assert res == {}, res
  4154. res = await admin.a_delete_client_role(client, role_name=attribute_role)
  4155. assert res == {}, res
  4156. @pytest.mark.asyncio
  4157. async def test_a_client_scope_realm_roles(admin: KeycloakAdmin, realm: str) -> None:
  4158. """
  4159. Test client realm roles.
  4160. :param admin: Keycloak admin
  4161. :type admin: KeycloakAdmin
  4162. :param realm: Keycloak realm
  4163. :type realm: str
  4164. """
  4165. await admin.a_change_current_realm(realm)
  4166. # Test get realm roles
  4167. roles = await admin.a_get_realm_roles()
  4168. assert len(roles) == 3, roles
  4169. role_names = [x["name"] for x in roles]
  4170. assert "uma_authorization" in role_names, role_names
  4171. assert "offline_access" in role_names, role_names
  4172. # create realm role for test
  4173. role_id = await admin.a_create_realm_role(
  4174. payload={"name": "test-realm-role"},
  4175. skip_exists=True,
  4176. )
  4177. assert role_id, role_id
  4178. # Test realm role client assignment
  4179. client_id = await admin.a_create_client(
  4180. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4181. )
  4182. with pytest.raises(KeycloakPostError) as err:
  4183. await admin.a_assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  4184. assert err.match(UNKOWN_ERROR_REGEX), err
  4185. res = await admin.a_assign_realm_roles_to_client_scope(
  4186. client_id=client_id,
  4187. roles=[
  4188. await admin.a_get_realm_role(role_name="offline_access"),
  4189. await admin.a_get_realm_role(role_name="test-realm-role"),
  4190. ],
  4191. )
  4192. assert res == {}, res
  4193. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4194. assert len(roles) == 2
  4195. client_role_names = [x["name"] for x in roles]
  4196. assert "offline_access" in client_role_names, client_role_names
  4197. assert "test-realm-role" in client_role_names, client_role_names
  4198. assert "uma_authorization" not in client_role_names, client_role_names
  4199. # Test remove realm role of client
  4200. with pytest.raises(KeycloakDeleteError) as err:
  4201. await admin.a_delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  4202. assert err.match(UNKOWN_ERROR_REGEX), err
  4203. res = await admin.a_delete_realm_roles_of_client_scope(
  4204. client_id=client_id,
  4205. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4206. )
  4207. assert res == {}, res
  4208. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4209. assert len(roles) == 1
  4210. assert "test-realm-role" in [x["name"] for x in roles]
  4211. res = await admin.a_delete_realm_roles_of_client_scope(
  4212. client_id=client_id,
  4213. roles=[await admin.a_get_realm_role(role_name="test-realm-role")],
  4214. )
  4215. assert res == {}, res
  4216. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  4217. assert len(roles) == 0
  4218. @pytest.mark.asyncio
  4219. async def test_a_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str) -> None:
  4220. """
  4221. Test client assignment of other client roles.
  4222. :param admin: Keycloak admin
  4223. :type admin: KeycloakAdmin
  4224. :param realm: Keycloak realm
  4225. :type realm: str
  4226. :param client: Keycloak client
  4227. :type client: str
  4228. """
  4229. await admin.a_change_current_realm(realm)
  4230. client_id = await admin.a_create_client(
  4231. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4232. )
  4233. # Test get client roles
  4234. roles = await admin.a_get_client_roles_of_client_scope(client_id, client)
  4235. assert len(roles) == 0, roles
  4236. # create client role for test
  4237. client_role_id = await admin.a_create_client_role(
  4238. client_role_id=client,
  4239. payload={"name": "client-role-test"},
  4240. skip_exists=True,
  4241. )
  4242. assert client_role_id, client_role_id
  4243. # Test client role assignment to other client
  4244. with pytest.raises(KeycloakPostError) as err:
  4245. await admin.a_assign_client_roles_to_client_scope(
  4246. client_id=client_id,
  4247. client_roles_owner_id=client,
  4248. roles=["bad"],
  4249. )
  4250. assert err.match(UNKOWN_ERROR_REGEX), err
  4251. res = await admin.a_assign_client_roles_to_client_scope(
  4252. client_id=client_id,
  4253. client_roles_owner_id=client,
  4254. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  4255. )
  4256. assert res == {}, res
  4257. roles = await admin.a_get_client_roles_of_client_scope(
  4258. client_id=client_id,
  4259. client_roles_owner_id=client,
  4260. )
  4261. assert len(roles) == 1
  4262. client_role_names = [x["name"] for x in roles]
  4263. assert "client-role-test" in client_role_names, client_role_names
  4264. # Test remove realm role of client
  4265. with pytest.raises(KeycloakDeleteError) as err:
  4266. await admin.a_delete_client_roles_of_client_scope(
  4267. client_id=client_id,
  4268. client_roles_owner_id=client,
  4269. roles=["bad"],
  4270. )
  4271. assert err.match(UNKOWN_ERROR_REGEX), err
  4272. res = await admin.a_delete_client_roles_of_client_scope(
  4273. client_id=client_id,
  4274. client_roles_owner_id=client,
  4275. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  4276. )
  4277. assert res == {}, res
  4278. roles = await admin.a_get_client_roles_of_client_scope(
  4279. client_id=client_id,
  4280. client_roles_owner_id=client,
  4281. )
  4282. assert len(roles) == 0
  4283. @pytest.mark.asyncio
  4284. async def test_a_client_scope_mapping_client_roles(
  4285. admin: KeycloakAdmin,
  4286. realm: str,
  4287. client: str,
  4288. ) -> None:
  4289. """
  4290. Test client scope assignment of client roles.
  4291. :param admin: Keycloak admin
  4292. :type admin: KeycloakAdmin
  4293. :param realm: Keycloak realm
  4294. :type realm: str
  4295. :param client: Keycloak client owning roles
  4296. :type client: str
  4297. """
  4298. _client_role_name = "some-client-role"
  4299. await admin.a_change_current_realm(realm)
  4300. client_obj = await admin.a_get_client(client)
  4301. client_name = client_obj["name"]
  4302. client_scope = {
  4303. "name": "test_client_scope",
  4304. "description": "Test Client Scope",
  4305. "protocol": "openid-connect",
  4306. "attributes": {},
  4307. }
  4308. client_scope_id = await admin.a_create_client_scope(client_scope, skip_exists=False)
  4309. # Test get client roles
  4310. client_specific_roles = await admin.a_get_client_specific_roles_of_client_scope(
  4311. client_scope_id,
  4312. client,
  4313. )
  4314. assert len(client_specific_roles) == 0, client_specific_roles
  4315. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id)
  4316. assert len(all_roles) == 0, all_roles
  4317. # create client role for test
  4318. client_role_name = await admin.a_create_client_role(
  4319. client_role_id=client,
  4320. payload={"name": _client_role_name},
  4321. skip_exists=True,
  4322. )
  4323. assert client_role_name, client_role_name
  4324. # Test client role assignment to other client
  4325. with pytest.raises(KeycloakPostError) as err:
  4326. await admin.a_add_client_specific_roles_to_client_scope(
  4327. client_scope_id=client_scope_id,
  4328. client_roles_owner_id=client,
  4329. roles=["bad"],
  4330. )
  4331. assert err.match(UNKOWN_ERROR_REGEX), err
  4332. res = await admin.a_add_client_specific_roles_to_client_scope(
  4333. client_scope_id=client_scope_id,
  4334. client_roles_owner_id=client,
  4335. roles=[await admin.a_get_client_role(client_id=client, role_name=_client_role_name)],
  4336. )
  4337. assert res == {}, res
  4338. # Test when getting roles for the specific owner client
  4339. client_specific_roles = await admin.a_get_client_specific_roles_of_client_scope(
  4340. client_scope_id=client_scope_id,
  4341. client_roles_owner_id=client,
  4342. )
  4343. assert len(client_specific_roles) == 1
  4344. client_role_names = [x["name"] for x in client_specific_roles]
  4345. assert _client_role_name in client_role_names, client_role_names
  4346. # Test when getting all roles for the client scope
  4347. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  4348. assert "clientMappings" in all_roles, all_roles
  4349. all_roles_clients = all_roles["clientMappings"]
  4350. assert client_name in all_roles_clients, all_roles_clients
  4351. mappings = all_roles_clients[client_name]["mappings"]
  4352. client_role_names = [x["name"] for x in mappings]
  4353. assert _client_role_name in client_role_names, client_role_names
  4354. # Test remove realm role of client
  4355. with pytest.raises(KeycloakDeleteError) as err:
  4356. await admin.a_remove_client_specific_roles_of_client_scope(
  4357. client_scope_id=client_scope_id,
  4358. client_roles_owner_id=client,
  4359. roles=["bad"],
  4360. )
  4361. assert err.match(UNKOWN_ERROR_REGEX), err
  4362. res = await admin.a_remove_client_specific_roles_of_client_scope(
  4363. client_scope_id=client_scope_id,
  4364. client_roles_owner_id=client,
  4365. roles=[await admin.a_get_client_role(client_id=client, role_name=_client_role_name)],
  4366. )
  4367. assert res == {}, res
  4368. all_roles = await admin.a_get_all_roles_of_client_scope(client_scope_id=client_scope_id)
  4369. assert len(all_roles) == 0
  4370. @pytest.mark.asyncio
  4371. async def test_a_client_default_client_scopes(
  4372. admin: KeycloakAdmin,
  4373. realm: str,
  4374. client: str,
  4375. ) -> None:
  4376. """
  4377. Test client assignment of default client scopes.
  4378. :param admin: Keycloak admin
  4379. :type admin: KeycloakAdmin
  4380. :param realm: Keycloak realm
  4381. :type realm: str
  4382. :param client: Keycloak client
  4383. :type client: str
  4384. """
  4385. await admin.a_change_current_realm(realm)
  4386. client_id = await admin.a_create_client(
  4387. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4388. )
  4389. # Test get client default scopes
  4390. # keycloak default roles: web-origins, acr, profile, roles, email
  4391. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4392. assert len(default_client_scopes) in [6, 5], default_client_scopes
  4393. # Test add a client scope to client default scopes
  4394. default_client_scope = "test-client-default-scope"
  4395. new_client_scope = {
  4396. "name": default_client_scope,
  4397. "description": f"Test Client Scope: {default_client_scope}",
  4398. "protocol": "openid-connect",
  4399. "attributes": {},
  4400. }
  4401. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  4402. new_default_client_scope_data = {
  4403. "realm": realm,
  4404. "client": client_id,
  4405. "clientScopeId": new_client_scope_id,
  4406. }
  4407. await admin.a_add_client_default_client_scope(
  4408. client_id,
  4409. new_client_scope_id,
  4410. new_default_client_scope_data,
  4411. )
  4412. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4413. assert len(default_client_scopes) in [6, 7], default_client_scopes
  4414. # Test remove a client default scope
  4415. await admin.a_delete_client_default_client_scope(client_id, new_client_scope_id)
  4416. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  4417. assert len(default_client_scopes) in [5, 6], default_client_scopes
  4418. @pytest.mark.asyncio
  4419. async def test_a_client_optional_client_scopes(
  4420. admin: KeycloakAdmin,
  4421. realm: str,
  4422. client: str,
  4423. ) -> None:
  4424. """
  4425. Test client assignment of optional client scopes.
  4426. :param admin: Keycloak admin
  4427. :type admin: KeycloakAdmin
  4428. :param realm: Keycloak realm
  4429. :type realm: str
  4430. :param client: Keycloak client
  4431. :type client: str
  4432. """
  4433. await admin.a_change_current_realm(realm)
  4434. client_id = await admin.a_create_client(
  4435. payload={"name": "role-testing-client", "clientId": "role-testing-client"},
  4436. )
  4437. # Test get client optional scopes
  4438. # keycloak optional roles: microprofile-jwt, offline_access, address, --> for versions < 26.0.0
  4439. # starting with Keycloak version 26.0.0 a new optional role is added: organization
  4440. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4441. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  4442. # Test add a client scope to client optional scopes
  4443. optional_client_scope = "test-client-optional-scope"
  4444. new_client_scope = {
  4445. "name": optional_client_scope,
  4446. "description": f"Test Client Scope: {optional_client_scope}",
  4447. "protocol": "openid-connect",
  4448. "attributes": {},
  4449. }
  4450. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  4451. new_optional_client_scope_data = {
  4452. "realm": realm,
  4453. "client": client_id,
  4454. "clientScopeId": new_client_scope_id,
  4455. }
  4456. await admin.a_add_client_optional_client_scope(
  4457. client_id,
  4458. new_client_scope_id,
  4459. new_optional_client_scope_data,
  4460. )
  4461. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4462. assert len(optional_client_scopes) in [5, 6], optional_client_scopes
  4463. # Test remove a client optional scope
  4464. await admin.a_delete_client_optional_client_scope(client_id, new_client_scope_id)
  4465. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  4466. assert len(optional_client_scopes) in [4, 5], optional_client_scopes
  4467. @pytest.mark.asyncio
  4468. async def test_a_client_roles(admin: KeycloakAdmin, client: str) -> None:
  4469. """
  4470. Test client roles.
  4471. :param admin: Keycloak Admin client
  4472. :type admin: KeycloakAdmin
  4473. :param client: Keycloak client
  4474. :type client: str
  4475. """
  4476. # Test get client roles
  4477. res = await admin.a_get_client_roles(client_id=client)
  4478. assert len(res) == 0
  4479. with pytest.raises(KeycloakGetError) as err:
  4480. await admin.a_get_client_roles(client_id="bad")
  4481. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  4482. # Test create client role
  4483. client_role_id = await admin.a_create_client_role(
  4484. client_role_id=client,
  4485. payload={"name": "client-role-test"},
  4486. skip_exists=True,
  4487. )
  4488. with pytest.raises(KeycloakPostError) as err:
  4489. await admin.a_create_client_role(
  4490. client_role_id=client,
  4491. payload={"name": "client-role-test"},
  4492. )
  4493. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  4494. client_role_id_2 = await admin.a_create_client_role(
  4495. client_role_id=client,
  4496. payload={"name": "client-role-test"},
  4497. skip_exists=True,
  4498. )
  4499. assert client_role_id == client_role_id_2
  4500. # Test get client role
  4501. res = await admin.a_get_client_role(client_id=client, role_name="client-role-test")
  4502. assert res["name"] == client_role_id
  4503. with pytest.raises(KeycloakGetError) as err:
  4504. await admin.a_get_client_role(client_id=client, role_name="bad")
  4505. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4506. res_ = await admin.a_get_client_role_id(client_id=client, role_name="client-role-test")
  4507. assert res_ == res["id"]
  4508. with pytest.raises(KeycloakGetError) as err:
  4509. await admin.a_get_client_role_id(client_id=client, role_name="bad")
  4510. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4511. assert len(await admin.a_get_client_roles(client_id=client)) == 1
  4512. # Test update client role
  4513. res = await admin.a_update_client_role(
  4514. client_id=client,
  4515. role_name="client-role-test",
  4516. payload={"name": "client-role-test-update"},
  4517. )
  4518. assert res == {}
  4519. with pytest.raises(KeycloakPutError) as err:
  4520. res = await admin.a_update_client_role(
  4521. client_id=client,
  4522. role_name="client-role-test",
  4523. payload={"name": "client-role-test-update"},
  4524. )
  4525. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4526. # Test user with client role
  4527. res = await admin.a_get_client_role_members(
  4528. client_id=client,
  4529. role_name="client-role-test-update",
  4530. )
  4531. assert len(res) == 0
  4532. with pytest.raises(KeycloakGetError) as err:
  4533. await admin.a_get_client_role_members(client_id=client, role_name="bad")
  4534. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4535. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  4536. with pytest.raises(KeycloakPostError) as err:
  4537. await admin.a_assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  4538. assert err.match(UNKOWN_ERROR_REGEX), err
  4539. res = await admin.a_assign_client_role(
  4540. user_id=user_id,
  4541. client_id=client,
  4542. roles=[
  4543. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4544. ],
  4545. )
  4546. assert res == {}
  4547. assert (
  4548. len(
  4549. await admin.a_get_client_role_members(
  4550. client_id=client,
  4551. role_name="client-role-test-update",
  4552. ),
  4553. )
  4554. == 1
  4555. )
  4556. roles = await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)
  4557. assert len(roles) == 1, roles
  4558. with pytest.raises(KeycloakGetError) as err:
  4559. await admin.a_get_client_roles_of_user(user_id=user_id, client_id="bad")
  4560. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4561. roles = await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  4562. assert len(roles) == 1, roles
  4563. with pytest.raises(KeycloakGetError) as err:
  4564. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4565. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4566. roles = await admin.a_get_available_client_roles_of_user(user_id=user_id, client_id=client)
  4567. assert len(roles) == 0, roles
  4568. with pytest.raises(KeycloakGetError) as err:
  4569. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4570. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4571. with pytest.raises(KeycloakDeleteError) as err:
  4572. await admin.a_delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  4573. assert err.match(UNKOWN_ERROR_REGEX), err
  4574. await admin.a_delete_client_roles_of_user(
  4575. user_id=user_id,
  4576. client_id=client,
  4577. roles=[
  4578. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4579. ],
  4580. )
  4581. assert len(await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  4582. # Test groups and client roles
  4583. res = await admin.a_get_client_role_groups(
  4584. client_id=client,
  4585. role_name="client-role-test-update",
  4586. )
  4587. assert len(res) == 0
  4588. with pytest.raises(KeycloakGetError) as err:
  4589. await admin.a_get_client_role_groups(client_id=client, role_name="bad")
  4590. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4591. group_id = await admin.a_create_group(payload={"name": "test-group"})
  4592. res = await admin.a_get_group_client_roles(group_id=group_id, client_id=client)
  4593. assert len(res) == 0
  4594. with pytest.raises(KeycloakGetError) as err:
  4595. await admin.a_get_group_client_roles(group_id=group_id, client_id="bad")
  4596. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4597. with pytest.raises(KeycloakPostError) as err:
  4598. await admin.a_assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4599. assert err.match(UNKOWN_ERROR_REGEX), err
  4600. res = await admin.a_assign_group_client_roles(
  4601. group_id=group_id,
  4602. client_id=client,
  4603. roles=[
  4604. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4605. ],
  4606. )
  4607. assert res == {}
  4608. assert (
  4609. len(
  4610. await admin.a_get_client_role_groups(
  4611. client_id=client,
  4612. role_name="client-role-test-update",
  4613. ),
  4614. )
  4615. == 1
  4616. )
  4617. assert len(await admin.a_get_group_client_roles(group_id=group_id, client_id=client)) == 1
  4618. with pytest.raises(KeycloakDeleteError) as err:
  4619. await admin.a_delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4620. assert err.match(UNKOWN_ERROR_REGEX), err
  4621. res = await admin.a_delete_group_client_roles(
  4622. group_id=group_id,
  4623. client_id=client,
  4624. roles=[
  4625. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"),
  4626. ],
  4627. )
  4628. assert res == {}
  4629. # Test composite client roles
  4630. with pytest.raises(KeycloakPostError) as err:
  4631. await admin.a_add_composite_client_roles_to_role(
  4632. client_role_id=client,
  4633. role_name="client-role-test-update",
  4634. roles=["bad"],
  4635. )
  4636. assert err.match(UNKOWN_ERROR_REGEX), err
  4637. res = await admin.a_add_composite_client_roles_to_role(
  4638. client_role_id=client,
  4639. role_name="client-role-test-update",
  4640. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4641. )
  4642. assert res == {}
  4643. assert (await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"))[
  4644. "composite"
  4645. ]
  4646. # Test removal of composite client roles
  4647. with pytest.raises(KeycloakDeleteError) as err:
  4648. await admin.a_remove_composite_client_roles_from_role(
  4649. client_role_id=client,
  4650. role_name="client-role-test-update",
  4651. roles=["bad"],
  4652. )
  4653. assert err.match(UNKOWN_ERROR_REGEX), err
  4654. res = await admin.a_remove_composite_client_roles_from_role(
  4655. client_role_id=client,
  4656. role_name="client-role-test-update",
  4657. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4658. )
  4659. assert res == {}
  4660. assert not (
  4661. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4662. )["composite"]
  4663. # Test delete of client role
  4664. res = await admin.a_delete_client_role(
  4665. client_role_id=client,
  4666. role_name="client-role-test-update",
  4667. )
  4668. assert res == {}
  4669. with pytest.raises(KeycloakDeleteError) as err:
  4670. await admin.a_delete_client_role(
  4671. client_role_id=client,
  4672. role_name="client-role-test-update",
  4673. )
  4674. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4675. # Test of roles by id - Get role
  4676. await admin.a_create_client_role(
  4677. client_role_id=client,
  4678. payload={"name": "client-role-by-id-test"},
  4679. skip_exists=True,
  4680. )
  4681. role = await admin.a_get_client_role(client_id=client, role_name="client-role-by-id-test")
  4682. res = await admin.a_get_role_by_id(role_id=role["id"])
  4683. assert res["name"] == "client-role-by-id-test"
  4684. with pytest.raises(KeycloakGetError) as err:
  4685. await admin.a_get_role_by_id(role_id="bad")
  4686. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4687. # Test of roles by id - Update role
  4688. res = await admin.a_update_role_by_id(
  4689. role_id=role["id"],
  4690. payload={"name": "client-role-by-id-test-update"},
  4691. )
  4692. assert res == {}
  4693. with pytest.raises(KeycloakPutError) as err:
  4694. res = await admin.a_update_role_by_id(
  4695. role_id="bad",
  4696. payload={"name": "client-role-by-id-test-update"},
  4697. )
  4698. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4699. # Test of roles by id - Delete role
  4700. res = await admin.a_delete_role_by_id(role_id=role["id"])
  4701. assert res == {}
  4702. with pytest.raises(KeycloakDeleteError) as err:
  4703. await admin.a_delete_role_by_id(role_id="bad")
  4704. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4705. @pytest.mark.asyncio
  4706. async def test_a_enable_token_exchange(admin: KeycloakAdmin, realm: str) -> None:
  4707. """
  4708. Test enable token exchange.
  4709. :param admin: Keycloak Admin client
  4710. :type admin: KeycloakAdmin
  4711. :param realm: Keycloak realm
  4712. :type realm: str
  4713. :raises AssertionError: In case of bad configuration
  4714. """
  4715. # Test enabling token exchange between two confidential clients
  4716. await admin.a_change_current_realm(realm)
  4717. # Create test clients
  4718. source_client_id = await admin.a_create_client(
  4719. payload={"name": "Source Client", "clientId": "source-client"},
  4720. )
  4721. target_client_id = await admin.a_create_client(
  4722. payload={"name": "Target Client", "clientId": "target-client"},
  4723. )
  4724. for c in await admin.a_get_clients():
  4725. if c["clientId"] == "realm-management":
  4726. realm_management_id = c["id"]
  4727. break
  4728. else:
  4729. pytest.fail("Missing realm management client")
  4730. # Enable permissions on the Superset client
  4731. await admin.a_update_client_management_permissions(
  4732. payload={"enabled": True},
  4733. client_id=target_client_id,
  4734. )
  4735. # Fetch various IDs and strings needed when creating the permission
  4736. token_exchange_permission_id = (
  4737. await admin.a_get_client_management_permissions(client_id=target_client_id)
  4738. )["scopePermissions"]["token-exchange"]
  4739. scopes = await admin.a_get_client_authz_policy_scopes(
  4740. client_id=realm_management_id,
  4741. policy_id=token_exchange_permission_id,
  4742. )
  4743. for s in scopes:
  4744. if s["name"] == "token-exchange":
  4745. token_exchange_scope_id = s["id"]
  4746. break
  4747. else:
  4748. pytest.fail("Missing token-exchange scope")
  4749. resources = await admin.a_get_client_authz_policy_resources(
  4750. client_id=realm_management_id,
  4751. policy_id=token_exchange_permission_id,
  4752. )
  4753. for r in resources:
  4754. if r["name"] == f"client.resource.{target_client_id}":
  4755. token_exchange_resource_id = r["_id"]
  4756. break
  4757. else:
  4758. pytest.fail("Missing client resource")
  4759. # Create a client policy for source client
  4760. policy_name = "Exchange source client token with target client token"
  4761. client_policy_id = (
  4762. await admin.a_create_client_authz_client_policy(
  4763. payload={
  4764. "type": "client",
  4765. "logic": "POSITIVE",
  4766. "decisionStrategy": "UNANIMOUS",
  4767. "name": policy_name,
  4768. "clients": [source_client_id],
  4769. },
  4770. client_id=realm_management_id,
  4771. )
  4772. )["id"]
  4773. policies = await admin.a_get_client_authz_client_policies(client_id=realm_management_id)
  4774. for policy in policies:
  4775. if policy["name"] == policy_name:
  4776. assert policy["clients"] == [source_client_id]
  4777. break
  4778. else:
  4779. pytest.fail("Missing client policy")
  4780. # Update permissions on the target client to reference this policy
  4781. permission_name = (
  4782. await admin.a_get_client_authz_scope_permission(
  4783. client_id=realm_management_id,
  4784. scope_id=token_exchange_permission_id,
  4785. )
  4786. )["name"]
  4787. await admin.a_update_client_authz_scope_permission(
  4788. payload={
  4789. "id": token_exchange_permission_id,
  4790. "name": permission_name,
  4791. "type": "scope",
  4792. "logic": "POSITIVE",
  4793. "decisionStrategy": "UNANIMOUS",
  4794. "resources": [token_exchange_resource_id],
  4795. "scopes": [token_exchange_scope_id],
  4796. "policies": [client_policy_id],
  4797. },
  4798. client_id=realm_management_id,
  4799. scope_id=token_exchange_permission_id,
  4800. )
  4801. # Create permissions on the target client to reference this policy
  4802. await admin.a_create_client_authz_scope_permission(
  4803. payload={
  4804. "id": "some-id",
  4805. "name": "test-permission",
  4806. "type": "scope",
  4807. "logic": "POSITIVE",
  4808. "decisionStrategy": "UNANIMOUS",
  4809. "resources": [token_exchange_resource_id],
  4810. "scopes": [token_exchange_scope_id],
  4811. "policies": [client_policy_id],
  4812. },
  4813. client_id=realm_management_id,
  4814. )
  4815. permission_name = (
  4816. await admin.a_get_client_authz_scope_permission(
  4817. client_id=realm_management_id,
  4818. scope_id=token_exchange_permission_id,
  4819. )
  4820. )["name"]
  4821. assert permission_name.startswith("token-exchange.permission.client.")
  4822. with pytest.raises(KeycloakPostError) as err:
  4823. await admin.a_create_client_authz_scope_permission(
  4824. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  4825. client_id="realm_management_id",
  4826. )
  4827. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  4828. @pytest.mark.asyncio
  4829. async def test_a_email(admin: KeycloakAdmin, user: str) -> None:
  4830. """
  4831. Test email.
  4832. :param admin: Keycloak Admin client
  4833. :type admin: KeycloakAdmin
  4834. :param user: Keycloak user
  4835. :type user: str
  4836. """
  4837. # Emails will fail as we don't have SMTP test setup
  4838. with pytest.raises(KeycloakPutError) as err:
  4839. await admin.a_send_update_account(user_id=user, payload={})
  4840. assert err.match(UNKOWN_ERROR_REGEX), err
  4841. admin.update_user(user_id=user, payload={"enabled": True})
  4842. with pytest.raises(KeycloakPutError) as err:
  4843. await admin.a_send_verify_email(user_id=user)
  4844. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  4845. @pytest.mark.asyncio
  4846. async def test_a_email_query_param_handling(admin: KeycloakAdmin, user: str) -> None:
  4847. """
  4848. Test that the optional parameters are correctly transformed into query params.
  4849. :param admin: Keycloak Admin client
  4850. :type admin: KeycloakAdmin
  4851. :param user: Keycloak user
  4852. :type user: str
  4853. """
  4854. with (
  4855. patch.object(
  4856. admin.connection.async_s,
  4857. "put",
  4858. side_effect=Exception("An expected error"),
  4859. ) as mock_put,
  4860. pytest.raises(KeycloakConnectionError),
  4861. ):
  4862. await admin.a_send_update_account(
  4863. user_id=user,
  4864. payload=["UPDATE_PASSWORD"],
  4865. client_id="update-account-client-id",
  4866. redirect_uri="https://example.com",
  4867. )
  4868. mock_put.assert_awaited_once_with(
  4869. ANY,
  4870. data='["UPDATE_PASSWORD"]',
  4871. params={"client_id": "update-account-client-id", "redirect_uri": "https://example.com"},
  4872. headers=ANY,
  4873. timeout=60,
  4874. )
  4875. with (
  4876. patch.object(
  4877. admin.connection.async_s,
  4878. "put",
  4879. side_effect=Exception("An expected error"),
  4880. ) as mock_put,
  4881. pytest.raises(KeycloakConnectionError),
  4882. ):
  4883. await admin.a_send_verify_email(
  4884. user_id=user,
  4885. client_id="verify-client-id",
  4886. redirect_uri="https://example.com",
  4887. )
  4888. mock_put.assert_awaited_once_with(
  4889. ANY,
  4890. data=ANY,
  4891. params={"client_id": "verify-client-id", "redirect_uri": "https://example.com"},
  4892. headers=ANY,
  4893. timeout=60,
  4894. )
  4895. @pytest.mark.asyncio
  4896. async def test_a_get_sessions(admin: KeycloakAdmin) -> None:
  4897. """
  4898. Test get sessions.
  4899. :param admin: Keycloak Admin client
  4900. :type admin: KeycloakAdmin
  4901. """
  4902. sessions = await admin.a_get_sessions(
  4903. user_id=admin.get_user_id(username=admin.connection.username),
  4904. )
  4905. assert len(sessions) >= 1
  4906. with pytest.raises(KeycloakGetError) as err:
  4907. await admin.a_get_sessions(user_id="bad")
  4908. assert err.match(USER_NOT_FOUND_REGEX)
  4909. @pytest.mark.asyncio
  4910. async def test_a_get_client_installation_provider(admin: KeycloakAdmin, client: str) -> None:
  4911. """
  4912. Test get client installation provider.
  4913. :param admin: Keycloak Admin client
  4914. :type admin: KeycloakAdmin
  4915. :param client: Keycloak client
  4916. :type client: str
  4917. """
  4918. with pytest.raises(KeycloakGetError) as err:
  4919. await admin.a_get_client_installation_provider(client_id=client, provider_id="bad")
  4920. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  4921. installation = await admin.a_get_client_installation_provider(
  4922. client_id=client,
  4923. provider_id="keycloak-oidc-keycloak-json",
  4924. )
  4925. assert set(installation.keys()) == {
  4926. "auth-server-url",
  4927. "confidential-port",
  4928. "credentials",
  4929. "realm",
  4930. "resource",
  4931. "ssl-required",
  4932. }
  4933. @pytest.mark.asyncio
  4934. async def test_a_auth_flows(admin: KeycloakAdmin, realm: str) -> None:
  4935. """
  4936. Test auth flows.
  4937. :param admin: Keycloak Admin client
  4938. :type admin: KeycloakAdmin
  4939. :param realm: Keycloak realm
  4940. :type realm: str
  4941. """
  4942. await admin.a_change_current_realm(realm)
  4943. res = await admin.a_get_authentication_flows()
  4944. assert len(res) <= 8, res
  4945. default_flows = len(res)
  4946. assert {x["alias"] for x in res}.issubset(
  4947. {
  4948. "reset credentials",
  4949. "browser",
  4950. "registration",
  4951. "http challenge",
  4952. "docker auth",
  4953. "direct grant",
  4954. "first broker login",
  4955. "clients",
  4956. },
  4957. )
  4958. assert set(res[0].keys()) == {
  4959. "alias",
  4960. "authenticationExecutions",
  4961. "builtIn",
  4962. "description",
  4963. "id",
  4964. "providerId",
  4965. "topLevel",
  4966. }
  4967. assert {x["alias"] for x in res}.issubset(
  4968. {
  4969. "reset credentials",
  4970. "browser",
  4971. "registration",
  4972. "docker auth",
  4973. "direct grant",
  4974. "first broker login",
  4975. "clients",
  4976. "http challenge",
  4977. },
  4978. )
  4979. with pytest.raises(KeycloakGetError) as err:
  4980. await admin.a_get_authentication_flow_for_id(flow_id="bad")
  4981. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  4982. browser_flow_id = next(x for x in res if x["alias"] == "browser")["id"]
  4983. res = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
  4984. assert res["alias"] == "browser"
  4985. # Test copying
  4986. with pytest.raises(KeycloakPostError) as err:
  4987. await admin.a_copy_authentication_flow(payload={}, flow_alias="bad")
  4988. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  4989. res = await admin.a_copy_authentication_flow(
  4990. payload={"newName": "test-browser"},
  4991. flow_alias="browser",
  4992. )
  4993. assert res == b"", res
  4994. assert len(await admin.a_get_authentication_flows()) == (default_flows + 1)
  4995. # Test create
  4996. res = await admin.a_create_authentication_flow(
  4997. payload={"alias": "test-create", "providerId": "basic-flow"},
  4998. )
  4999. assert res == b""
  5000. with pytest.raises(KeycloakPostError) as err:
  5001. await admin.a_create_authentication_flow(
  5002. payload={"alias": "test-create", "builtIn": False},
  5003. )
  5004. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  5005. assert await admin.a_create_authentication_flow(
  5006. payload={"alias": "test-create"},
  5007. skip_exists=True,
  5008. ) == {"msg": "Already exists"}
  5009. # Test flow executions
  5010. res = await admin.a_get_authentication_flow_executions(flow_alias="browser")
  5011. assert len(res) in [8, 12], res
  5012. with pytest.raises(KeycloakGetError) as err:
  5013. await admin.a_get_authentication_flow_executions(flow_alias="bad")
  5014. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  5015. exec_id = res[0]["id"]
  5016. res = await admin.a_get_authentication_flow_execution(execution_id=exec_id)
  5017. assert set(res.keys()).issubset(
  5018. {
  5019. "alternative",
  5020. "authenticator",
  5021. "authenticatorFlow",
  5022. "autheticatorFlow",
  5023. "conditional",
  5024. "disabled",
  5025. "enabled",
  5026. "id",
  5027. "parentFlow",
  5028. "priority",
  5029. "required",
  5030. "requirement",
  5031. },
  5032. ), res.keys()
  5033. with pytest.raises(KeycloakGetError) as err:
  5034. await admin.a_get_authentication_flow_execution(execution_id="bad")
  5035. assert err.match(ILLEGAL_EXECUTION_REGEX)
  5036. with pytest.raises(KeycloakPostError) as err:
  5037. await admin.a_create_authentication_flow_execution(payload={}, flow_alias="browser")
  5038. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  5039. res = await admin.a_create_authentication_flow_execution(
  5040. payload={"provider": "auth-cookie"},
  5041. flow_alias="test-create",
  5042. )
  5043. assert res == b""
  5044. assert len(await admin.a_get_authentication_flow_executions(flow_alias="test-create")) == 1
  5045. with pytest.raises(KeycloakPutError) as err:
  5046. await admin.a_update_authentication_flow_executions(
  5047. payload={"required": "yes"},
  5048. flow_alias="test-create",
  5049. )
  5050. assert err.match("Unrecognized field")
  5051. payload = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]
  5052. payload["displayName"] = "test"
  5053. res = await admin.a_update_authentication_flow_executions(
  5054. payload=payload,
  5055. flow_alias="test-create",
  5056. )
  5057. assert res or (res == {})
  5058. exec_id = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]["id"]
  5059. res = await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  5060. assert res == {}
  5061. with pytest.raises(KeycloakDeleteError) as err:
  5062. await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  5063. assert err.match(ILLEGAL_EXECUTION_REGEX)
  5064. # Test subflows
  5065. res = await admin.a_create_authentication_flow_subflow(
  5066. payload={
  5067. "alias": "test-subflow",
  5068. "provider": "basic-flow",
  5069. "type": "something",
  5070. "description": "something",
  5071. },
  5072. flow_alias="test-browser",
  5073. )
  5074. assert res == b""
  5075. with pytest.raises(KeycloakPostError) as err:
  5076. await admin.a_create_authentication_flow_subflow(
  5077. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  5078. flow_alias="test-browser",
  5079. )
  5080. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  5081. res = await admin.a_create_authentication_flow_subflow(
  5082. payload={
  5083. "alias": "test-subflow",
  5084. "provider": "basic-flow",
  5085. "type": "something",
  5086. "description": "something",
  5087. },
  5088. flow_alias="test-create",
  5089. skip_exists=True,
  5090. )
  5091. assert res == {"msg": "Already exists"}
  5092. # Test delete auth flow
  5093. flow_id = next(
  5094. x for x in await admin.a_get_authentication_flows() if x["alias"] == "test-browser"
  5095. )["id"]
  5096. res = await admin.a_delete_authentication_flow(flow_id=flow_id)
  5097. assert res == {}
  5098. with pytest.raises(KeycloakDeleteError) as err:
  5099. await admin.a_delete_authentication_flow(flow_id=flow_id)
  5100. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  5101. 'b\'{"error":"Flow not found"' in str(err)
  5102. )
  5103. @pytest.mark.asyncio
  5104. async def test_a_authentication_configs(admin: KeycloakAdmin, realm: str) -> None:
  5105. """
  5106. Test authentication configs.
  5107. :param admin: Keycloak Admin client
  5108. :type admin: KeycloakAdmin
  5109. :param realm: Keycloak realm
  5110. :type realm: str
  5111. """
  5112. admin.change_current_realm(realm)
  5113. # Test list of auth providers
  5114. res = await admin.a_get_authenticator_providers()
  5115. assert len(res) <= 40
  5116. res = await admin.a_get_authenticator_provider_config_description(provider_id="auth-cookie")
  5117. assert res == {
  5118. "helpText": "Validates the SSO cookie set by the auth server.",
  5119. "name": "Cookie",
  5120. "properties": [],
  5121. "providerId": "auth-cookie",
  5122. }
  5123. # Test authenticator config
  5124. # Currently unable to find a sustainable way to fetch the config id,
  5125. # therefore testing only failures
  5126. with pytest.raises(KeycloakGetError) as err:
  5127. await admin.a_get_authenticator_config(config_id="bad")
  5128. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5129. with pytest.raises(KeycloakPutError) as err:
  5130. await admin.a_update_authenticator_config(payload={}, config_id="bad")
  5131. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5132. with pytest.raises(KeycloakDeleteError) as err:
  5133. await admin.a_delete_authenticator_config(config_id="bad")
  5134. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  5135. @pytest.mark.asyncio
  5136. async def test_a_sync_users(admin: KeycloakAdmin, realm: str) -> None:
  5137. """
  5138. Test sync users.
  5139. :param admin: Keycloak Admin client
  5140. :type admin: KeycloakAdmin
  5141. :param realm: Keycloak realm
  5142. :type realm: str
  5143. """
  5144. await admin.a_change_current_realm(realm)
  5145. # Only testing the error message
  5146. with pytest.raises(KeycloakPostError) as err:
  5147. await admin.a_sync_users(storage_id="does-not-exist", action="triggerFullSync")
  5148. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5149. @pytest.mark.asyncio
  5150. async def test_a_client_scopes(admin: KeycloakAdmin, realm: str) -> None:
  5151. """
  5152. Test client scopes.
  5153. :param admin: Keycloak Admin client
  5154. :type admin: KeycloakAdmin
  5155. :param realm: Keycloak realm
  5156. :type realm: str
  5157. """
  5158. await admin.a_change_current_realm(realm)
  5159. # Test get client scopes
  5160. res = await admin.a_get_client_scopes()
  5161. scope_names = {x["name"] for x in res}
  5162. assert len(res) in [10, 11, 13, 14]
  5163. assert "email" in scope_names
  5164. assert "profile" in scope_names
  5165. assert "offline_access" in scope_names
  5166. with pytest.raises(KeycloakGetError) as err:
  5167. await admin.a_get_client_scope(client_scope_id="does-not-exist")
  5168. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5169. scope = await admin.a_get_client_scope(client_scope_id=res[0]["id"])
  5170. assert res[0] == scope
  5171. scope = await admin.a_get_client_scope_by_name(client_scope_name=res[0]["name"])
  5172. assert res[0] == scope
  5173. # Test create client scope
  5174. res = await admin.a_create_client_scope(
  5175. payload={"name": "test-scope", "protocol": "openid-connect"},
  5176. skip_exists=True,
  5177. )
  5178. assert res
  5179. res2 = await admin.a_create_client_scope(
  5180. payload={"name": "test-scope", "protocol": "openid-connect"},
  5181. skip_exists=True,
  5182. )
  5183. assert res == res2
  5184. with pytest.raises(KeycloakPostError) as err:
  5185. await admin.a_create_client_scope(
  5186. payload={"name": "test-scope", "protocol": "openid-connect"},
  5187. skip_exists=False,
  5188. )
  5189. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  5190. # Test update client scope
  5191. with pytest.raises(KeycloakPutError) as err:
  5192. await admin.a_update_client_scope(client_scope_id="does-not-exist", payload={})
  5193. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5194. res_update = await admin.a_update_client_scope(
  5195. client_scope_id=res,
  5196. payload={"name": "test-scope-update"},
  5197. )
  5198. assert res_update == {}
  5199. assert (await admin.a_get_client_scope(client_scope_id=res))["name"] == "test-scope-update"
  5200. # Test get mappers
  5201. mappers = await admin.a_get_mappers_from_client_scope(client_scope_id=res)
  5202. assert mappers == []
  5203. # Test add mapper
  5204. with pytest.raises(KeycloakPostError) as err:
  5205. await admin.a_add_mapper_to_client_scope(client_scope_id=res, payload={})
  5206. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  5207. res_add = await admin.a_add_mapper_to_client_scope(
  5208. client_scope_id=res,
  5209. payload={
  5210. "name": "test-mapper",
  5211. "protocol": "openid-connect",
  5212. "protocolMapper": "oidc-usermodel-attribute-mapper",
  5213. },
  5214. )
  5215. assert res_add == b""
  5216. assert len(await admin.a_get_mappers_from_client_scope(client_scope_id=res)) == 1
  5217. # Test update mapper
  5218. test_mapper = (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]
  5219. with pytest.raises(KeycloakPutError) as err:
  5220. await admin.a_update_mapper_in_client_scope(
  5221. client_scope_id="does-not-exist",
  5222. protocol_mapper_id=test_mapper["id"],
  5223. payload={},
  5224. )
  5225. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5226. test_mapper["config"]["user.attribute"] = "test"
  5227. res_update = await admin.a_update_mapper_in_client_scope(
  5228. client_scope_id=res,
  5229. protocol_mapper_id=test_mapper["id"],
  5230. payload=test_mapper,
  5231. )
  5232. assert res_update == {}
  5233. assert (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]["config"][
  5234. "user.attribute"
  5235. ] == "test"
  5236. # Test delete mapper
  5237. res_del = await admin.a_delete_mapper_from_client_scope(
  5238. client_scope_id=res,
  5239. protocol_mapper_id=test_mapper["id"],
  5240. )
  5241. assert res_del == {}
  5242. with pytest.raises(KeycloakDeleteError) as err:
  5243. await admin.a_delete_mapper_from_client_scope(
  5244. client_scope_id=res,
  5245. protocol_mapper_id=test_mapper["id"],
  5246. )
  5247. assert err.match('404: b\'{"error":"Model not found".*}\'')
  5248. # Test default default scopes
  5249. res_defaults = await admin.a_get_default_default_client_scopes()
  5250. assert len(res_defaults) in [6, 7, 8]
  5251. with pytest.raises(KeycloakPutError) as err:
  5252. await admin.a_add_default_default_client_scope(scope_id="does-not-exist")
  5253. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5254. res_add = await admin.a_add_default_default_client_scope(scope_id=res)
  5255. assert res_add == {}
  5256. assert len(admin.get_default_default_client_scopes()) in [7, 8, 9]
  5257. with pytest.raises(KeycloakDeleteError) as err:
  5258. await admin.a_delete_default_default_client_scope(scope_id="does-not-exist")
  5259. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5260. res_del = await admin.a_delete_default_default_client_scope(scope_id=res)
  5261. assert res_del == {}
  5262. assert len(admin.get_default_default_client_scopes()) in [6, 7, 8]
  5263. # Test default optional scopes
  5264. res_defaults = await admin.a_get_default_optional_client_scopes()
  5265. assert len(res_defaults) in [4, 5]
  5266. with pytest.raises(KeycloakPutError) as err:
  5267. await admin.a_add_default_optional_client_scope(scope_id="does-not-exist")
  5268. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5269. res_add = await admin.a_add_default_optional_client_scope(scope_id=res)
  5270. assert res_add == {}
  5271. assert len(await admin.a_get_default_optional_client_scopes()) in [5, 6]
  5272. with pytest.raises(KeycloakDeleteError) as err:
  5273. await admin.a_delete_default_optional_client_scope(scope_id="does-not-exist")
  5274. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  5275. res_del = await admin.a_delete_default_optional_client_scope(scope_id=res)
  5276. assert res_del == {}
  5277. assert len(await admin.a_get_default_optional_client_scopes()) in [4, 5]
  5278. # Test client scope delete
  5279. res_del = await admin.a_delete_client_scope(client_scope_id=res)
  5280. assert res_del == {}
  5281. with pytest.raises(KeycloakDeleteError) as err:
  5282. await admin.a_delete_client_scope(client_scope_id=res)
  5283. assert err.match(NO_CLIENT_SCOPE_REGEX)
  5284. @pytest.mark.asyncio
  5285. async def test_a_components(admin: KeycloakAdmin, realm: str) -> None:
  5286. """
  5287. Test components.
  5288. :param admin: Keycloak Admin client
  5289. :type admin: KeycloakAdmin
  5290. :param realm: Keycloak realm
  5291. :type realm: str
  5292. """
  5293. await admin.a_change_current_realm(realm)
  5294. # Test get components
  5295. res = await admin.a_get_components()
  5296. assert len(res) == 12
  5297. with pytest.raises(KeycloakGetError) as err:
  5298. await admin.a_get_component(component_id="does-not-exist")
  5299. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5300. res_get = await admin.a_get_component(component_id=res[0]["id"])
  5301. assert res_get == res[0]
  5302. # Test create component
  5303. with pytest.raises(KeycloakPostError) as err:
  5304. await admin.a_create_component(payload={"bad": "dict"})
  5305. assert err.match("Unrecognized field")
  5306. res = await admin.a_create_component(
  5307. payload={
  5308. "name": "Test Component",
  5309. "providerId": "max-clients",
  5310. "providerType": "org.keycloak.services.clientregistration."
  5311. "policy.ClientRegistrationPolicy",
  5312. "config": {"max-clients": ["1000"]},
  5313. },
  5314. )
  5315. assert res
  5316. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component"
  5317. # Test update component
  5318. component = await admin.a_get_component(component_id=res)
  5319. component["name"] = "Test Component Update"
  5320. with pytest.raises(KeycloakPutError) as err:
  5321. await admin.a_update_component(component_id="does-not-exist", payload={})
  5322. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5323. res_upd = await admin.a_update_component(component_id=res, payload=component)
  5324. assert res_upd == {}
  5325. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component Update"
  5326. # Test delete component
  5327. res_del = await admin.a_delete_component(component_id=res)
  5328. assert res_del == {}
  5329. with pytest.raises(KeycloakDeleteError) as err:
  5330. await admin.a_delete_component(component_id=res)
  5331. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  5332. @pytest.mark.asyncio
  5333. async def test_a_keys(admin: KeycloakAdmin, realm: str) -> None:
  5334. """
  5335. Test keys.
  5336. :param admin: Keycloak Admin client
  5337. :type admin: KeycloakAdmin
  5338. :param realm: Keycloak realm
  5339. :type realm: str
  5340. """
  5341. await admin.a_change_current_realm(realm)
  5342. assert set((await admin.a_get_keys())["active"].keys()) == {
  5343. "AES",
  5344. "HS256",
  5345. "RS256",
  5346. "RSA-OAEP",
  5347. } or set((await admin.a_get_keys())["active"].keys()) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  5348. assert {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  5349. "HS256",
  5350. "RSA-OAEP",
  5351. "AES",
  5352. "RS256",
  5353. } or {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  5354. "HS512",
  5355. "RSA-OAEP",
  5356. "AES",
  5357. "RS256",
  5358. }
  5359. @pytest.mark.asyncio
  5360. async def test_a_admin_events(admin: KeycloakAdmin, realm: str) -> None:
  5361. """
  5362. Test events.
  5363. :param admin: Keycloak Admin client
  5364. :type admin: KeycloakAdmin
  5365. :param realm: Keycloak realm
  5366. :type realm: str
  5367. """
  5368. await admin.a_change_current_realm(realm)
  5369. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  5370. events = await admin.a_get_admin_events()
  5371. assert events == []
  5372. @pytest.mark.asyncio
  5373. async def test_a_user_events(admin: KeycloakAdmin, realm: str) -> None:
  5374. """
  5375. Test events.
  5376. :param admin: Keycloak Admin client
  5377. :type admin: KeycloakAdmin
  5378. :param realm: Keycloak realm
  5379. :type realm: str
  5380. """
  5381. await admin.a_change_current_realm(realm)
  5382. events = await admin.a_get_events()
  5383. assert events == []
  5384. with pytest.raises(KeycloakPutError) as err:
  5385. await admin.a_set_events(payload={"bad": "conf"})
  5386. assert err.match("Unrecognized field")
  5387. res = await admin.a_set_events(
  5388. payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True},
  5389. )
  5390. assert res == {}
  5391. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  5392. events = await admin.a_get_events()
  5393. assert events == []
  5394. @pytest.mark.asyncio
  5395. @freezegun.freeze_time("2023-02-25 10:00:00")
  5396. async def test_a_auto_refresh(admin_frozen: KeycloakAdmin, realm: str) -> None:
  5397. """
  5398. Test auto refresh token.
  5399. :param admin_frozen: Keycloak Admin client with time frozen in place
  5400. :type admin_frozen: KeycloakAdmin
  5401. :param realm: Keycloak realm
  5402. :type realm: str
  5403. """
  5404. admin = admin_frozen
  5405. admin.get_realm(realm)
  5406. # Test get refresh
  5407. admin.connection.custom_headers = {
  5408. "Authorization": "Bearer bad",
  5409. "Content-Type": "application/json",
  5410. }
  5411. res = await admin.a_get_realm(realm_name=realm)
  5412. assert res["realm"] == realm
  5413. # Freeze time to simulate the access token expiring
  5414. with freezegun.freeze_time("2023-02-25 10:05:00"):
  5415. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:05:00Z")
  5416. assert await admin.a_get_realm(realm_name=realm)
  5417. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:05:00Z")
  5418. # Test bad refresh token, but first make sure access token has expired again
  5419. with freezegun.freeze_time("2023-02-25 10:10:00"):
  5420. admin.connection.custom_headers = {"Content-Type": "application/json"}
  5421. admin.connection.token["refresh_token"] = "bad" # noqa: S105
  5422. with pytest.raises(KeycloakPostError) as err:
  5423. await admin.a_get_realm(realm_name="test-refresh")
  5424. assert err.match(
  5425. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\'',
  5426. )
  5427. admin.connection.get_token()
  5428. # Test post refresh
  5429. with freezegun.freeze_time("2023-02-25 10:15:00"):
  5430. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:15:00Z")
  5431. admin.connection.token = None
  5432. assert await admin.a_create_realm(payload={"realm": "test-refresh"}) == b""
  5433. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:15:00Z")
  5434. # Test update refresh
  5435. with freezegun.freeze_time("2023-02-25 10:25:00"):
  5436. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:25:00Z")
  5437. admin.connection.token = None
  5438. assert (
  5439. await admin.a_update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  5440. == {}
  5441. )
  5442. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:25:00Z")
  5443. # Test delete refresh
  5444. with freezegun.freeze_time("2023-02-25 10:35:00"):
  5445. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25T10:35:00Z")
  5446. admin.connection.token = None
  5447. assert await admin.a_delete_realm(realm_name="test-refresh") == {}
  5448. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25T10:35:00Z")
  5449. @pytest.mark.asyncio
  5450. async def test_a_get_required_actions(admin: KeycloakAdmin, realm: str) -> None:
  5451. """
  5452. Test required actions.
  5453. :param admin: Keycloak Admin client
  5454. :type admin: KeycloakAdmin
  5455. :param realm: Keycloak realm
  5456. :type realm: str
  5457. """
  5458. await admin.a_change_current_realm(realm)
  5459. ractions = await admin.a_get_required_actions()
  5460. assert isinstance(ractions, list)
  5461. for ra in ractions:
  5462. for key in [
  5463. "alias",
  5464. "name",
  5465. "providerId",
  5466. "enabled",
  5467. "defaultAction",
  5468. "priority",
  5469. "config",
  5470. ]:
  5471. assert key in ra
  5472. @pytest.mark.asyncio
  5473. async def test_a_get_required_action_by_alias(admin: KeycloakAdmin, realm: str) -> None:
  5474. """
  5475. Test get required action by alias.
  5476. :param admin: Keycloak Admin client
  5477. :type admin: KeycloakAdmin
  5478. :param realm: Keycloak realm
  5479. :type realm: str
  5480. """
  5481. await admin.a_change_current_realm(realm)
  5482. ractions = await admin.a_get_required_actions()
  5483. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5484. assert ra in ractions
  5485. assert ra["alias"] == "UPDATE_PASSWORD"
  5486. assert await admin.a_get_required_action_by_alias("does-not-exist") is None
  5487. @pytest.mark.asyncio
  5488. async def test_a_update_required_action(admin: KeycloakAdmin, realm: str) -> None:
  5489. """
  5490. Test update required action.
  5491. :param admin: Keycloak Admin client
  5492. :type admin: KeycloakAdmin
  5493. :param realm: Keycloak realm
  5494. :type realm: str
  5495. """
  5496. await admin.a_change_current_realm(realm)
  5497. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5498. old = copy.deepcopy(ra)
  5499. ra["enabled"] = False
  5500. admin.update_required_action("UPDATE_PASSWORD", ra)
  5501. newra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  5502. assert old != newra
  5503. assert newra["enabled"] is False
  5504. @pytest.mark.asyncio
  5505. async def test_a_get_composite_client_roles_of_group(
  5506. admin: KeycloakAdmin,
  5507. realm: str,
  5508. client: str,
  5509. group: str,
  5510. composite_client_role: str,
  5511. ) -> None:
  5512. """
  5513. Test get composite client roles of group.
  5514. :param admin: Keycloak Admin client
  5515. :type admin: KeycloakAdmin
  5516. :param realm: Keycloak realm
  5517. :type realm: str
  5518. :param client: Keycloak client
  5519. :type client: str
  5520. :param group: Keycloak group
  5521. :type group: str
  5522. :param composite_client_role: Composite client role
  5523. :type composite_client_role: str
  5524. """
  5525. await admin.a_change_current_realm(realm)
  5526. role = await admin.a_get_client_role(client, composite_client_role)
  5527. await admin.a_assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  5528. result = await admin.a_get_composite_client_roles_of_group(client, group)
  5529. assert role["id"] in [x["id"] for x in result]
  5530. @pytest.mark.asyncio
  5531. async def test_a_get_role_client_level_children(
  5532. admin: KeycloakAdmin,
  5533. realm: str,
  5534. client: str,
  5535. composite_client_role: str,
  5536. client_role: str,
  5537. ) -> None:
  5538. """
  5539. Test get children of composite client role.
  5540. :param admin: Keycloak Admin client
  5541. :type admin: KeycloakAdmin
  5542. :param realm: Keycloak realm
  5543. :type realm: str
  5544. :param client: Keycloak client
  5545. :type client: str
  5546. :param composite_client_role: Composite client role
  5547. :type composite_client_role: str
  5548. :param client_role: Client role
  5549. :type client_role: str
  5550. """
  5551. await admin.a_change_current_realm(realm)
  5552. child = await admin.a_get_client_role(client, client_role)
  5553. parent = await admin.a_get_client_role(client, composite_client_role)
  5554. res = await admin.a_get_role_client_level_children(client, parent["id"])
  5555. assert child["id"] in [x["id"] for x in res]
  5556. @pytest.mark.asyncio
  5557. async def test_a_upload_certificate(
  5558. admin: KeycloakAdmin,
  5559. realm: str,
  5560. client: str,
  5561. selfsigned_cert: tuple,
  5562. ) -> None:
  5563. """
  5564. Test upload certificate.
  5565. :param admin: Keycloak Admin client
  5566. :type admin: KeycloakAdmin
  5567. :param realm: Keycloak realm
  5568. :type realm: str
  5569. :param client: Keycloak client
  5570. :type client: str
  5571. :param selfsigned_cert: Selfsigned certificates
  5572. :type selfsigned_cert: tuple
  5573. """
  5574. await admin.a_change_current_realm(realm)
  5575. cert, _ = selfsigned_cert
  5576. cert = cert.decode("utf-8").strip()
  5577. admin.upload_certificate(client, cert)
  5578. cl = await admin.a_get_client(client)
  5579. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  5580. @pytest.mark.asyncio
  5581. async def test_a_get_bruteforce_status_for_user(
  5582. admin: KeycloakAdmin,
  5583. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5584. realm: str,
  5585. ) -> None:
  5586. """
  5587. Test users.
  5588. :param admin: Keycloak Admin client
  5589. :type admin: KeycloakAdmin
  5590. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5591. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5592. :param realm: Keycloak realm
  5593. :type realm: str
  5594. """
  5595. oid, username, password = oid_with_credentials
  5596. await admin.a_change_current_realm(realm)
  5597. # Turn on bruteforce protection
  5598. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5599. res = await admin.a_get_realm(realm_name=realm)
  5600. assert res["bruteForceProtected"] is True
  5601. # Test login user with wrong credentials
  5602. with contextlib.suppress(KeycloakAuthenticationError):
  5603. oid.token(username=username, password="wrongpassword") # noqa: S106
  5604. user_id = await admin.a_get_user_id(username)
  5605. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5606. assert bruteforce_status["numFailures"] == 1
  5607. # Cleanup
  5608. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5609. res = await admin.a_get_realm(realm_name=realm)
  5610. assert res["bruteForceProtected"] is False
  5611. @pytest.mark.asyncio
  5612. async def test_a_clear_bruteforce_attempts_for_user(
  5613. admin: KeycloakAdmin,
  5614. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5615. realm: str,
  5616. ) -> None:
  5617. """
  5618. Test users.
  5619. :param admin: Keycloak Admin client
  5620. :type admin: KeycloakAdmin
  5621. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5622. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5623. :param realm: Keycloak realm
  5624. :type realm: str
  5625. """
  5626. oid, username, password = oid_with_credentials
  5627. await admin.a_change_current_realm(realm)
  5628. # Turn on bruteforce protection
  5629. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5630. res = await admin.a_get_realm(realm_name=realm)
  5631. assert res["bruteForceProtected"] is True
  5632. # Test login user with wrong credentials
  5633. with contextlib.suppress(KeycloakAuthenticationError):
  5634. oid.token(username=username, password="wrongpassword") # noqa: S106
  5635. user_id = await admin.a_get_user_id(username)
  5636. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5637. assert bruteforce_status["numFailures"] == 1
  5638. res = await admin.a_clear_bruteforce_attempts_for_user(user_id)
  5639. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5640. assert bruteforce_status["numFailures"] == 0
  5641. # Cleanup
  5642. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5643. res = await admin.a_get_realm(realm_name=realm)
  5644. assert res["bruteForceProtected"] is False
  5645. @pytest.mark.asyncio
  5646. async def test_a_clear_bruteforce_attempts_for_all_users(
  5647. admin: KeycloakAdmin,
  5648. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5649. realm: str,
  5650. ) -> None:
  5651. """
  5652. Test users.
  5653. :param admin: Keycloak Admin client
  5654. :type admin: KeycloakAdmin
  5655. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5656. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5657. :param realm: Keycloak realm
  5658. :type realm: str
  5659. """
  5660. oid, username, password = oid_with_credentials
  5661. await admin.a_change_current_realm(realm)
  5662. # Turn on bruteforce protection
  5663. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5664. res = await admin.a_get_realm(realm_name=realm)
  5665. assert res["bruteForceProtected"] is True
  5666. # Test login user with wrong credentials
  5667. with contextlib.suppress(KeycloakAuthenticationError):
  5668. oid.token(username=username, password="wrongpassword") # noqa: S106
  5669. user_id = await admin.a_get_user_id(username)
  5670. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5671. assert bruteforce_status["numFailures"] == 1
  5672. res = await admin.a_clear_all_bruteforce_attempts()
  5673. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5674. assert bruteforce_status["numFailures"] == 0
  5675. # Cleanup
  5676. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5677. res = await admin.a_get_realm(realm_name=realm)
  5678. assert res["bruteForceProtected"] is False
  5679. @pytest.mark.asyncio
  5680. async def test_a_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  5681. """
  5682. Test that the default realm role is present in a brand new realm.
  5683. :param realm: Realm name
  5684. :type realm: str
  5685. :param admin: Keycloak admin
  5686. :type admin: KeycloakAdmin
  5687. """
  5688. await admin.a_change_current_realm(realm)
  5689. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  5690. assert (
  5691. len(
  5692. [
  5693. x["name"]
  5694. for x in await admin.a_get_realm_roles()
  5695. if x["name"] == f"default-roles-{realm}"
  5696. ],
  5697. )
  5698. == 1
  5699. )
  5700. @pytest.mark.asyncio
  5701. async def test_a_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  5702. """
  5703. Test getter for the ID of the default realm role.
  5704. :param realm: Realm name
  5705. :type realm: str
  5706. :param admin: Keycloak admin
  5707. :type admin: KeycloakAdmin
  5708. """
  5709. await admin.a_change_current_realm(realm)
  5710. assert await admin.a_get_default_realm_role_id() == next(
  5711. x["id"] for x in await admin.a_get_realm_roles() if x["name"] == f"default-roles-{realm}"
  5712. )
  5713. @pytest.mark.asyncio
  5714. async def test_a_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  5715. """
  5716. Test getting, adding and deleting default realm roles.
  5717. :param realm: Realm name
  5718. :type realm: str
  5719. :param admin: Keycloak admin
  5720. :type admin: KeycloakAdmin
  5721. """
  5722. await admin.a_change_current_realm(realm)
  5723. # Test listing all default realm roles
  5724. roles = await admin.a_get_realm_default_roles()
  5725. assert len(roles) == 2
  5726. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  5727. await admin.a_change_current_realm("doesnotexist")
  5728. with pytest.raises(KeycloakGetError) as err:
  5729. await admin.a_get_realm_default_roles()
  5730. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  5731. await admin.a_change_current_realm(realm)
  5732. # Test removing a default realm role
  5733. res = await admin.a_remove_realm_default_roles(payload=[roles[0]])
  5734. assert res == {}
  5735. assert roles[0] not in await admin.a_get_realm_default_roles()
  5736. assert len(await admin.a_get_realm_default_roles()) == 1
  5737. with pytest.raises(KeycloakDeleteError) as err:
  5738. await admin.a_remove_realm_default_roles(payload=[{"id": "bad id"}])
  5739. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5740. # Test adding a default realm role
  5741. res = await admin.a_add_realm_default_roles(payload=[roles[0]])
  5742. assert res == {}
  5743. assert roles[0] in await admin.a_get_realm_default_roles()
  5744. assert len(await admin.a_get_realm_default_roles()) == 2
  5745. with pytest.raises(KeycloakPostError) as err:
  5746. await admin.a_add_realm_default_roles(payload=[{"id": "bad id"}])
  5747. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5748. @pytest.mark.asyncio
  5749. async def test_a_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  5750. """
  5751. Test clearing the keys cache.
  5752. :param realm: Realm name
  5753. :type realm: str
  5754. :param admin: Keycloak admin
  5755. :type admin: KeycloakAdmin
  5756. """
  5757. await admin.a_change_current_realm(realm)
  5758. res = await admin.a_clear_keys_cache()
  5759. assert res == {}
  5760. @pytest.mark.asyncio
  5761. async def test_a_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  5762. """
  5763. Test clearing the realm cache.
  5764. :param realm: Realm name
  5765. :type realm: str
  5766. :param admin: Keycloak admin
  5767. :type admin: KeycloakAdmin
  5768. """
  5769. await admin.a_change_current_realm(realm)
  5770. res = await admin.a_clear_realm_cache()
  5771. assert res == {}
  5772. @pytest.mark.asyncio
  5773. async def test_a_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  5774. """
  5775. Test clearing the user cache.
  5776. :param realm: Realm name
  5777. :type realm: str
  5778. :param admin: Keycloak admin
  5779. :type admin: KeycloakAdmin
  5780. """
  5781. await admin.a_change_current_realm(realm)
  5782. res = await admin.a_clear_user_cache()
  5783. assert res == {}
  5784. @pytest.mark.asyncio
  5785. async def test_a_initial_access_token(
  5786. admin: KeycloakAdmin,
  5787. oid_with_credentials: tuple[KeycloakOpenID, str, str],
  5788. ) -> None:
  5789. """
  5790. Test initial access token and client creation.
  5791. :param admin: Keycloak admin
  5792. :type admin: KeycloakAdmin
  5793. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5794. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5795. """
  5796. res = await admin.a_create_initial_access_token(2, 3)
  5797. assert "token" in res
  5798. assert res["count"] == 2
  5799. assert res["expiration"] == 3
  5800. oid, username, password = oid_with_credentials
  5801. client = str(uuid.uuid4())
  5802. secret = str(uuid.uuid4())
  5803. res = await oid.a_register_client(
  5804. token=res["token"],
  5805. payload={
  5806. "name": "DynamicRegisteredClient",
  5807. "clientId": client,
  5808. "enabled": True,
  5809. "publicClient": False,
  5810. "protocol": "openid-connect",
  5811. "secret": secret,
  5812. "clientAuthenticatorType": "client-secret",
  5813. },
  5814. )
  5815. assert res["clientId"] == client
  5816. new_secret = str(uuid.uuid4())
  5817. res = await oid.a_update_client(
  5818. res["registrationAccessToken"],
  5819. client,
  5820. payload={"secret": new_secret},
  5821. )
  5822. assert res["secret"] == new_secret
  5823. @pytest.mark.asyncio
  5824. async def test_a_refresh_token(admin: KeycloakAdmin) -> None:
  5825. """
  5826. Test refresh token on connection even if it is expired.
  5827. :param admin: Keycloak admin
  5828. :type admin: KeycloakAdmin
  5829. """
  5830. admin.get_realms()
  5831. assert admin.connection.token is not None
  5832. await admin.a_user_logout(await admin.a_get_user_id(admin.connection.username))
  5833. admin.connection.refresh_token()
  5834. def test_counter_part() -> None:
  5835. """Test that each function has its async counter part."""
  5836. admin_methods = [func for func in dir(KeycloakAdmin) if callable(getattr(KeycloakAdmin, func))]
  5837. sync_methods = [
  5838. method
  5839. for method in admin_methods
  5840. if not method.startswith("a_") and not method.startswith("_")
  5841. ]
  5842. async_methods = [
  5843. method for method in admin_methods if iscoroutinefunction(getattr(KeycloakAdmin, method))
  5844. ]
  5845. for method in sync_methods:
  5846. async_method = f"a_{method}"
  5847. assert async_method in admin_methods
  5848. sync_sign = signature(getattr(KeycloakAdmin, method))
  5849. async_sign = signature(getattr(KeycloakAdmin, async_method))
  5850. assert sync_sign.parameters == async_sign.parameters, f"Parameters mismatch for {method}"
  5851. for async_method in async_methods:
  5852. if async_method[2:].startswith("_"):
  5853. continue
  5854. assert async_method[2:] in sync_methods