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.

6523 lines
234 KiB

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