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.

2908 lines
104 KiB

  1. """Test the keycloak admin object."""
  2. import copy
  3. import uuid
  4. from typing import Tuple
  5. import freezegun
  6. import pytest
  7. from dateutil import parser as datetime_parser
  8. import keycloak
  9. from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakOpenIDConnection
  10. from keycloak.connection import ConnectionManager
  11. from keycloak.exceptions import (
  12. KeycloakAuthenticationError,
  13. KeycloakDeleteError,
  14. KeycloakGetError,
  15. KeycloakPostError,
  16. KeycloakPutError,
  17. )
  18. def test_keycloak_version():
  19. """Test version."""
  20. assert keycloak.__version__, keycloak.__version__
  21. def test_keycloak_admin_init(env):
  22. """Test keycloak admin init.
  23. :param env: Environment fixture
  24. :type env: KeycloakTestEnv
  25. """
  26. admin = KeycloakAdmin(
  27. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  28. username=env.KEYCLOAK_ADMIN,
  29. password=env.KEYCLOAK_ADMIN_PASSWORD,
  30. )
  31. assert admin.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", admin.server_url
  32. assert admin.realm_name == "master", admin.realm_name
  33. assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
  34. assert admin.client_id == "admin-cli", admin.client_id
  35. assert admin.client_secret_key is None, admin.client_secret_key
  36. assert admin.verify, admin.verify
  37. assert admin.username == env.KEYCLOAK_ADMIN, admin.username
  38. assert admin.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.password
  39. assert admin.totp is None, admin.totp
  40. assert admin.token is not None, admin.token
  41. assert admin.user_realm_name is None, admin.user_realm_name
  42. assert admin.custom_headers is None, admin.custom_headers
  43. assert admin.token
  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. realm_name=None,
  49. user_realm_name="master",
  50. )
  51. assert admin.token
  52. admin = KeycloakAdmin(
  53. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  54. username=env.KEYCLOAK_ADMIN,
  55. password=env.KEYCLOAK_ADMIN_PASSWORD,
  56. realm_name=None,
  57. user_realm_name=None,
  58. )
  59. assert admin.token
  60. token = admin.token
  61. admin = KeycloakAdmin(
  62. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  63. token=token,
  64. realm_name=None,
  65. user_realm_name=None,
  66. )
  67. assert admin.token == token
  68. admin.create_realm(payload={"realm": "authz", "enabled": True})
  69. admin.realm_name = "authz"
  70. admin.create_client(
  71. payload={
  72. "name": "authz-client",
  73. "clientId": "authz-client",
  74. "authorizationServicesEnabled": True,
  75. "serviceAccountsEnabled": True,
  76. "clientAuthenticatorType": "client-secret",
  77. "directAccessGrantsEnabled": False,
  78. "enabled": True,
  79. "implicitFlowEnabled": False,
  80. "publicClient": False,
  81. }
  82. )
  83. secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client"))
  84. assert KeycloakAdmin(
  85. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  86. user_realm_name="authz",
  87. client_id="authz-client",
  88. client_secret_key=secret["value"],
  89. ).token
  90. admin.delete_realm(realm_name="authz")
  91. assert (
  92. KeycloakAdmin(
  93. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  94. username=None,
  95. password=None,
  96. client_secret_key=None,
  97. custom_headers={"custom": "header"},
  98. ).token
  99. is None
  100. )
  101. keycloak_connection = KeycloakOpenIDConnection(
  102. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  103. username=env.KEYCLOAK_ADMIN,
  104. password=env.KEYCLOAK_ADMIN_PASSWORD,
  105. realm_name="master",
  106. client_id="admin-cli",
  107. verify=True,
  108. )
  109. keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
  110. assert keycloak_admin.token
  111. def test_realms(admin: KeycloakAdmin):
  112. """Test realms.
  113. :param admin: Keycloak Admin client
  114. :type admin: KeycloakAdmin
  115. """
  116. # Get realms
  117. realms = admin.get_realms()
  118. assert len(realms) == 1, realms
  119. assert "master" == realms[0]["realm"]
  120. # Create a test realm
  121. res = admin.create_realm(payload={"realm": "test"})
  122. assert res == b"", res
  123. # Create the same realm, should fail
  124. with pytest.raises(KeycloakPostError) as err:
  125. res = admin.create_realm(payload={"realm": "test"})
  126. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  127. # Create the same realm, skip_exists true
  128. res = admin.create_realm(payload={"realm": "test"}, skip_exists=True)
  129. assert res == {"msg": "Already exists"}, res
  130. # Get a single realm
  131. res = admin.get_realm(realm_name="test")
  132. assert res["realm"] == "test"
  133. # Get non-existing realm
  134. with pytest.raises(KeycloakGetError) as err:
  135. admin.get_realm(realm_name="non-existent")
  136. assert err.match('404: b\'{"error":"Realm not found."}\'')
  137. # Update realm
  138. res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"})
  139. assert res == dict(), res
  140. # Check that the update worked
  141. res = admin.get_realm(realm_name="test")
  142. assert res["realm"] == "test"
  143. assert res["accountTheme"] == "test"
  144. # Update wrong payload
  145. with pytest.raises(KeycloakPutError) as err:
  146. admin.update_realm(realm_name="test", payload={"wrong": "payload"})
  147. assert err.match('400: b\'{"error":"Unrecognized field')
  148. # Check that get realms returns both realms
  149. realms = admin.get_realms()
  150. realm_names = [x["realm"] for x in realms]
  151. assert len(realms) == 2, realms
  152. assert "master" in realm_names, realm_names
  153. assert "test" in realm_names, realm_names
  154. # Delete the realm
  155. res = admin.delete_realm(realm_name="test")
  156. assert res == dict(), res
  157. # Check that the realm does not exist anymore
  158. with pytest.raises(KeycloakGetError) as err:
  159. admin.get_realm(realm_name="test")
  160. assert err.match('404: b\'{"error":"Realm not found."}\'')
  161. # Delete non-existing realm
  162. with pytest.raises(KeycloakDeleteError) as err:
  163. admin.delete_realm(realm_name="non-existent")
  164. assert err.match('404: b\'{"error":"Realm not found."}\'')
  165. def test_changing_of_realms(admin: KeycloakAdmin, realm: str):
  166. """Test changing of realms.
  167. :param admin: Keycloak Admin client
  168. :type admin: KeycloakAdmin
  169. :param realm: Keycloak realm
  170. :type realm: str
  171. """
  172. assert admin.get_current_realm() == "master"
  173. admin.change_current_realm(realm)
  174. assert admin.get_current_realm() == realm
  175. def test_import_export_realms(admin: KeycloakAdmin, realm: str):
  176. """Test import and export of realms.
  177. :param admin: Keycloak Admin client
  178. :type admin: KeycloakAdmin
  179. :param realm: Keycloak realm
  180. :type realm: str
  181. """
  182. admin.change_current_realm(realm)
  183. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True)
  184. assert realm_export != dict(), realm_export
  185. admin.delete_realm(realm_name=realm)
  186. admin.realm_name = "master"
  187. res = admin.import_realm(payload=realm_export)
  188. assert res == b"", res
  189. # Test bad import
  190. with pytest.raises(KeycloakPostError) as err:
  191. admin.import_realm(payload=dict())
  192. assert err.match('500: b\'{"error":"unknown_error"}\'')
  193. def test_partial_import_realm(admin: KeycloakAdmin, realm: str):
  194. """Test partial import of realm configuration.
  195. :param admin: Keycloak Admin client
  196. :type admin: KeycloakAdmin
  197. :param realm: Keycloak realm
  198. :type realm: str
  199. """
  200. test_realm_role = str(uuid.uuid4())
  201. test_user = str(uuid.uuid4())
  202. test_client = str(uuid.uuid4())
  203. admin.change_current_realm(realm)
  204. client_id = admin.create_client(payload={"name": test_client, "clientId": test_client})
  205. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=False)
  206. client_config = [
  207. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  208. ][0]
  209. # delete before partial import
  210. admin.delete_client(client_id)
  211. payload = {
  212. "ifResourceExists": "SKIP",
  213. "id": realm_export["id"],
  214. "realm": realm,
  215. "clients": [client_config],
  216. "roles": {"realm": [{"name": test_realm_role}]},
  217. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  218. }
  219. # check add
  220. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  221. assert res["added"] == 3
  222. # check skip
  223. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  224. assert res["skipped"] == 3
  225. # check overwrite
  226. payload["ifResourceExists"] = "OVERWRITE"
  227. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  228. assert res["overwritten"] == 3
  229. def test_users(admin: KeycloakAdmin, realm: str):
  230. """Test users.
  231. :param admin: Keycloak Admin client
  232. :type admin: KeycloakAdmin
  233. :param realm: Keycloak realm
  234. :type realm: str
  235. """
  236. admin.change_current_realm(realm)
  237. # Check no users present
  238. users = admin.get_users()
  239. assert users == list(), users
  240. # Test create user
  241. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  242. assert user_id is not None, user_id
  243. # Test create the same user
  244. with pytest.raises(KeycloakPostError) as err:
  245. admin.create_user(payload={"username": "test", "email": "test@test.test"})
  246. assert err.match('409: b\'{"errorMessage":"User exists with same username"}\'')
  247. # Test create the same user, exists_ok true
  248. user_id_2 = admin.create_user(
  249. payload={"username": "test", "email": "test@test.test"}, exist_ok=True
  250. )
  251. assert user_id == user_id_2
  252. # Test get user
  253. user = admin.get_user(user_id=user_id)
  254. assert user["username"] == "test", user["username"]
  255. assert user["email"] == "test@test.test", user["email"]
  256. # Test update user
  257. res = admin.update_user(user_id=user_id, payload={"firstName": "Test"})
  258. assert res == dict(), res
  259. user = admin.get_user(user_id=user_id)
  260. assert user["firstName"] == "Test"
  261. # Test update user fail
  262. with pytest.raises(KeycloakPutError) as err:
  263. admin.update_user(user_id=user_id, payload={"wrong": "payload"})
  264. assert err.match('400: b\'{"error":"Unrecognized field')
  265. # Test get users again
  266. users = admin.get_users()
  267. usernames = [x["username"] for x in users]
  268. assert "test" in usernames
  269. # Test users counts
  270. count = admin.users_count()
  271. assert count == 1, count
  272. # Test users count with query
  273. count = admin.users_count(query={"username": "notpresent"})
  274. assert count == 0
  275. # Test user groups
  276. groups = admin.get_user_groups(user_id=user["id"])
  277. assert len(groups) == 0
  278. # Test user groups bad id
  279. with pytest.raises(KeycloakGetError) as err:
  280. admin.get_user_groups(user_id="does-not-exist")
  281. assert err.match('404: b\'{"error":"User not found"}\'')
  282. # Test logout
  283. res = admin.user_logout(user_id=user["id"])
  284. assert res == dict(), res
  285. # Test logout fail
  286. with pytest.raises(KeycloakPostError) as err:
  287. admin.user_logout(user_id="non-existent-id")
  288. assert err.match('404: b\'{"error":"User not found"}\'')
  289. # Test consents
  290. res = admin.user_consents(user_id=user["id"])
  291. assert len(res) == 0, res
  292. # Test consents fail
  293. with pytest.raises(KeycloakGetError) as err:
  294. admin.user_consents(user_id="non-existent-id")
  295. assert err.match('404: b\'{"error":"User not found"}\'')
  296. # Test delete user
  297. res = admin.delete_user(user_id=user_id)
  298. assert res == dict(), res
  299. with pytest.raises(KeycloakGetError) as err:
  300. admin.get_user(user_id=user_id)
  301. err.match('404: b\'{"error":"User not found"}\'')
  302. # Test delete fail
  303. with pytest.raises(KeycloakDeleteError) as err:
  304. admin.delete_user(user_id="non-existent-id")
  305. assert err.match('404: b\'{"error":"User not found"}\'')
  306. def test_users_pagination(admin: KeycloakAdmin, realm: str):
  307. """Test user pagination.
  308. :param admin: Keycloak Admin client
  309. :type admin: KeycloakAdmin
  310. :param realm: Keycloak realm
  311. :type realm: str
  312. """
  313. admin.change_current_realm(realm)
  314. for ind in range(admin.PAGE_SIZE + 50):
  315. username = f"user_{ind}"
  316. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  317. users = admin.get_users()
  318. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  319. users = admin.get_users(query={"first": 100})
  320. assert len(users) == 50, len(users)
  321. users = admin.get_users(query={"max": 20})
  322. assert len(users) == 20, len(users)
  323. def test_user_groups_pagination(admin: KeycloakAdmin, realm: str):
  324. """Test user groups pagination.
  325. :param admin: Keycloak Admin client
  326. :type admin: KeycloakAdmin
  327. :param realm: Keycloak realm
  328. :type realm: str
  329. """
  330. admin.change_current_realm(realm)
  331. user_id = admin.create_user(
  332. payload={"username": "username_1", "email": "username_1@test.test"}
  333. )
  334. for ind in range(admin.PAGE_SIZE + 50):
  335. group_name = f"group_{ind}"
  336. group_id = admin.create_group(payload={"name": group_name})
  337. admin.group_user_add(user_id=user_id, group_id=group_id)
  338. groups = admin.get_user_groups(user_id=user_id)
  339. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  340. groups = admin.get_user_groups(user_id=user_id, query={"first": 100, "max": -1, "search": ""})
  341. assert len(groups) == 50, len(groups)
  342. groups = admin.get_user_groups(user_id=user_id, query={"max": 20, "first": -1, "search": ""})
  343. assert len(groups) == 20, len(groups)
  344. def test_idps(admin: KeycloakAdmin, realm: str):
  345. """Test IDPs.
  346. :param admin: Keycloak Admin client
  347. :type admin: KeycloakAdmin
  348. :param realm: Keycloak realm
  349. :type realm: str
  350. """
  351. admin.change_current_realm(realm)
  352. # Create IDP
  353. res = admin.create_idp(
  354. payload=dict(
  355. providerId="github", alias="github", config=dict(clientId="test", clientSecret="test")
  356. )
  357. )
  358. assert res == b"", res
  359. # Test create idp fail
  360. with pytest.raises(KeycloakPostError) as err:
  361. admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  362. assert err.match("Invalid identity provider id"), err
  363. # Test listing
  364. idps = admin.get_idps()
  365. assert len(idps) == 1
  366. assert "github" == idps[0]["alias"]
  367. # Test get idp
  368. idp = admin.get_idp("github")
  369. assert "github" == idp["alias"]
  370. assert idp.get("config")
  371. assert "test" == idp["config"]["clientId"]
  372. assert "**********" == idp["config"]["clientSecret"]
  373. # Test get idp fail
  374. with pytest.raises(KeycloakGetError) as err:
  375. admin.get_idp("does-not-exist")
  376. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  377. # Test IdP update
  378. res = admin.update_idp(idp_alias="github", payload=idps[0])
  379. assert res == {}, res
  380. # Test adding a mapper
  381. res = admin.add_mapper_to_idp(
  382. idp_alias="github",
  383. payload={
  384. "identityProviderAlias": "github",
  385. "identityProviderMapper": "github-user-attribute-mapper",
  386. "name": "test",
  387. },
  388. )
  389. assert res == b"", res
  390. # Test mapper fail
  391. with pytest.raises(KeycloakPostError) as err:
  392. admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
  393. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  394. # Test IdP mappers listing
  395. idp_mappers = admin.get_idp_mappers(idp_alias="github")
  396. assert len(idp_mappers) == 1
  397. # Test IdP mapper update
  398. res = admin.update_mapper_in_idp(
  399. idp_alias="github",
  400. mapper_id=idp_mappers[0]["id"],
  401. # For an obscure reason, keycloak expect all fields
  402. payload={
  403. "id": idp_mappers[0]["id"],
  404. "identityProviderAlias": "github-alias",
  405. "identityProviderMapper": "github-user-attribute-mapper",
  406. "name": "test",
  407. "config": idp_mappers[0]["config"],
  408. },
  409. )
  410. assert res == dict(), res
  411. # Test delete
  412. res = admin.delete_idp(idp_alias="github")
  413. assert res == dict(), res
  414. # Test delete fail
  415. with pytest.raises(KeycloakDeleteError) as err:
  416. admin.delete_idp(idp_alias="does-not-exist")
  417. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  418. def test_user_credentials(admin: KeycloakAdmin, user: str):
  419. """Test user credentials.
  420. :param admin: Keycloak Admin client
  421. :type admin: KeycloakAdmin
  422. :param user: Keycloak user
  423. :type user: str
  424. """
  425. res = admin.set_user_password(user_id=user, password="booya", temporary=True)
  426. assert res == dict(), res
  427. # Test user password set fail
  428. with pytest.raises(KeycloakPutError) as err:
  429. admin.set_user_password(user_id="does-not-exist", password="")
  430. assert err.match('404: b\'{"error":"User not found"}\'')
  431. credentials = admin.get_credentials(user_id=user)
  432. assert len(credentials) == 1
  433. assert credentials[0]["type"] == "password", credentials
  434. # Test get credentials fail
  435. with pytest.raises(KeycloakGetError) as err:
  436. admin.get_credentials(user_id="does-not-exist")
  437. assert err.match('404: b\'{"error":"User not found"}\'')
  438. res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"])
  439. assert res == dict(), res
  440. # Test delete fail
  441. with pytest.raises(KeycloakDeleteError) as err:
  442. admin.delete_credential(user_id=user, credential_id="does-not-exist")
  443. assert err.match('404: b\'{"error":"Credential not found"}\'')
  444. def test_social_logins(admin: KeycloakAdmin, user: str):
  445. """Test social logins.
  446. :param admin: Keycloak Admin client
  447. :type admin: KeycloakAdmin
  448. :param user: Keycloak user
  449. :type user: str
  450. """
  451. res = admin.add_user_social_login(
  452. user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
  453. )
  454. assert res == dict(), res
  455. admin.add_user_social_login(
  456. user_id=user, provider_id="github", provider_userid="test", provider_username="test"
  457. )
  458. assert res == dict(), res
  459. # Test add social login fail
  460. with pytest.raises(KeycloakPostError) as err:
  461. admin.add_user_social_login(
  462. user_id="does-not-exist",
  463. provider_id="does-not-exist",
  464. provider_userid="test",
  465. provider_username="test",
  466. )
  467. assert err.match('404: b\'{"error":"User not found"}\'')
  468. res = admin.get_user_social_logins(user_id=user)
  469. assert res == list(), res
  470. # Test get social logins fail
  471. with pytest.raises(KeycloakGetError) as err:
  472. admin.get_user_social_logins(user_id="does-not-exist")
  473. assert err.match('404: b\'{"error":"User not found"}\'')
  474. res = admin.delete_user_social_login(user_id=user, provider_id="gitlab")
  475. assert res == {}, res
  476. res = admin.delete_user_social_login(user_id=user, provider_id="github")
  477. assert res == {}, res
  478. with pytest.raises(KeycloakDeleteError) as err:
  479. admin.delete_user_social_login(user_id=user, provider_id="instagram")
  480. assert err.match('404: b\'{"error":"Link not found"}\''), err
  481. def test_server_info(admin: KeycloakAdmin):
  482. """Test server info.
  483. :param admin: Keycloak Admin client
  484. :type admin: KeycloakAdmin
  485. """
  486. info = admin.get_server_info()
  487. assert set(info.keys()).issubset(
  488. {
  489. "systemInfo",
  490. "memoryInfo",
  491. "profileInfo",
  492. "features",
  493. "themes",
  494. "socialProviders",
  495. "identityProviders",
  496. "providers",
  497. "protocolMapperTypes",
  498. "builtinProtocolMappers",
  499. "clientInstallations",
  500. "componentTypes",
  501. "passwordPolicies",
  502. "enums",
  503. "cryptoInfo",
  504. "features",
  505. }
  506. ), info.keys()
  507. def test_groups(admin: KeycloakAdmin, user: str):
  508. """Test groups.
  509. :param admin: Keycloak Admin client
  510. :type admin: KeycloakAdmin
  511. :param user: Keycloak user
  512. :type user: str
  513. """
  514. # Test get groups
  515. groups = admin.get_groups()
  516. assert len(groups) == 0
  517. # Test create group
  518. group_id = admin.create_group(payload={"name": "main-group"})
  519. assert group_id is not None, group_id
  520. # Test create subgroups
  521. subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  522. subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id)
  523. # Test create group fail
  524. with pytest.raises(KeycloakPostError) as err:
  525. admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  526. assert err.match("409"), err
  527. # Test skip exists OK
  528. subgroup_id_1_eq = admin.create_group(
  529. payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
  530. )
  531. assert subgroup_id_1_eq is None
  532. # Test get groups again
  533. groups = admin.get_groups()
  534. assert len(groups) == 1, groups
  535. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  536. assert groups[0]["id"] == group_id
  537. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  538. # Test get groups query
  539. groups = admin.get_groups(query={"max": 10})
  540. assert len(groups) == 1, groups
  541. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  542. assert groups[0]["id"] == group_id
  543. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  544. # Test get group
  545. res = admin.get_group(group_id=subgroup_id_1)
  546. assert res["id"] == subgroup_id_1, res
  547. assert res["name"] == "subgroup-1"
  548. assert res["path"] == "/main-group/subgroup-1"
  549. # Test get group fail
  550. with pytest.raises(KeycloakGetError) as err:
  551. admin.get_group(group_id="does-not-exist")
  552. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  553. # Create 1 more subgroup
  554. subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2)
  555. main_group = admin.get_group(group_id=group_id)
  556. # Test nested searches
  557. subgroup_2 = admin.get_group(group_id=subgroup_id_2)
  558. res = admin.get_subgroups(group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1")
  559. assert res is not None, res
  560. assert res["id"] == subsubgroup_id_1
  561. # Test empty search
  562. res = admin.get_subgroups(group=main_group, path="/none")
  563. assert res is None, res
  564. # Test get group by path
  565. res = admin.get_group_by_path(path="/main-group/subgroup-1")
  566. assert res is not None, res
  567. assert res["id"] == subgroup_id_1, res
  568. with pytest.raises(KeycloakGetError) as err:
  569. admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  570. assert err.match('404: b\'{"error":"Group path does not exist"}\'')
  571. res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  572. assert res is not None, res
  573. assert res["id"] == subsubgroup_id_1
  574. res = admin.get_group_by_path(path="/main-group")
  575. assert res is not None, res
  576. assert res["id"] == group_id, res
  577. # Test group members
  578. res = admin.get_group_members(group_id=subgroup_id_2)
  579. assert len(res) == 0, res
  580. # Test fail group members
  581. with pytest.raises(KeycloakGetError) as err:
  582. admin.get_group_members(group_id="does-not-exist")
  583. assert err.match('404: b\'{"error":"Could not find group by id"}\'')
  584. res = admin.group_user_add(user_id=user, group_id=subgroup_id_2)
  585. assert res == dict(), res
  586. res = admin.get_group_members(group_id=subgroup_id_2)
  587. assert len(res) == 1, res
  588. assert res[0]["id"] == user
  589. # Test get group members query
  590. res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10})
  591. assert len(res) == 1, res
  592. assert res[0]["id"] == user
  593. with pytest.raises(KeycloakDeleteError) as err:
  594. admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  595. assert err.match('404: b\'{"error":"User not found"}\''), err
  596. res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2)
  597. assert res == dict(), res
  598. # Test set permissions
  599. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True)
  600. assert res["enabled"], res
  601. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False)
  602. assert not res["enabled"], res
  603. with pytest.raises(KeycloakPutError) as err:
  604. admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  605. assert err.match('b\'{"error":"unknown_error"}\''), err
  606. # Test update group
  607. res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  608. assert res == dict(), res
  609. assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2"
  610. # test update fail
  611. with pytest.raises(KeycloakPutError) as err:
  612. admin.update_group(group_id="does-not-exist", payload=dict())
  613. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  614. # Test delete
  615. res = admin.delete_group(group_id=group_id)
  616. assert res == dict(), res
  617. assert len(admin.get_groups()) == 0
  618. # Test delete fail
  619. with pytest.raises(KeycloakDeleteError) as err:
  620. admin.delete_group(group_id="does-not-exist")
  621. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  622. def test_clients(admin: KeycloakAdmin, realm: str):
  623. """Test clients.
  624. :param admin: Keycloak Admin client
  625. :type admin: KeycloakAdmin
  626. :param realm: Keycloak realm
  627. :type realm: str
  628. """
  629. admin.change_current_realm(realm)
  630. # Test get clients
  631. clients = admin.get_clients()
  632. assert len(clients) == 6, clients
  633. assert {x["name"] for x in clients} == set(
  634. [
  635. "${client_admin-cli}",
  636. "${client_security-admin-console}",
  637. "${client_account-console}",
  638. "${client_broker}",
  639. "${client_account}",
  640. "${client_realm-management}",
  641. ]
  642. ), clients
  643. # Test create client
  644. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  645. assert client_id, client_id
  646. with pytest.raises(KeycloakPostError) as err:
  647. admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  648. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  649. client_id_2 = admin.create_client(
  650. payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True
  651. )
  652. assert client_id == client_id_2, client_id_2
  653. # Test get client
  654. res = admin.get_client(client_id=client_id)
  655. assert res["clientId"] == "test-client", res
  656. assert res["name"] == "test-client", res
  657. assert res["id"] == client_id, res
  658. with pytest.raises(KeycloakGetError) as err:
  659. admin.get_client(client_id="does-not-exist")
  660. assert err.match('404: b\'{"error":"Could not find client"}\'')
  661. assert len(admin.get_clients()) == 7
  662. # Test get client id
  663. assert admin.get_client_id(client_id="test-client") == client_id
  664. assert admin.get_client_id(client_id="does-not-exist") is None
  665. # Test update client
  666. res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"})
  667. assert res == dict(), res
  668. with pytest.raises(KeycloakPutError) as err:
  669. admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"})
  670. assert err.match('404: b\'{"error":"Could not find client"}\'')
  671. # Test client mappers
  672. res = admin.get_mappers_from_client(client_id=client_id)
  673. assert len(res) == 0
  674. with pytest.raises(KeycloakPostError) as err:
  675. admin.add_mapper_to_client(client_id="does-not-exist", payload=dict())
  676. assert err.match('404: b\'{"error":"Could not find client"}\'')
  677. res = admin.add_mapper_to_client(
  678. client_id=client_id,
  679. payload={
  680. "name": "test-mapper",
  681. "protocol": "openid-connect",
  682. "protocolMapper": "oidc-usermodel-attribute-mapper",
  683. },
  684. )
  685. assert res == b""
  686. assert len(admin.get_mappers_from_client(client_id=client_id)) == 1
  687. mapper = admin.get_mappers_from_client(client_id=client_id)[0]
  688. with pytest.raises(KeycloakPutError) as err:
  689. admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload=dict())
  690. assert err.match('404: b\'{"error":"Model not found"}\'')
  691. mapper["config"]["user.attribute"] = "test"
  692. res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper)
  693. assert res == dict()
  694. res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  695. assert res == dict()
  696. with pytest.raises(KeycloakDeleteError) as err:
  697. admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  698. assert err.match('404: b\'{"error":"Model not found"}\'')
  699. # Test client sessions
  700. with pytest.raises(KeycloakGetError) as err:
  701. admin.get_client_all_sessions(client_id="does-not-exist")
  702. assert err.match('404: b\'{"error":"Could not find client"}\'')
  703. assert admin.get_client_all_sessions(client_id=client_id) == list()
  704. assert admin.get_client_sessions_stats() == list()
  705. # Test authz
  706. auth_client_id = admin.create_client(
  707. payload={
  708. "name": "authz-client",
  709. "clientId": "authz-client",
  710. "authorizationServicesEnabled": True,
  711. "serviceAccountsEnabled": True,
  712. }
  713. )
  714. res = admin.get_client_authz_settings(client_id=auth_client_id)
  715. assert res["allowRemoteResourceManagement"]
  716. assert res["decisionStrategy"] == "UNANIMOUS"
  717. assert len(res["policies"]) >= 0
  718. with pytest.raises(KeycloakGetError) as err:
  719. admin.get_client_authz_settings(client_id=client_id)
  720. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  721. # Authz resources
  722. res = admin.get_client_authz_resources(client_id=auth_client_id)
  723. assert len(res) == 1
  724. assert res[0]["name"] == "Default Resource"
  725. with pytest.raises(KeycloakGetError) as err:
  726. admin.get_client_authz_resources(client_id=client_id)
  727. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  728. res = admin.create_client_authz_resource(
  729. client_id=auth_client_id, payload={"name": "test-resource"}
  730. )
  731. assert res["name"] == "test-resource", res
  732. test_resource_id = res["_id"]
  733. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=test_resource_id)
  734. assert res["_id"] == test_resource_id, res
  735. assert res["name"] == "test-resource", res
  736. with pytest.raises(KeycloakPostError) as err:
  737. admin.create_client_authz_resource(
  738. client_id=auth_client_id, payload={"name": "test-resource"}
  739. )
  740. assert err.match('409: b\'{"error":"invalid_request"')
  741. assert admin.create_client_authz_resource(
  742. client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
  743. ) == {"msg": "Already exists"}
  744. res = admin.get_client_authz_resources(client_id=auth_client_id)
  745. assert len(res) == 2
  746. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  747. res = admin.create_client_authz_resource(
  748. client_id=auth_client_id, payload={"name": "temp-resource"}
  749. )
  750. assert res["name"] == "temp-resource", res
  751. temp_resource_id: str = res["_id"]
  752. # Test update authz resources
  753. admin.update_client_authz_resource(
  754. client_id=auth_client_id,
  755. resource_id=temp_resource_id,
  756. payload={"name": "temp-updated-resource"},
  757. )
  758. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  759. assert res["name"] == "temp-updated-resource", res
  760. with pytest.raises(KeycloakPutError) as err:
  761. admin.update_client_authz_resource(
  762. client_id=auth_client_id,
  763. resource_id="invalid_resource_id",
  764. payload={"name": "temp-updated-resource"},
  765. )
  766. assert err.match("404: b''"), err
  767. admin.delete_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  768. with pytest.raises(KeycloakGetError) as err:
  769. admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  770. assert err.match("404: b''")
  771. # Authz policies
  772. res = admin.get_client_authz_policies(client_id=auth_client_id)
  773. assert len(res) == 1, res
  774. assert res[0]["name"] == "Default Policy"
  775. with pytest.raises(KeycloakGetError) as err:
  776. admin.get_client_authz_policies(client_id="does-not-exist")
  777. assert err.match('404: b\'{"error":"Could not find client"}\'')
  778. role_id = admin.get_realm_role(role_name="offline_access")["id"]
  779. res = admin.create_client_authz_role_based_policy(
  780. client_id=auth_client_id,
  781. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  782. )
  783. assert res["name"] == "test-authz-rb-policy", res
  784. with pytest.raises(KeycloakPostError) as err:
  785. admin.create_client_authz_role_based_policy(
  786. client_id=auth_client_id,
  787. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  788. )
  789. assert err.match('409: b\'{"error":"Policy with name')
  790. assert admin.create_client_authz_role_based_policy(
  791. client_id=auth_client_id,
  792. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  793. skip_exists=True,
  794. ) == {"msg": "Already exists"}
  795. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2
  796. res = admin.create_client_authz_role_based_policy(
  797. client_id=auth_client_id,
  798. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  799. )
  800. res2 = admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  801. assert res["id"] == res2["id"]
  802. admin.delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  803. with pytest.raises(KeycloakGetError) as err:
  804. admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  805. assert err.match("404: b''")
  806. res = admin.create_client_authz_policy(
  807. client_id=auth_client_id,
  808. payload={
  809. "name": "test-authz-policy",
  810. "type": "time",
  811. "config": {"hourEnd": "18", "hour": "9"},
  812. },
  813. )
  814. assert res["name"] == "test-authz-policy", res
  815. with pytest.raises(KeycloakPostError) as err:
  816. admin.create_client_authz_policy(
  817. client_id=auth_client_id,
  818. payload={
  819. "name": "test-authz-policy",
  820. "type": "time",
  821. "config": {"hourEnd": "18", "hour": "9"},
  822. },
  823. )
  824. assert err.match('409: b\'{"error":"Policy with name')
  825. assert admin.create_client_authz_policy(
  826. client_id=auth_client_id,
  827. payload={
  828. "name": "test-authz-policy",
  829. "type": "time",
  830. "config": {"hourEnd": "18", "hour": "9"},
  831. },
  832. skip_exists=True,
  833. ) == {"msg": "Already exists"}
  834. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 3
  835. # Test authz permissions
  836. res = admin.get_client_authz_permissions(client_id=auth_client_id)
  837. assert len(res) == 1, res
  838. assert res[0]["name"] == "Default Permission"
  839. with pytest.raises(KeycloakGetError) as err:
  840. admin.get_client_authz_permissions(client_id="does-not-exist")
  841. assert err.match('404: b\'{"error":"Could not find client"}\'')
  842. res = admin.create_client_authz_resource_based_permission(
  843. client_id=auth_client_id,
  844. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  845. )
  846. assert res, res
  847. assert res["name"] == "test-permission-rb"
  848. assert res["resources"] == [test_resource_id]
  849. with pytest.raises(KeycloakPostError) as err:
  850. admin.create_client_authz_resource_based_permission(
  851. client_id=auth_client_id,
  852. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  853. )
  854. assert err.match('409: b\'{"error":"Policy with name')
  855. assert admin.create_client_authz_resource_based_permission(
  856. client_id=auth_client_id,
  857. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  858. skip_exists=True,
  859. ) == {"msg": "Already exists"}
  860. assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2
  861. # Test authz scopes
  862. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  863. assert len(res) == 0, res
  864. with pytest.raises(KeycloakGetError) as err:
  865. admin.get_client_authz_scopes(client_id=client_id)
  866. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  867. res = admin.create_client_authz_scopes(
  868. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  869. )
  870. assert res["name"] == "test-authz-scope", res
  871. with pytest.raises(KeycloakPostError) as err:
  872. admin.create_client_authz_scopes(
  873. client_id="invalid_client_id", payload={"name": "test-authz-scope"}
  874. )
  875. assert err.match('404: b\'{"error":"Could not find client"')
  876. assert admin.create_client_authz_scopes(
  877. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  878. )
  879. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  880. assert len(res) == 1
  881. assert {x["name"] for x in res} == {"test-authz-scope"}
  882. # Test service account user
  883. res = admin.get_client_service_account_user(client_id=auth_client_id)
  884. assert res["username"] == "service-account-authz-client", res
  885. with pytest.raises(KeycloakGetError) as err:
  886. admin.get_client_service_account_user(client_id=client_id)
  887. assert err.match('400: b\'{"error":"unknown_error"}\'')
  888. # Test delete client
  889. res = admin.delete_client(client_id=auth_client_id)
  890. assert res == dict(), res
  891. with pytest.raises(KeycloakDeleteError) as err:
  892. admin.delete_client(client_id=auth_client_id)
  893. assert err.match('404: b\'{"error":"Could not find client"}\'')
  894. # Test client credentials
  895. admin.create_client(
  896. payload={
  897. "name": "test-confidential",
  898. "enabled": True,
  899. "protocol": "openid-connect",
  900. "publicClient": False,
  901. "redirectUris": ["http://localhost/*"],
  902. "webOrigins": ["+"],
  903. "clientId": "test-confidential",
  904. "secret": "test-secret",
  905. "clientAuthenticatorType": "client-secret",
  906. }
  907. )
  908. with pytest.raises(KeycloakGetError) as err:
  909. admin.get_client_secrets(client_id="does-not-exist")
  910. assert err.match('404: b\'{"error":"Could not find client"}\'')
  911. secrets = admin.get_client_secrets(
  912. client_id=admin.get_client_id(client_id="test-confidential")
  913. )
  914. assert secrets == {"type": "secret", "value": "test-secret"}
  915. with pytest.raises(KeycloakPostError) as err:
  916. admin.generate_client_secrets(client_id="does-not-exist")
  917. assert err.match('404: b\'{"error":"Could not find client"}\'')
  918. res = admin.generate_client_secrets(
  919. client_id=admin.get_client_id(client_id="test-confidential")
  920. )
  921. assert res
  922. assert (
  923. admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
  924. == res
  925. )
  926. def test_realm_roles(admin: KeycloakAdmin, realm: str):
  927. """Test realm roles.
  928. :param admin: Keycloak Admin client
  929. :type admin: KeycloakAdmin
  930. :param realm: Keycloak realm
  931. :type realm: str
  932. """
  933. admin.change_current_realm(realm)
  934. # Test get realm roles
  935. roles = admin.get_realm_roles()
  936. assert len(roles) == 3, roles
  937. role_names = [x["name"] for x in roles]
  938. assert "uma_authorization" in role_names, role_names
  939. assert "offline_access" in role_names, role_names
  940. # Test get realm roles with search text
  941. searched_roles = admin.get_realm_roles(search_text="uma_a")
  942. searched_role_names = [x["name"] for x in searched_roles]
  943. assert "uma_authorization" in searched_role_names, searched_role_names
  944. assert "offline_access" not in searched_role_names, searched_role_names
  945. # Test empty members
  946. with pytest.raises(KeycloakGetError) as err:
  947. admin.get_realm_role_members(role_name="does-not-exist")
  948. assert err.match('404: b\'{"error":"Could not find role"}\'')
  949. members = admin.get_realm_role_members(role_name="offline_access")
  950. assert members == list(), members
  951. # Test create realm role
  952. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  953. assert role_id, role_id
  954. with pytest.raises(KeycloakPostError) as err:
  955. admin.create_realm_role(payload={"name": "test-realm-role"})
  956. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  957. role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  958. assert role_id == role_id_2
  959. # Test get realm role by its id
  960. role_id = admin.get_realm_role(role_name="test-realm-role")["id"]
  961. res = admin.get_realm_role_by_id(role_id)
  962. assert res["name"] == "test-realm-role"
  963. # Test update realm role
  964. res = admin.update_realm_role(
  965. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  966. )
  967. assert res == dict(), res
  968. with pytest.raises(KeycloakPutError) as err:
  969. admin.update_realm_role(
  970. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  971. )
  972. assert err.match('404: b\'{"error":"Could not find role"}\''), err
  973. # Test realm role user assignment
  974. user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"})
  975. with pytest.raises(KeycloakPostError) as err:
  976. admin.assign_realm_roles(user_id=user_id, roles=["bad"])
  977. assert err.match('b\'{"error":"unknown_error"}\''), err
  978. res = admin.assign_realm_roles(
  979. user_id=user_id,
  980. roles=[
  981. admin.get_realm_role(role_name="offline_access"),
  982. admin.get_realm_role(role_name="test-realm-role-update"),
  983. ],
  984. )
  985. assert res == dict(), res
  986. assert admin.get_user(user_id=user_id)["username"] in [
  987. x["username"] for x in admin.get_realm_role_members(role_name="offline_access")
  988. ]
  989. assert admin.get_user(user_id=user_id)["username"] in [
  990. x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update")
  991. ]
  992. roles = admin.get_realm_roles_of_user(user_id=user_id)
  993. assert len(roles) == 3
  994. assert "offline_access" in [x["name"] for x in roles]
  995. assert "test-realm-role-update" in [x["name"] for x in roles]
  996. with pytest.raises(KeycloakDeleteError) as err:
  997. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  998. assert err.match('b\'{"error":"unknown_error"}\''), err
  999. res = admin.delete_realm_roles_of_user(
  1000. user_id=user_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1001. )
  1002. assert res == dict(), res
  1003. assert admin.get_realm_role_members(role_name="offline_access") == list()
  1004. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1005. assert len(roles) == 2
  1006. assert "offline_access" not in [x["name"] for x in roles]
  1007. assert "test-realm-role-update" in [x["name"] for x in roles]
  1008. roles = admin.get_available_realm_roles_of_user(user_id=user_id)
  1009. assert len(roles) == 2
  1010. assert "offline_access" in [x["name"] for x in roles]
  1011. assert "uma_authorization" in [x["name"] for x in roles]
  1012. # Test realm role group assignment
  1013. group_id = admin.create_group(payload={"name": "test-group"})
  1014. with pytest.raises(KeycloakPostError) as err:
  1015. admin.assign_group_realm_roles(group_id=group_id, roles=["bad"])
  1016. assert err.match('b\'{"error":"unknown_error"}\''), err
  1017. res = admin.assign_group_realm_roles(
  1018. group_id=group_id,
  1019. roles=[
  1020. admin.get_realm_role(role_name="offline_access"),
  1021. admin.get_realm_role(role_name="test-realm-role-update"),
  1022. ],
  1023. )
  1024. assert res == dict(), res
  1025. roles = admin.get_group_realm_roles(group_id=group_id)
  1026. assert len(roles) == 2
  1027. assert "offline_access" in [x["name"] for x in roles]
  1028. assert "test-realm-role-update" in [x["name"] for x in roles]
  1029. with pytest.raises(KeycloakDeleteError) as err:
  1030. admin.delete_group_realm_roles(group_id=group_id, roles=["bad"])
  1031. assert err.match('b\'{"error":"unknown_error"}\''), err
  1032. res = admin.delete_group_realm_roles(
  1033. group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1034. )
  1035. assert res == dict(), res
  1036. roles = admin.get_group_realm_roles(group_id=group_id)
  1037. assert len(roles) == 1
  1038. assert "test-realm-role-update" in [x["name"] for x in roles]
  1039. # Test composite realm roles
  1040. composite_role = admin.create_realm_role(payload={"name": "test-composite-role"})
  1041. with pytest.raises(KeycloakPostError) as err:
  1042. admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1043. assert err.match('b\'{"error":"unknown_error"}\''), err
  1044. res = admin.add_composite_realm_roles_to_role(
  1045. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1046. )
  1047. assert res == dict(), res
  1048. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1049. assert len(res) == 1
  1050. assert "test-realm-role-update" in res[0]["name"]
  1051. with pytest.raises(KeycloakGetError) as err:
  1052. admin.get_composite_realm_roles_of_role(role_name="bad")
  1053. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1054. res = admin.get_composite_realm_roles_of_user(user_id=user_id)
  1055. assert len(res) == 4
  1056. assert "offline_access" in {x["name"] for x in res}
  1057. assert "test-realm-role-update" in {x["name"] for x in res}
  1058. assert "uma_authorization" in {x["name"] for x in res}
  1059. with pytest.raises(KeycloakGetError) as err:
  1060. admin.get_composite_realm_roles_of_user(user_id="bad")
  1061. assert err.match('b\'{"error":"User not found"}\''), err
  1062. with pytest.raises(KeycloakDeleteError) as err:
  1063. admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1064. assert err.match('b\'{"error":"unknown_error"}\''), err
  1065. res = admin.remove_composite_realm_roles_to_role(
  1066. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1067. )
  1068. assert res == dict(), res
  1069. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1070. assert len(res) == 0
  1071. # Test realm role group list
  1072. res = admin.get_realm_role_groups(role_name="test-realm-role-update")
  1073. assert len(res) == 1
  1074. assert res[0]["id"] == group_id
  1075. with pytest.raises(KeycloakGetError) as err:
  1076. admin.get_realm_role_groups(role_name="non-existent-role")
  1077. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1078. # Test delete realm role
  1079. res = admin.delete_realm_role(role_name=composite_role)
  1080. assert res == dict(), res
  1081. with pytest.raises(KeycloakDeleteError) as err:
  1082. admin.delete_realm_role(role_name=composite_role)
  1083. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1084. @pytest.mark.parametrize(
  1085. "testcase, arg_brief_repr, includes_attributes",
  1086. [
  1087. ("brief True", {"brief_representation": True}, False),
  1088. ("brief False", {"brief_representation": False}, True),
  1089. ("default", {}, False),
  1090. ],
  1091. )
  1092. def test_role_attributes(
  1093. admin: KeycloakAdmin,
  1094. realm: str,
  1095. client: str,
  1096. arg_brief_repr: dict,
  1097. includes_attributes: bool,
  1098. testcase: str,
  1099. ):
  1100. """Test getting role attributes for bulk calls.
  1101. :param admin: Keycloak admin
  1102. :type admin: KeycloakAdmin
  1103. :param realm: Keycloak realm
  1104. :type realm: str
  1105. :param client: Keycloak client
  1106. :type client: str
  1107. :param arg_brief_repr: Brief representation
  1108. :type arg_brief_repr: dict
  1109. :param includes_attributes: Indicator whether to include attributes
  1110. :type includes_attributes: bool
  1111. :param testcase: Test case
  1112. :type testcase: str
  1113. """
  1114. # setup
  1115. attribute_role = "test-realm-role-w-attr"
  1116. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  1117. role_id = admin.create_realm_role(
  1118. payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1119. )
  1120. assert role_id, role_id
  1121. cli_role_id = admin.create_client_role(
  1122. client, payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1123. )
  1124. assert cli_role_id, cli_role_id
  1125. if not includes_attributes:
  1126. test_attrs = None
  1127. # tests
  1128. roles = admin.get_realm_roles(**arg_brief_repr)
  1129. roles_filtered = [role for role in roles if role["name"] == role_id]
  1130. assert roles_filtered, roles_filtered
  1131. role = roles_filtered[0]
  1132. assert role.get("attributes") == test_attrs, testcase
  1133. roles = admin.get_client_roles(client, **arg_brief_repr)
  1134. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  1135. assert roles_filtered, roles_filtered
  1136. role = roles_filtered[0]
  1137. assert role.get("attributes") == test_attrs, testcase
  1138. # cleanup
  1139. res = admin.delete_realm_role(role_name=attribute_role)
  1140. assert res == dict(), res
  1141. res = admin.delete_client_role(client, role_name=attribute_role)
  1142. assert res == dict(), res
  1143. def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
  1144. """Test client realm roles.
  1145. :param admin: Keycloak admin
  1146. :type admin: KeycloakAdmin
  1147. :param realm: Keycloak realm
  1148. :type realm: str
  1149. """
  1150. admin.change_current_realm(realm)
  1151. # Test get realm roles
  1152. roles = admin.get_realm_roles()
  1153. assert len(roles) == 3, roles
  1154. role_names = [x["name"] for x in roles]
  1155. assert "uma_authorization" in role_names, role_names
  1156. assert "offline_access" in role_names, role_names
  1157. # create realm role for test
  1158. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1159. assert role_id, role_id
  1160. # Test realm role client assignment
  1161. client_id = admin.create_client(
  1162. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1163. )
  1164. with pytest.raises(KeycloakPostError) as err:
  1165. admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  1166. assert err.match('b\'{"error":"unknown_error"}\''), err
  1167. res = admin.assign_realm_roles_to_client_scope(
  1168. client_id=client_id,
  1169. roles=[
  1170. admin.get_realm_role(role_name="offline_access"),
  1171. admin.get_realm_role(role_name="test-realm-role"),
  1172. ],
  1173. )
  1174. assert res == dict(), res
  1175. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1176. assert len(roles) == 2
  1177. client_role_names = [x["name"] for x in roles]
  1178. assert "offline_access" in client_role_names, client_role_names
  1179. assert "test-realm-role" in client_role_names, client_role_names
  1180. assert "uma_authorization" not in client_role_names, client_role_names
  1181. # Test remove realm role of client
  1182. with pytest.raises(KeycloakDeleteError) as err:
  1183. admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  1184. assert err.match('b\'{"error":"unknown_error"}\''), err
  1185. res = admin.delete_realm_roles_of_client_scope(
  1186. client_id=client_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1187. )
  1188. assert res == dict(), res
  1189. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1190. assert len(roles) == 1
  1191. assert "test-realm-role" in [x["name"] for x in roles]
  1192. res = admin.delete_realm_roles_of_client_scope(
  1193. client_id=client_id, roles=[admin.get_realm_role(role_name="test-realm-role")]
  1194. )
  1195. assert res == dict(), res
  1196. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1197. assert len(roles) == 0
  1198. def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
  1199. """Test client assignment of other client roles.
  1200. :param admin: Keycloak admin
  1201. :type admin: KeycloakAdmin
  1202. :param realm: Keycloak realm
  1203. :type realm: str
  1204. :param client: Keycloak client
  1205. :type client: str
  1206. """
  1207. admin.change_current_realm(realm)
  1208. client_id = admin.create_client(
  1209. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1210. )
  1211. # Test get client roles
  1212. roles = admin.get_client_roles_of_client_scope(client_id, client)
  1213. assert len(roles) == 0, roles
  1214. # create client role for test
  1215. client_role_id = admin.create_client_role(
  1216. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1217. )
  1218. assert client_role_id, client_role_id
  1219. # Test client role assignment to other client
  1220. with pytest.raises(KeycloakPostError) as err:
  1221. admin.assign_client_roles_to_client_scope(
  1222. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1223. )
  1224. assert err.match('b\'{"error":"unknown_error"}\''), err
  1225. res = admin.assign_client_roles_to_client_scope(
  1226. client_id=client_id,
  1227. client_roles_owner_id=client,
  1228. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1229. )
  1230. assert res == dict(), res
  1231. roles = admin.get_client_roles_of_client_scope(
  1232. client_id=client_id, client_roles_owner_id=client
  1233. )
  1234. assert len(roles) == 1
  1235. client_role_names = [x["name"] for x in roles]
  1236. assert "client-role-test" in client_role_names, client_role_names
  1237. # Test remove realm role of client
  1238. with pytest.raises(KeycloakDeleteError) as err:
  1239. admin.delete_client_roles_of_client_scope(
  1240. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1241. )
  1242. assert err.match('b\'{"error":"unknown_error"}\''), err
  1243. res = admin.delete_client_roles_of_client_scope(
  1244. client_id=client_id,
  1245. client_roles_owner_id=client,
  1246. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1247. )
  1248. assert res == dict(), res
  1249. roles = admin.get_client_roles_of_client_scope(
  1250. client_id=client_id, client_roles_owner_id=client
  1251. )
  1252. assert len(roles) == 0
  1253. def test_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1254. """Test client assignment of default client scopes.
  1255. :param admin: Keycloak admin
  1256. :type admin: KeycloakAdmin
  1257. :param realm: Keycloak realm
  1258. :type realm: str
  1259. :param client: Keycloak client
  1260. :type client: str
  1261. """
  1262. admin.change_current_realm(realm)
  1263. client_id = admin.create_client(
  1264. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1265. )
  1266. # Test get client default scopes
  1267. # keycloak default roles: web-origins, acr, profile, roles, email
  1268. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1269. assert len(default_client_scopes) == 5, default_client_scopes
  1270. # Test add a client scope to client default scopes
  1271. default_client_scope = "test-client-default-scope"
  1272. new_client_scope = {
  1273. "name": default_client_scope,
  1274. "description": f"Test Client Scope: {default_client_scope}",
  1275. "protocol": "openid-connect",
  1276. "attributes": {},
  1277. }
  1278. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1279. new_default_client_scope_data = {
  1280. "realm": realm,
  1281. "client": client_id,
  1282. "clientScopeId": new_client_scope_id,
  1283. }
  1284. admin.add_client_default_client_scope(
  1285. client_id, new_client_scope_id, new_default_client_scope_data
  1286. )
  1287. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1288. assert len(default_client_scopes) == 6, default_client_scopes
  1289. # Test remove a client default scope
  1290. admin.delete_client_default_client_scope(client_id, new_client_scope_id)
  1291. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1292. assert len(default_client_scopes) == 5, default_client_scopes
  1293. def test_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1294. """Test client assignment of optional client scopes.
  1295. :param admin: Keycloak admin
  1296. :type admin: KeycloakAdmin
  1297. :param realm: Keycloak realm
  1298. :type realm: str
  1299. :param client: Keycloak client
  1300. :type client: str
  1301. """
  1302. admin.change_current_realm(realm)
  1303. client_id = admin.create_client(
  1304. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1305. )
  1306. # Test get client optional scopes
  1307. # keycloak optional roles: microprofile-jwt, offline_access, address, phone
  1308. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1309. assert len(optional_client_scopes) == 4, optional_client_scopes
  1310. # Test add a client scope to client optional scopes
  1311. optional_client_scope = "test-client-optional-scope"
  1312. new_client_scope = {
  1313. "name": optional_client_scope,
  1314. "description": f"Test Client Scope: {optional_client_scope}",
  1315. "protocol": "openid-connect",
  1316. "attributes": {},
  1317. }
  1318. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1319. new_optional_client_scope_data = {
  1320. "realm": realm,
  1321. "client": client_id,
  1322. "clientScopeId": new_client_scope_id,
  1323. }
  1324. admin.add_client_optional_client_scope(
  1325. client_id, new_client_scope_id, new_optional_client_scope_data
  1326. )
  1327. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1328. assert len(optional_client_scopes) == 5, optional_client_scopes
  1329. # Test remove a client optional scope
  1330. admin.delete_client_optional_client_scope(client_id, new_client_scope_id)
  1331. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1332. assert len(optional_client_scopes) == 4, optional_client_scopes
  1333. def test_client_roles(admin: KeycloakAdmin, client: str):
  1334. """Test client roles.
  1335. :param admin: Keycloak Admin client
  1336. :type admin: KeycloakAdmin
  1337. :param client: Keycloak client
  1338. :type client: str
  1339. """
  1340. # Test get client roles
  1341. res = admin.get_client_roles(client_id=client)
  1342. assert len(res) == 0
  1343. with pytest.raises(KeycloakGetError) as err:
  1344. admin.get_client_roles(client_id="bad")
  1345. assert err.match('404: b\'{"error":"Could not find client"}\'')
  1346. # Test create client role
  1347. client_role_id = admin.create_client_role(
  1348. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1349. )
  1350. with pytest.raises(KeycloakPostError) as err:
  1351. admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"})
  1352. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  1353. client_role_id_2 = admin.create_client_role(
  1354. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1355. )
  1356. assert client_role_id == client_role_id_2
  1357. # Test get client role
  1358. res = admin.get_client_role(client_id=client, role_name="client-role-test")
  1359. assert res["name"] == client_role_id
  1360. with pytest.raises(KeycloakGetError) as err:
  1361. admin.get_client_role(client_id=client, role_name="bad")
  1362. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1363. res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test")
  1364. assert res_ == res["id"]
  1365. with pytest.raises(KeycloakGetError) as err:
  1366. admin.get_client_role_id(client_id=client, role_name="bad")
  1367. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1368. assert len(admin.get_client_roles(client_id=client)) == 1
  1369. # Test update client role
  1370. res = admin.update_client_role(
  1371. client_id=client,
  1372. role_name="client-role-test",
  1373. payload={"name": "client-role-test-update"},
  1374. )
  1375. assert res == dict()
  1376. with pytest.raises(KeycloakPutError) as err:
  1377. res = admin.update_client_role(
  1378. client_id=client,
  1379. role_name="client-role-test",
  1380. payload={"name": "client-role-test-update"},
  1381. )
  1382. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1383. # Test user with client role
  1384. res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update")
  1385. assert len(res) == 0
  1386. with pytest.raises(KeycloakGetError) as err:
  1387. admin.get_client_role_members(client_id=client, role_name="bad")
  1388. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1389. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  1390. with pytest.raises(KeycloakPostError) as err:
  1391. admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  1392. assert err.match('b\'{"error":"unknown_error"}\''), err
  1393. res = admin.assign_client_role(
  1394. user_id=user_id,
  1395. client_id=client,
  1396. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1397. )
  1398. assert res == dict()
  1399. assert (
  1400. len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update"))
  1401. == 1
  1402. )
  1403. roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client)
  1404. assert len(roles) == 1, roles
  1405. with pytest.raises(KeycloakGetError) as err:
  1406. admin.get_client_roles_of_user(user_id=user_id, client_id="bad")
  1407. assert err.match('404: b\'{"error":"Client not found"}\'')
  1408. roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  1409. assert len(roles) == 1, roles
  1410. with pytest.raises(KeycloakGetError) as err:
  1411. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1412. assert err.match('404: b\'{"error":"Client not found"}\'')
  1413. roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client)
  1414. assert len(roles) == 0, roles
  1415. with pytest.raises(KeycloakGetError) as err:
  1416. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1417. assert err.match('404: b\'{"error":"Client not found"}\'')
  1418. with pytest.raises(KeycloakDeleteError) as err:
  1419. admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  1420. assert err.match('b\'{"error":"unknown_error"}\''), err
  1421. admin.delete_client_roles_of_user(
  1422. user_id=user_id,
  1423. client_id=client,
  1424. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1425. )
  1426. assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  1427. # Test groups and client roles
  1428. res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")
  1429. assert len(res) == 0
  1430. with pytest.raises(KeycloakGetError) as err:
  1431. admin.get_client_role_groups(client_id=client, role_name="bad")
  1432. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1433. group_id = admin.create_group(payload={"name": "test-group"})
  1434. res = admin.get_group_client_roles(group_id=group_id, client_id=client)
  1435. assert len(res) == 0
  1436. with pytest.raises(KeycloakGetError) as err:
  1437. admin.get_group_client_roles(group_id=group_id, client_id="bad")
  1438. assert err.match('404: b\'{"error":"Client not found"}\'')
  1439. with pytest.raises(KeycloakPostError) as err:
  1440. admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1441. assert err.match('b\'{"error":"unknown_error"}\''), err
  1442. res = admin.assign_group_client_roles(
  1443. group_id=group_id,
  1444. client_id=client,
  1445. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1446. )
  1447. assert res == dict()
  1448. assert (
  1449. len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update"))
  1450. == 1
  1451. )
  1452. assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1
  1453. with pytest.raises(KeycloakDeleteError) as err:
  1454. admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1455. assert err.match('b\'{"error":"unknown_error"}\''), err
  1456. res = admin.delete_group_client_roles(
  1457. group_id=group_id,
  1458. client_id=client,
  1459. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1460. )
  1461. assert res == dict()
  1462. # Test composite client roles
  1463. with pytest.raises(KeycloakPostError) as err:
  1464. admin.add_composite_client_roles_to_role(
  1465. client_role_id=client, role_name="client-role-test-update", roles=["bad"]
  1466. )
  1467. assert err.match('b\'{"error":"unknown_error"}\''), err
  1468. res = admin.add_composite_client_roles_to_role(
  1469. client_role_id=client,
  1470. role_name="client-role-test-update",
  1471. roles=[admin.get_realm_role(role_name="offline_access")],
  1472. )
  1473. assert res == dict()
  1474. assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1475. "composite"
  1476. ]
  1477. # Test delete of client role
  1478. res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1479. assert res == dict()
  1480. with pytest.raises(KeycloakDeleteError) as err:
  1481. admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1482. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1483. # Test of roles by id - Get role
  1484. admin.create_client_role(
  1485. client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
  1486. )
  1487. role = admin.get_client_role(client_id=client, role_name="client-role-by-id-test")
  1488. res = admin.get_role_by_id(role_id=role["id"])
  1489. assert res["name"] == "client-role-by-id-test"
  1490. with pytest.raises(KeycloakGetError) as err:
  1491. admin.get_role_by_id(role_id="bad")
  1492. assert err.match('404: b\'{"error":"Could not find role with id"}\'')
  1493. # Test of roles by id - Update role
  1494. res = admin.update_role_by_id(
  1495. role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
  1496. )
  1497. assert res == dict()
  1498. with pytest.raises(KeycloakPutError) as err:
  1499. res = admin.update_role_by_id(
  1500. role_id="bad", payload={"name": "client-role-by-id-test-update"}
  1501. )
  1502. assert err.match('404: b\'{"error":"Could not find role with id"}\'')
  1503. # Test of roles by id - Delete role
  1504. res = admin.delete_role_by_id(role_id=role["id"])
  1505. assert res == dict()
  1506. with pytest.raises(KeycloakDeleteError) as err:
  1507. admin.delete_role_by_id(role_id="bad")
  1508. assert err.match('404: b\'{"error":"Could not find role with id"}\'')
  1509. def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
  1510. """Test enable token exchange.
  1511. :param admin: Keycloak Admin client
  1512. :type admin: KeycloakAdmin
  1513. :param realm: Keycloak realm
  1514. :type realm: str
  1515. :raises AssertionError: In case of bad configuration
  1516. """
  1517. # Test enabling token exchange between two confidential clients
  1518. admin.change_current_realm(realm)
  1519. # Create test clients
  1520. source_client_id = admin.create_client(
  1521. payload={"name": "Source Client", "clientId": "source-client"}
  1522. )
  1523. target_client_id = admin.create_client(
  1524. payload={"name": "Target Client", "clientId": "target-client"}
  1525. )
  1526. for c in admin.get_clients():
  1527. if c["clientId"] == "realm-management":
  1528. realm_management_id = c["id"]
  1529. break
  1530. else:
  1531. raise AssertionError("Missing realm management client")
  1532. # Enable permissions on the Superset client
  1533. admin.update_client_management_permissions(
  1534. payload={"enabled": True}, client_id=target_client_id
  1535. )
  1536. # Fetch various IDs and strings needed when creating the permission
  1537. token_exchange_permission_id = admin.get_client_management_permissions(
  1538. client_id=target_client_id
  1539. )["scopePermissions"]["token-exchange"]
  1540. scopes = admin.get_client_authz_policy_scopes(
  1541. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1542. )
  1543. for s in scopes:
  1544. if s["name"] == "token-exchange":
  1545. token_exchange_scope_id = s["id"]
  1546. break
  1547. else:
  1548. raise AssertionError("Missing token-exchange scope")
  1549. resources = admin.get_client_authz_policy_resources(
  1550. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1551. )
  1552. for r in resources:
  1553. if r["name"] == f"client.resource.{target_client_id}":
  1554. token_exchange_resource_id = r["_id"]
  1555. break
  1556. else:
  1557. raise AssertionError("Missing client resource")
  1558. # Create a client policy for source client
  1559. policy_name = "Exchange source client token with target client token"
  1560. client_policy_id = admin.create_client_authz_client_policy(
  1561. payload={
  1562. "type": "client",
  1563. "logic": "POSITIVE",
  1564. "decisionStrategy": "UNANIMOUS",
  1565. "name": policy_name,
  1566. "clients": [source_client_id],
  1567. },
  1568. client_id=realm_management_id,
  1569. )["id"]
  1570. policies = admin.get_client_authz_client_policies(client_id=realm_management_id)
  1571. for policy in policies:
  1572. if policy["name"] == policy_name:
  1573. assert policy["clients"] == [source_client_id]
  1574. break
  1575. else:
  1576. raise AssertionError("Missing client policy")
  1577. # Update permissions on the target client to reference this policy
  1578. permission_name = admin.get_client_authz_scope_permission(
  1579. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1580. )["name"]
  1581. admin.update_client_authz_scope_permission(
  1582. payload={
  1583. "id": token_exchange_permission_id,
  1584. "name": permission_name,
  1585. "type": "scope",
  1586. "logic": "POSITIVE",
  1587. "decisionStrategy": "UNANIMOUS",
  1588. "resources": [token_exchange_resource_id],
  1589. "scopes": [token_exchange_scope_id],
  1590. "policies": [client_policy_id],
  1591. },
  1592. client_id=realm_management_id,
  1593. scope_id=token_exchange_permission_id,
  1594. )
  1595. # Create permissions on the target client to reference this policy
  1596. admin.create_client_authz_scope_permission(
  1597. payload={
  1598. "id": "some-id",
  1599. "name": "test-permission",
  1600. "type": "scope",
  1601. "logic": "POSITIVE",
  1602. "decisionStrategy": "UNANIMOUS",
  1603. "resources": [token_exchange_resource_id],
  1604. "scopes": [token_exchange_scope_id],
  1605. "policies": [client_policy_id],
  1606. },
  1607. client_id=realm_management_id,
  1608. )
  1609. permission_name = admin.get_client_authz_scope_permission(
  1610. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1611. )["name"]
  1612. assert permission_name.startswith("token-exchange.permission.client.")
  1613. with pytest.raises(KeycloakPostError) as err:
  1614. admin.create_client_authz_scope_permission(
  1615. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  1616. client_id="realm_management_id",
  1617. )
  1618. assert err.match('404: b\'{"error":"Could not find client"}\'')
  1619. def test_email(admin: KeycloakAdmin, user: str):
  1620. """Test email.
  1621. :param admin: Keycloak Admin client
  1622. :type admin: KeycloakAdmin
  1623. :param user: Keycloak user
  1624. :type user: str
  1625. """
  1626. # Emails will fail as we don't have SMTP test setup
  1627. with pytest.raises(KeycloakPutError) as err:
  1628. admin.send_update_account(user_id=user, payload=dict())
  1629. assert err.match('b\'{"error":"unknown_error"}\''), err
  1630. admin.update_user(user_id=user, payload={"enabled": True})
  1631. with pytest.raises(KeycloakPutError) as err:
  1632. admin.send_verify_email(user_id=user)
  1633. assert err.match('500: b\'{"errorMessage":"Failed to send execute actions email"}\'')
  1634. def test_get_sessions(admin: KeycloakAdmin):
  1635. """Test get sessions.
  1636. :param admin: Keycloak Admin client
  1637. :type admin: KeycloakAdmin
  1638. """
  1639. sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username))
  1640. assert len(sessions) >= 1
  1641. with pytest.raises(KeycloakGetError) as err:
  1642. admin.get_sessions(user_id="bad")
  1643. assert err.match('404: b\'{"error":"User not found"}\'')
  1644. def test_get_client_installation_provider(admin: KeycloakAdmin, client: str):
  1645. """Test get client installation provider.
  1646. :param admin: Keycloak Admin client
  1647. :type admin: KeycloakAdmin
  1648. :param client: Keycloak client
  1649. :type client: str
  1650. """
  1651. with pytest.raises(KeycloakGetError) as err:
  1652. admin.get_client_installation_provider(client_id=client, provider_id="bad")
  1653. assert err.match('404: b\'{"error":"Unknown Provider"}\'')
  1654. installation = admin.get_client_installation_provider(
  1655. client_id=client, provider_id="keycloak-oidc-keycloak-json"
  1656. )
  1657. assert set(installation.keys()) == {
  1658. "auth-server-url",
  1659. "confidential-port",
  1660. "credentials",
  1661. "realm",
  1662. "resource",
  1663. "ssl-required",
  1664. }
  1665. def test_auth_flows(admin: KeycloakAdmin, realm: str):
  1666. """Test auth flows.
  1667. :param admin: Keycloak Admin client
  1668. :type admin: KeycloakAdmin
  1669. :param realm: Keycloak realm
  1670. :type realm: str
  1671. """
  1672. admin.change_current_realm(realm)
  1673. res = admin.get_authentication_flows()
  1674. assert len(res) <= 8, res
  1675. default_flows = len(res)
  1676. assert {x["alias"] for x in res}.issubset(
  1677. {
  1678. "reset credentials",
  1679. "browser",
  1680. "registration",
  1681. "http challenge",
  1682. "docker auth",
  1683. "direct grant",
  1684. "first broker login",
  1685. "clients",
  1686. }
  1687. )
  1688. assert set(res[0].keys()) == {
  1689. "alias",
  1690. "authenticationExecutions",
  1691. "builtIn",
  1692. "description",
  1693. "id",
  1694. "providerId",
  1695. "topLevel",
  1696. }
  1697. assert {x["alias"] for x in res}.issubset(
  1698. {
  1699. "reset credentials",
  1700. "browser",
  1701. "registration",
  1702. "docker auth",
  1703. "direct grant",
  1704. "first broker login",
  1705. "clients",
  1706. "http challenge",
  1707. }
  1708. )
  1709. with pytest.raises(KeycloakGetError) as err:
  1710. admin.get_authentication_flow_for_id(flow_id="bad")
  1711. assert err.match('404: b\'{"error":"Could not find flow with id"}\'')
  1712. browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"]
  1713. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  1714. assert res["alias"] == "browser"
  1715. # Test copying
  1716. with pytest.raises(KeycloakPostError) as err:
  1717. admin.copy_authentication_flow(payload=dict(), flow_alias="bad")
  1718. assert err.match("404: b''")
  1719. res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser")
  1720. assert res == b"", res
  1721. assert len(admin.get_authentication_flows()) == (default_flows + 1)
  1722. # Test create
  1723. res = admin.create_authentication_flow(
  1724. payload={"alias": "test-create", "providerId": "basic-flow"}
  1725. )
  1726. assert res == b""
  1727. with pytest.raises(KeycloakPostError) as err:
  1728. admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False})
  1729. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  1730. assert admin.create_authentication_flow(
  1731. payload={"alias": "test-create"}, skip_exists=True
  1732. ) == {"msg": "Already exists"}
  1733. # Test flow executions
  1734. res = admin.get_authentication_flow_executions(flow_alias="browser")
  1735. assert len(res) == 8, res
  1736. with pytest.raises(KeycloakGetError) as err:
  1737. admin.get_authentication_flow_executions(flow_alias="bad")
  1738. assert err.match("404: b''")
  1739. exec_id = res[0]["id"]
  1740. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  1741. assert set(res.keys()) == {
  1742. "alternative",
  1743. "authenticator",
  1744. "authenticatorFlow",
  1745. "conditional",
  1746. "disabled",
  1747. "enabled",
  1748. "id",
  1749. "parentFlow",
  1750. "priority",
  1751. "required",
  1752. "requirement",
  1753. }, res
  1754. with pytest.raises(KeycloakGetError) as err:
  1755. admin.get_authentication_flow_execution(execution_id="bad")
  1756. assert err.match('404: b\'{"error":"Illegal execution"}\'')
  1757. with pytest.raises(KeycloakPostError) as err:
  1758. admin.create_authentication_flow_execution(payload=dict(), flow_alias="browser")
  1759. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow"}\'')
  1760. res = admin.create_authentication_flow_execution(
  1761. payload={"provider": "auth-cookie"}, flow_alias="test-create"
  1762. )
  1763. assert res == b""
  1764. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  1765. with pytest.raises(KeycloakPutError) as err:
  1766. admin.update_authentication_flow_executions(
  1767. payload={"required": "yes"}, flow_alias="test-create"
  1768. )
  1769. assert err.match('400: b\'{"error":"Unrecognized field')
  1770. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  1771. payload["displayName"] = "test"
  1772. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  1773. assert res
  1774. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  1775. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  1776. assert res == dict()
  1777. with pytest.raises(KeycloakDeleteError) as err:
  1778. admin.delete_authentication_flow_execution(execution_id=exec_id)
  1779. assert err.match('404: b\'{"error":"Illegal execution"}\'')
  1780. # Test subflows
  1781. res = admin.create_authentication_flow_subflow(
  1782. payload={
  1783. "alias": "test-subflow",
  1784. "provider": "basic-flow",
  1785. "type": "something",
  1786. "description": "something",
  1787. },
  1788. flow_alias="test-browser",
  1789. )
  1790. assert res == b""
  1791. with pytest.raises(KeycloakPostError) as err:
  1792. admin.create_authentication_flow_subflow(
  1793. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  1794. flow_alias="test-browser",
  1795. )
  1796. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  1797. res = admin.create_authentication_flow_subflow(
  1798. payload={
  1799. "alias": "test-subflow",
  1800. "provider": "basic-flow",
  1801. "type": "something",
  1802. "description": "something",
  1803. },
  1804. flow_alias="test-create",
  1805. skip_exists=True,
  1806. )
  1807. assert res == {"msg": "Already exists"}
  1808. # Test delete auth flow
  1809. flow_id = [x for x in admin.get_authentication_flows() if x["alias"] == "test-browser"][0][
  1810. "id"
  1811. ]
  1812. res = admin.delete_authentication_flow(flow_id=flow_id)
  1813. assert res == dict()
  1814. with pytest.raises(KeycloakDeleteError) as err:
  1815. admin.delete_authentication_flow(flow_id=flow_id)
  1816. assert err.match('404: b\'{"error":"Could not find flow with id"}\'')
  1817. def test_authentication_configs(admin: KeycloakAdmin, realm: str):
  1818. """Test authentication configs.
  1819. :param admin: Keycloak Admin client
  1820. :type admin: KeycloakAdmin
  1821. :param realm: Keycloak realm
  1822. :type realm: str
  1823. """
  1824. admin.change_current_realm(realm)
  1825. # Test list of auth providers
  1826. res = admin.get_authenticator_providers()
  1827. assert len(res) <= 38
  1828. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  1829. assert res == {
  1830. "helpText": "Validates the SSO cookie set by the auth server.",
  1831. "name": "Cookie",
  1832. "properties": [],
  1833. "providerId": "auth-cookie",
  1834. }
  1835. # Test authenticator config
  1836. # Currently unable to find a sustainable way to fetch the config id,
  1837. # therefore testing only failures
  1838. with pytest.raises(KeycloakGetError) as err:
  1839. admin.get_authenticator_config(config_id="bad")
  1840. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1841. with pytest.raises(KeycloakPutError) as err:
  1842. admin.update_authenticator_config(payload=dict(), config_id="bad")
  1843. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1844. with pytest.raises(KeycloakDeleteError) as err:
  1845. admin.delete_authenticator_config(config_id="bad")
  1846. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1847. def test_sync_users(admin: KeycloakAdmin, realm: str):
  1848. """Test sync users.
  1849. :param admin: Keycloak Admin client
  1850. :type admin: KeycloakAdmin
  1851. :param realm: Keycloak realm
  1852. :type realm: str
  1853. """
  1854. admin.change_current_realm(realm)
  1855. # Only testing the error message
  1856. with pytest.raises(KeycloakPostError) as err:
  1857. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  1858. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1859. def test_client_scopes(admin: KeycloakAdmin, realm: str):
  1860. """Test client scopes.
  1861. :param admin: Keycloak Admin client
  1862. :type admin: KeycloakAdmin
  1863. :param realm: Keycloak realm
  1864. :type realm: str
  1865. """
  1866. admin.change_current_realm(realm)
  1867. # Test get client scopes
  1868. res = admin.get_client_scopes()
  1869. scope_names = {x["name"] for x in res}
  1870. assert len(res) == 10
  1871. assert "email" in scope_names
  1872. assert "profile" in scope_names
  1873. assert "offline_access" in scope_names
  1874. with pytest.raises(KeycloakGetError) as err:
  1875. admin.get_client_scope(client_scope_id="does-not-exist")
  1876. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1877. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  1878. assert res[0] == scope
  1879. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  1880. assert res[0] == scope
  1881. # Test create client scope
  1882. res = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1883. assert res
  1884. res2 = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1885. assert res == res2
  1886. with pytest.raises(KeycloakPostError) as err:
  1887. admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=False)
  1888. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  1889. # Test update client scope
  1890. with pytest.raises(KeycloakPutError) as err:
  1891. admin.update_client_scope(client_scope_id="does-not-exist", payload=dict())
  1892. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1893. res_update = admin.update_client_scope(
  1894. client_scope_id=res, payload={"name": "test-scope-update"}
  1895. )
  1896. assert res_update == dict()
  1897. admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  1898. # Test get mappers
  1899. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  1900. assert mappers == list()
  1901. # Test add mapper
  1902. with pytest.raises(KeycloakPostError) as err:
  1903. admin.add_mapper_to_client_scope(client_scope_id=res, payload=dict())
  1904. assert err.match('404: b\'{"error":"ProtocolMapper provider not found"}\'')
  1905. res_add = admin.add_mapper_to_client_scope(
  1906. client_scope_id=res,
  1907. payload={
  1908. "name": "test-mapper",
  1909. "protocol": "openid-connect",
  1910. "protocolMapper": "oidc-usermodel-attribute-mapper",
  1911. },
  1912. )
  1913. assert res_add == b""
  1914. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  1915. # Test update mapper
  1916. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  1917. with pytest.raises(KeycloakPutError) as err:
  1918. admin.update_mapper_in_client_scope(
  1919. client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
  1920. )
  1921. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1922. test_mapper["config"]["user.attribute"] = "test"
  1923. res_update = admin.update_mapper_in_client_scope(
  1924. client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
  1925. )
  1926. assert res_update == dict()
  1927. assert (
  1928. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  1929. == "test"
  1930. )
  1931. # Test delete mapper
  1932. res_del = admin.delete_mapper_from_client_scope(
  1933. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1934. )
  1935. assert res_del == dict()
  1936. with pytest.raises(KeycloakDeleteError) as err:
  1937. admin.delete_mapper_from_client_scope(
  1938. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1939. )
  1940. assert err.match('404: b\'{"error":"Model not found"}\'')
  1941. # Test default default scopes
  1942. res_defaults = admin.get_default_default_client_scopes()
  1943. assert len(res_defaults) == 6
  1944. with pytest.raises(KeycloakPutError) as err:
  1945. admin.add_default_default_client_scope(scope_id="does-not-exist")
  1946. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1947. res_add = admin.add_default_default_client_scope(scope_id=res)
  1948. assert res_add == dict()
  1949. assert len(admin.get_default_default_client_scopes()) == 7
  1950. with pytest.raises(KeycloakDeleteError) as err:
  1951. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  1952. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1953. res_del = admin.delete_default_default_client_scope(scope_id=res)
  1954. assert res_del == dict()
  1955. assert len(admin.get_default_default_client_scopes()) == 6
  1956. # Test default optional scopes
  1957. res_defaults = admin.get_default_optional_client_scopes()
  1958. assert len(res_defaults) == 4
  1959. with pytest.raises(KeycloakPutError) as err:
  1960. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  1961. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1962. res_add = admin.add_default_optional_client_scope(scope_id=res)
  1963. assert res_add == dict()
  1964. assert len(admin.get_default_optional_client_scopes()) == 5
  1965. with pytest.raises(KeycloakDeleteError) as err:
  1966. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  1967. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1968. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  1969. assert res_del == dict()
  1970. assert len(admin.get_default_optional_client_scopes()) == 4
  1971. # Test client scope delete
  1972. res_del = admin.delete_client_scope(client_scope_id=res)
  1973. assert res_del == dict()
  1974. with pytest.raises(KeycloakDeleteError) as err:
  1975. admin.delete_client_scope(client_scope_id=res)
  1976. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1977. def test_components(admin: KeycloakAdmin, realm: str):
  1978. """Test components.
  1979. :param admin: Keycloak Admin client
  1980. :type admin: KeycloakAdmin
  1981. :param realm: Keycloak realm
  1982. :type realm: str
  1983. """
  1984. admin.change_current_realm(realm)
  1985. # Test get components
  1986. res = admin.get_components()
  1987. assert len(res) == 12
  1988. with pytest.raises(KeycloakGetError) as err:
  1989. admin.get_component(component_id="does-not-exist")
  1990. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1991. res_get = admin.get_component(component_id=res[0]["id"])
  1992. assert res_get == res[0]
  1993. # Test create component
  1994. with pytest.raises(KeycloakPostError) as err:
  1995. admin.create_component(payload={"bad": "dict"})
  1996. assert err.match('400: b\'{"error":"Unrecognized field')
  1997. res = admin.create_component(
  1998. payload={
  1999. "name": "Test Component",
  2000. "providerId": "max-clients",
  2001. "providerType": "org.keycloak.services.clientregistration."
  2002. + "policy.ClientRegistrationPolicy",
  2003. "config": {"max-clients": ["1000"]},
  2004. }
  2005. )
  2006. assert res
  2007. assert admin.get_component(component_id=res)["name"] == "Test Component"
  2008. # Test update component
  2009. component = admin.get_component(component_id=res)
  2010. component["name"] = "Test Component Update"
  2011. with pytest.raises(KeycloakPutError) as err:
  2012. admin.update_component(component_id="does-not-exist", payload=dict())
  2013. assert err.match('404: b\'{"error":"Could not find component"}\'')
  2014. res_upd = admin.update_component(component_id=res, payload=component)
  2015. assert res_upd == dict()
  2016. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  2017. # Test delete component
  2018. res_del = admin.delete_component(component_id=res)
  2019. assert res_del == dict()
  2020. with pytest.raises(KeycloakDeleteError) as err:
  2021. admin.delete_component(component_id=res)
  2022. assert err.match('404: b\'{"error":"Could not find component"}\'')
  2023. def test_keys(admin: KeycloakAdmin, realm: str):
  2024. """Test keys.
  2025. :param admin: Keycloak Admin client
  2026. :type admin: KeycloakAdmin
  2027. :param realm: Keycloak realm
  2028. :type realm: str
  2029. """
  2030. admin.change_current_realm(realm)
  2031. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"}
  2032. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2033. "HS256",
  2034. "RSA-OAEP",
  2035. "AES",
  2036. "RS256",
  2037. }
  2038. def test_admin_events(admin: KeycloakAdmin, realm: str):
  2039. """Test events.
  2040. :param admin: Keycloak Admin client
  2041. :type admin: KeycloakAdmin
  2042. :param realm: Keycloak realm
  2043. :type realm: str
  2044. """
  2045. admin.change_current_realm(realm)
  2046. admin.create_client(payload={"name": "test", "clientId": "test"})
  2047. events = admin.get_admin_events()
  2048. assert events == list()
  2049. def test_user_events(admin: KeycloakAdmin, realm: str):
  2050. """Test events.
  2051. :param admin: Keycloak Admin client
  2052. :type admin: KeycloakAdmin
  2053. :param realm: Keycloak realm
  2054. :type realm: str
  2055. """
  2056. admin.change_current_realm(realm)
  2057. events = admin.get_events()
  2058. assert events == list()
  2059. with pytest.raises(KeycloakPutError) as err:
  2060. admin.set_events(payload={"bad": "conf"})
  2061. assert err.match('400: b\'{"error":"Unrecognized field')
  2062. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  2063. assert res == dict()
  2064. admin.create_client(payload={"name": "test", "clientId": "test"})
  2065. events = admin.get_events()
  2066. assert events == list()
  2067. @freezegun.freeze_time("2023-02-25 10:00:00")
  2068. def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
  2069. """Test auto refresh token.
  2070. :param admin_frozen: Keycloak Admin client with time frozen in place
  2071. :type admin_frozen: KeycloakAdmin
  2072. :param realm: Keycloak realm
  2073. :type realm: str
  2074. """
  2075. admin = admin_frozen
  2076. # Test get refresh
  2077. admin.connection.custom_headers = {
  2078. "Authorization": "Bearer bad",
  2079. "Content-Type": "application/json",
  2080. }
  2081. with pytest.raises(KeycloakAuthenticationError) as err:
  2082. admin.get_realm(realm_name=realm)
  2083. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'')
  2084. # Freeze time to simulate the access token expiring
  2085. with freezegun.freeze_time("2023-02-25 10:05:00"):
  2086. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
  2087. assert admin.get_realm(realm_name=realm)
  2088. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
  2089. # Test bad refresh token, but first make sure access token has expired again
  2090. with freezegun.freeze_time("2023-02-25 10:10:00"):
  2091. admin.connection.custom_headers = {"Content-Type": "application/json"}
  2092. admin.connection.token["refresh_token"] = "bad"
  2093. with pytest.raises(KeycloakPostError) as err:
  2094. admin.get_realm(realm_name="test-refresh")
  2095. assert err.match(
  2096. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
  2097. )
  2098. admin.connection.get_token()
  2099. # Test post refresh
  2100. with freezegun.freeze_time("2023-02-25 10:15:00"):
  2101. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
  2102. admin.connection.token = None
  2103. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  2104. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
  2105. # Test update refresh
  2106. with freezegun.freeze_time("2023-02-25 10:25:00"):
  2107. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
  2108. admin.connection.token = None
  2109. assert (
  2110. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  2111. == dict()
  2112. )
  2113. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
  2114. # Test delete refresh
  2115. with freezegun.freeze_time("2023-02-25 10:35:00"):
  2116. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
  2117. admin.connection.token = None
  2118. assert admin.delete_realm(realm_name="test-refresh") == dict()
  2119. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
  2120. def test_get_required_actions(admin: KeycloakAdmin, realm: str):
  2121. """Test required actions.
  2122. :param admin: Keycloak Admin client
  2123. :type admin: KeycloakAdmin
  2124. :param realm: Keycloak realm
  2125. :type realm: str
  2126. """
  2127. admin.change_current_realm(realm)
  2128. ractions = admin.get_required_actions()
  2129. assert isinstance(ractions, list)
  2130. for ra in ractions:
  2131. for key in [
  2132. "alias",
  2133. "name",
  2134. "providerId",
  2135. "enabled",
  2136. "defaultAction",
  2137. "priority",
  2138. "config",
  2139. ]:
  2140. assert key in ra
  2141. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
  2142. """Test get required action by alias.
  2143. :param admin: Keycloak Admin client
  2144. :type admin: KeycloakAdmin
  2145. :param realm: Keycloak realm
  2146. :type realm: str
  2147. """
  2148. admin.change_current_realm(realm)
  2149. ractions = admin.get_required_actions()
  2150. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2151. assert ra in ractions
  2152. assert ra["alias"] == "UPDATE_PASSWORD"
  2153. assert admin.get_required_action_by_alias("does-not-exist") is None
  2154. def test_update_required_action(admin: KeycloakAdmin, realm: str):
  2155. """Test update required action.
  2156. :param admin: Keycloak Admin client
  2157. :type admin: KeycloakAdmin
  2158. :param realm: Keycloak realm
  2159. :type realm: str
  2160. """
  2161. admin.change_current_realm(realm)
  2162. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2163. old = copy.deepcopy(ra)
  2164. ra["enabled"] = False
  2165. admin.update_required_action("UPDATE_PASSWORD", ra)
  2166. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2167. assert old != newra
  2168. assert newra["enabled"] is False
  2169. def test_get_composite_client_roles_of_group(
  2170. admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str
  2171. ):
  2172. """Test get composite client roles of group.
  2173. :param admin: Keycloak Admin client
  2174. :type admin: KeycloakAdmin
  2175. :param realm: Keycloak realm
  2176. :type realm: str
  2177. :param client: Keycloak client
  2178. :type client: str
  2179. :param group: Keycloak group
  2180. :type group: str
  2181. :param composite_client_role: Composite client role
  2182. :type composite_client_role: str
  2183. """
  2184. admin.change_current_realm(realm)
  2185. role = admin.get_client_role(client, composite_client_role)
  2186. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  2187. result = admin.get_composite_client_roles_of_group(client, group)
  2188. assert role["id"] in [x["id"] for x in result]
  2189. def test_get_role_client_level_children(
  2190. admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str
  2191. ):
  2192. """Test get children of composite client role.
  2193. :param admin: Keycloak Admin client
  2194. :type admin: KeycloakAdmin
  2195. :param realm: Keycloak realm
  2196. :type realm: str
  2197. :param client: Keycloak client
  2198. :type client: str
  2199. :param composite_client_role: Composite client role
  2200. :type composite_client_role: str
  2201. :param client_role: Client role
  2202. :type client_role: str
  2203. """
  2204. admin.change_current_realm(realm)
  2205. child = admin.get_client_role(client, client_role)
  2206. parent = admin.get_client_role(client, composite_client_role)
  2207. res = admin.get_role_client_level_children(client, parent["id"])
  2208. assert child["id"] in [x["id"] for x in res]
  2209. def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple):
  2210. """Test upload certificate.
  2211. :param admin: Keycloak Admin client
  2212. :type admin: KeycloakAdmin
  2213. :param realm: Keycloak realm
  2214. :type realm: str
  2215. :param client: Keycloak client
  2216. :type client: str
  2217. :param selfsigned_cert: Selfsigned certificates
  2218. :type selfsigned_cert: tuple
  2219. """
  2220. admin.change_current_realm(realm)
  2221. cert, _ = selfsigned_cert
  2222. cert = cert.decode("utf-8").strip()
  2223. admin.upload_certificate(client, cert)
  2224. cl = admin.get_client(client)
  2225. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  2226. def test_get_bruteforce_status_for_user(
  2227. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2228. ):
  2229. """Test users.
  2230. :param admin: Keycloak Admin client
  2231. :type admin: KeycloakAdmin
  2232. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2233. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2234. :param realm: Keycloak realm
  2235. :type realm: str
  2236. """
  2237. oid, username, password = oid_with_credentials
  2238. admin.change_current_realm(realm)
  2239. # Turn on bruteforce protection
  2240. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2241. res = admin.get_realm(realm_name=realm)
  2242. assert res["bruteForceProtected"] is True
  2243. # Test login user with wrong credentials
  2244. try:
  2245. oid.token(username=username, password="wrongpassword")
  2246. except KeycloakAuthenticationError:
  2247. pass
  2248. user_id = admin.get_user_id(username)
  2249. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2250. assert bruteforce_status["numFailures"] == 1
  2251. # Cleanup
  2252. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2253. res = admin.get_realm(realm_name=realm)
  2254. assert res["bruteForceProtected"] is False
  2255. def test_clear_bruteforce_attempts_for_user(
  2256. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2257. ):
  2258. """Test users.
  2259. :param admin: Keycloak Admin client
  2260. :type admin: KeycloakAdmin
  2261. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2262. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2263. :param realm: Keycloak realm
  2264. :type realm: str
  2265. """
  2266. oid, username, password = oid_with_credentials
  2267. admin.change_current_realm(realm)
  2268. # Turn on bruteforce protection
  2269. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2270. res = admin.get_realm(realm_name=realm)
  2271. assert res["bruteForceProtected"] is True
  2272. # Test login user with wrong credentials
  2273. try:
  2274. oid.token(username=username, password="wrongpassword")
  2275. except KeycloakAuthenticationError:
  2276. pass
  2277. user_id = admin.get_user_id(username)
  2278. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2279. assert bruteforce_status["numFailures"] == 1
  2280. res = admin.clear_bruteforce_attempts_for_user(user_id)
  2281. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2282. assert bruteforce_status["numFailures"] == 0
  2283. # Cleanup
  2284. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2285. res = admin.get_realm(realm_name=realm)
  2286. assert res["bruteForceProtected"] is False
  2287. def test_clear_bruteforce_attempts_for_all_users(
  2288. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2289. ):
  2290. """Test users.
  2291. :param admin: Keycloak Admin client
  2292. :type admin: KeycloakAdmin
  2293. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2294. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2295. :param realm: Keycloak realm
  2296. :type realm: str
  2297. """
  2298. oid, username, password = oid_with_credentials
  2299. admin.change_current_realm(realm)
  2300. # Turn on bruteforce protection
  2301. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2302. res = admin.get_realm(realm_name=realm)
  2303. assert res["bruteForceProtected"] is True
  2304. # Test login user with wrong credentials
  2305. try:
  2306. oid.token(username=username, password="wrongpassword")
  2307. except KeycloakAuthenticationError:
  2308. pass
  2309. user_id = admin.get_user_id(username)
  2310. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2311. assert bruteforce_status["numFailures"] == 1
  2312. res = admin.clear_all_bruteforce_attempts()
  2313. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2314. assert bruteforce_status["numFailures"] == 0
  2315. # Cleanup
  2316. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2317. res = admin.get_realm(realm_name=realm)
  2318. assert res["bruteForceProtected"] is False
  2319. def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  2320. """Test that the default realm role is present in a brand new realm.
  2321. :param realm: Realm name
  2322. :type realm: str
  2323. :param admin: Keycloak admin
  2324. :type admin: KeycloakAdmin
  2325. """
  2326. admin.change_current_realm(realm)
  2327. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  2328. assert (
  2329. len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"])
  2330. == 1
  2331. )
  2332. def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  2333. """Test getter for the ID of the default realm role.
  2334. :param realm: Realm name
  2335. :type realm: str
  2336. :param admin: Keycloak admin
  2337. :type admin: KeycloakAdmin
  2338. """
  2339. admin.change_current_realm(realm)
  2340. assert (
  2341. admin.get_default_realm_role_id()
  2342. == [x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"][0]
  2343. )
  2344. def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  2345. """Test getting, adding and deleting default realm roles.
  2346. :param realm: Realm name
  2347. :type realm: str
  2348. :param admin: Keycloak admin
  2349. :type admin: KeycloakAdmin
  2350. """
  2351. admin.change_current_realm(realm)
  2352. # Test listing all default realm roles
  2353. roles = admin.get_realm_default_roles()
  2354. assert len(roles) == 2
  2355. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  2356. with pytest.raises(KeycloakGetError) as err:
  2357. admin.realm_name = "doesnotexist"
  2358. admin.get_realm_default_roles()
  2359. assert err.match('404: b\'{"error":"Realm not found."}\'')
  2360. admin.change_current_realm(realm)
  2361. # Test removing a default realm role
  2362. res = admin.remove_realm_default_roles(payload=[roles[0]])
  2363. assert res == {}
  2364. assert roles[0] not in admin.get_realm_default_roles()
  2365. assert len(admin.get_realm_default_roles()) == 1
  2366. with pytest.raises(KeycloakDeleteError) as err:
  2367. admin.remove_realm_default_roles(payload=[{"id": "bad id"}])
  2368. assert err.match('404: b\'{"error":"Could not find composite role"}\'')
  2369. # Test adding a default realm role
  2370. res = admin.add_realm_default_roles(payload=[roles[0]])
  2371. assert res == {}
  2372. assert roles[0] in admin.get_realm_default_roles()
  2373. assert len(admin.get_realm_default_roles()) == 2
  2374. with pytest.raises(KeycloakPostError) as err:
  2375. admin.add_realm_default_roles(payload=[{"id": "bad id"}])
  2376. assert err.match('404: b\'{"error":"Could not find composite role"}\'')
  2377. def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  2378. """Test clearing the keys cache.
  2379. :param realm: Realm name
  2380. :type realm: str
  2381. :param admin: Keycloak admin
  2382. :type admin: KeycloakAdmin
  2383. """
  2384. admin.change_current_realm(realm)
  2385. res = admin.clear_keys_cache()
  2386. assert res == {}
  2387. def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  2388. """Test clearing the realm cache.
  2389. :param realm: Realm name
  2390. :type realm: str
  2391. :param admin: Keycloak admin
  2392. :type admin: KeycloakAdmin
  2393. """
  2394. admin.change_current_realm(realm)
  2395. res = admin.clear_realm_cache()
  2396. assert res == {}
  2397. def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  2398. """Test clearing the user cache.
  2399. :param realm: Realm name
  2400. :type realm: str
  2401. :param admin: Keycloak admin
  2402. :type admin: KeycloakAdmin
  2403. """
  2404. admin.change_current_realm(realm)
  2405. res = admin.clear_user_cache()
  2406. assert res == {}
  2407. def test_initial_access_token(
  2408. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2409. ) -> None:
  2410. """Test initial access token and client creation.
  2411. :param admin: Keycloak admin
  2412. :type admin: KeycloakAdmin
  2413. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2414. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2415. """
  2416. res = admin.create_initial_access_token(2, 3)
  2417. assert "token" in res
  2418. assert res["count"] == 2
  2419. assert res["expiration"] == 3
  2420. oid, username, password = oid_with_credentials
  2421. client = str(uuid.uuid4())
  2422. secret = str(uuid.uuid4())
  2423. res = oid.register_client(
  2424. token=res["token"],
  2425. payload={
  2426. "name": "DynamicRegisteredClient",
  2427. "clientId": client,
  2428. "enabled": True,
  2429. "publicClient": False,
  2430. "protocol": "openid-connect",
  2431. "secret": secret,
  2432. "clientAuthenticatorType": "client-secret",
  2433. },
  2434. )
  2435. assert res["clientId"] == client
  2436. new_secret = str(uuid.uuid4())
  2437. res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
  2438. assert res["secret"] == new_secret