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.

6515 lines
234 KiB

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