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.

2925 lines
104 KiB

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