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.

2900 lines
103 KiB

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