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.

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