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.

6517 lines
234 KiB

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