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.

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