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.

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