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.

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