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.

6341 lines
227 KiB

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