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.

6519 lines
234 KiB

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