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.

6202 lines
222 KiB

3 months ago
  1. """Test the keycloak admin object."""
  2. import copy
  3. import os
  4. import uuid
  5. from inspect import iscoroutinefunction, signature
  6. from typing import Tuple
  7. import freezegun
  8. import pytest
  9. from dateutil import parser as datetime_parser
  10. from packaging.version import Version
  11. import keycloak
  12. from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakOpenIDConnection
  13. from keycloak.connection import ConnectionManager
  14. from keycloak.exceptions import (
  15. KeycloakAuthenticationError,
  16. KeycloakDeleteError,
  17. KeycloakGetError,
  18. KeycloakPostError,
  19. KeycloakPutError,
  20. )
  21. CLIENT_NOT_FOUND_REGEX = '404: b\'{"error":"Client not found".*}\''
  22. CLIENT_SCOPE_NOT_FOUND_REGEX = '404: b\'{"error":"Client scope not found".*}\''
  23. COULD_NOT_FIND_ROLE_REGEX = '404: b\'{"error":"Could not find role".*}\''
  24. COULD_NOT_FIND_ROLE_WITH_ID_REGEX = '404: b\'{"error":"Could not find role with id".*}\''
  25. HTTP_404_REGEX = '404: b\'{"error":"HTTP 404 Not Found".*}\''
  26. ILLEGAL_EXECUTION_REGEX = '404: b\'{"error":"Illegal execution".*}\''
  27. NO_CLIENT_SCOPE_REGEX = '404: b\'{"error":"Could not find client scope".*}\''
  28. UNKOWN_ERROR_REGEX = 'b\'{"error":"unknown_error".*}\''
  29. USER_NOT_FOUND_REGEX = '404: b\'{"error":"User not found".*}\''
  30. def test_keycloak_version():
  31. """Test version."""
  32. assert keycloak.__version__, keycloak.__version__
  33. def test_keycloak_admin_init(env):
  34. """Test keycloak admin init.
  35. :param env: Environment fixture
  36. :type env: KeycloakTestEnv
  37. """
  38. admin = KeycloakAdmin(
  39. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  40. username=env.KEYCLOAK_ADMIN,
  41. password=env.KEYCLOAK_ADMIN_PASSWORD,
  42. )
  43. assert (
  44. admin.connection.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}"
  45. ), admin.connection.server_url
  46. assert admin.connection.realm_name == "master", admin.connection.realm_name
  47. assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
  48. assert admin.connection.client_id == "admin-cli", admin.connection.client_id
  49. assert admin.connection.client_secret_key is None, admin.connection.client_secret_key
  50. assert admin.connection.verify, admin.connection.verify
  51. assert admin.connection.username == env.KEYCLOAK_ADMIN, admin.connection.username
  52. assert admin.connection.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.connection.password
  53. assert admin.connection.totp is None, admin.connection.totp
  54. assert admin.connection.token is None, admin.connection.token
  55. assert admin.connection.user_realm_name is None, admin.connection.user_realm_name
  56. assert admin.connection.custom_headers is None, admin.connection.custom_headers
  57. admin = KeycloakAdmin(
  58. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  59. username=env.KEYCLOAK_ADMIN,
  60. password=env.KEYCLOAK_ADMIN_PASSWORD,
  61. realm_name=None,
  62. user_realm_name="master",
  63. )
  64. assert admin.connection.token is None
  65. admin = KeycloakAdmin(
  66. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  67. username=env.KEYCLOAK_ADMIN,
  68. password=env.KEYCLOAK_ADMIN_PASSWORD,
  69. realm_name=None,
  70. user_realm_name=None,
  71. )
  72. assert admin.connection.token is None
  73. admin.get_realms()
  74. token = admin.connection.token
  75. admin = KeycloakAdmin(
  76. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  77. token=token,
  78. realm_name=None,
  79. user_realm_name=None,
  80. )
  81. assert admin.connection.token == token
  82. admin.create_realm(payload={"realm": "authz", "enabled": True})
  83. admin.connection.realm_name = "authz"
  84. admin.create_client(
  85. payload={
  86. "name": "authz-client",
  87. "clientId": "authz-client",
  88. "authorizationServicesEnabled": True,
  89. "serviceAccountsEnabled": True,
  90. "clientAuthenticatorType": "client-secret",
  91. "directAccessGrantsEnabled": False,
  92. "enabled": True,
  93. "implicitFlowEnabled": False,
  94. "publicClient": False,
  95. }
  96. )
  97. secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client"))
  98. adminAuth = KeycloakAdmin(
  99. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  100. user_realm_name="authz",
  101. client_id="authz-client",
  102. client_secret_key=secret["value"],
  103. )
  104. adminAuth.connection.refresh_token()
  105. assert adminAuth.connection.token is not None
  106. admin.delete_realm(realm_name="authz")
  107. assert (
  108. KeycloakAdmin(
  109. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  110. username=None,
  111. password=None,
  112. client_secret_key=None,
  113. custom_headers={"custom": "header"},
  114. ).connection.token
  115. is None
  116. )
  117. keycloak_connection = KeycloakOpenIDConnection(
  118. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  119. username=env.KEYCLOAK_ADMIN,
  120. password=env.KEYCLOAK_ADMIN_PASSWORD,
  121. realm_name="master",
  122. client_id="admin-cli",
  123. verify=True,
  124. )
  125. keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
  126. keycloak_admin.connection.get_token()
  127. assert keycloak_admin.connection.token
  128. def test_realms(admin: KeycloakAdmin):
  129. """Test realms.
  130. :param admin: Keycloak Admin client
  131. :type admin: KeycloakAdmin
  132. """
  133. # Get realms
  134. realms = admin.get_realms()
  135. assert len(realms) == 1, realms
  136. assert "master" == realms[0]["realm"]
  137. # Create a test realm
  138. res = admin.create_realm(payload={"realm": "test"})
  139. assert res == b"", res
  140. # Create the same realm, should fail
  141. with pytest.raises(KeycloakPostError) as err:
  142. res = admin.create_realm(payload={"realm": "test"})
  143. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  144. # Create the same realm, skip_exists true
  145. res = admin.create_realm(payload={"realm": "test"}, skip_exists=True)
  146. assert res == {"msg": "Already exists"}, res
  147. # Get a single realm
  148. res = admin.get_realm(realm_name="test")
  149. assert res["realm"] == "test"
  150. # Get non-existing realm
  151. with pytest.raises(KeycloakGetError) as err:
  152. admin.get_realm(realm_name="non-existent")
  153. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  154. # Update realm
  155. res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"})
  156. assert res == dict(), res
  157. # Check that the update worked
  158. res = admin.get_realm(realm_name="test")
  159. assert res["realm"] == "test"
  160. assert res["accountTheme"] == "test"
  161. # Update wrong payload
  162. with pytest.raises(KeycloakPutError) as err:
  163. admin.update_realm(realm_name="test", payload={"wrong": "payload"})
  164. assert err.match('400: b\'{"error":"Unrecognized field')
  165. # Check that get realms returns both realms
  166. realms = admin.get_realms()
  167. realm_names = [x["realm"] for x in realms]
  168. assert len(realms) == 2, realms
  169. assert "master" in realm_names, realm_names
  170. assert "test" in realm_names, realm_names
  171. # Delete the realm
  172. res = admin.delete_realm(realm_name="test")
  173. assert res == dict(), res
  174. # Check that the realm does not exist anymore
  175. with pytest.raises(KeycloakGetError) as err:
  176. admin.get_realm(realm_name="test")
  177. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  178. # Delete non-existing realm
  179. with pytest.raises(KeycloakDeleteError) as err:
  180. admin.delete_realm(realm_name="non-existent")
  181. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  182. def test_changing_of_realms(admin: KeycloakAdmin, realm: str):
  183. """Test changing of realms.
  184. :param admin: Keycloak Admin client
  185. :type admin: KeycloakAdmin
  186. :param realm: Keycloak realm
  187. :type realm: str
  188. """
  189. assert admin.get_current_realm() == "master"
  190. admin.change_current_realm(realm)
  191. assert admin.get_current_realm() == realm
  192. def test_import_export_realms(admin: KeycloakAdmin, realm: str):
  193. """Test import and export of realms.
  194. :param admin: Keycloak Admin client
  195. :type admin: KeycloakAdmin
  196. :param realm: Keycloak realm
  197. :type realm: str
  198. """
  199. admin.change_current_realm(realm)
  200. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True)
  201. assert realm_export != dict(), realm_export
  202. admin.delete_realm(realm_name=realm)
  203. admin.realm_name = "master"
  204. res = admin.import_realm(payload=realm_export)
  205. assert res == b"", res
  206. # Test bad import
  207. with pytest.raises(KeycloakPostError) as err:
  208. admin.import_realm(payload=dict())
  209. assert err.match(
  210. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'' # noqa: E501
  211. )
  212. def test_partial_import_realm(admin: KeycloakAdmin, realm: str):
  213. """Test partial import of realm configuration.
  214. :param admin: Keycloak Admin client
  215. :type admin: KeycloakAdmin
  216. :param realm: Keycloak realm
  217. :type realm: str
  218. """
  219. test_realm_role = str(uuid.uuid4())
  220. test_user = str(uuid.uuid4())
  221. test_client = str(uuid.uuid4())
  222. admin.change_current_realm(realm)
  223. client_id = admin.create_client(payload={"name": test_client, "clientId": test_client})
  224. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=False)
  225. client_config = [
  226. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  227. ][0]
  228. # delete before partial import
  229. admin.delete_client(client_id)
  230. payload = {
  231. "ifResourceExists": "SKIP",
  232. "id": realm_export["id"],
  233. "realm": realm,
  234. "clients": [client_config],
  235. "roles": {"realm": [{"name": test_realm_role}]},
  236. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  237. }
  238. # check add
  239. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  240. assert res["added"] == 3
  241. # check skip
  242. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  243. assert res["skipped"] == 3
  244. # check overwrite
  245. payload["ifResourceExists"] = "OVERWRITE"
  246. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  247. assert res["overwritten"] == 3
  248. def test_users(admin: KeycloakAdmin, realm: str):
  249. """Test users.
  250. :param admin: Keycloak Admin client
  251. :type admin: KeycloakAdmin
  252. :param realm: Keycloak realm
  253. :type realm: str
  254. """
  255. admin.change_current_realm(realm)
  256. # Check no users present
  257. users = admin.get_users()
  258. assert users == list(), users
  259. # Test create user
  260. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  261. assert user_id is not None, user_id
  262. # Test create the same user
  263. with pytest.raises(KeycloakPostError) as err:
  264. admin.create_user(payload={"username": "test", "email": "test@test.test"})
  265. assert err.match(".*User exists with same.*")
  266. # Test create the same user, exists_ok true
  267. user_id_2 = admin.create_user(
  268. payload={"username": "test", "email": "test@test.test"}, exist_ok=True
  269. )
  270. assert user_id == user_id_2
  271. # Test get user
  272. user = admin.get_user(user_id=user_id)
  273. assert user["username"] == "test", user["username"]
  274. assert user["email"] == "test@test.test", user["email"]
  275. # Test update user
  276. res = admin.update_user(user_id=user_id, payload={"firstName": "Test"})
  277. assert res == dict(), res
  278. user = admin.get_user(user_id=user_id)
  279. assert user["firstName"] == "Test"
  280. # Test update user fail
  281. with pytest.raises(KeycloakPutError) as err:
  282. admin.update_user(user_id=user_id, payload={"wrong": "payload"})
  283. assert err.match('400: b\'{"error":"Unrecognized field')
  284. # Test disable user
  285. res = admin.disable_user(user_id=user_id)
  286. assert res == {}, res
  287. assert not admin.get_user(user_id=user_id)["enabled"]
  288. # Test enable user
  289. res = admin.enable_user(user_id=user_id)
  290. assert res == {}, res
  291. assert admin.get_user(user_id=user_id)["enabled"]
  292. # Test get users again
  293. users = admin.get_users()
  294. usernames = [x["username"] for x in users]
  295. assert "test" in usernames
  296. # Test users counts
  297. count = admin.users_count()
  298. assert count == 1, count
  299. # Test users count with query
  300. count = admin.users_count(query={"username": "notpresent"})
  301. assert count == 0
  302. # Test user groups
  303. groups = admin.get_user_groups(user_id=user["id"])
  304. assert len(groups) == 0
  305. # Test user groups bad id
  306. with pytest.raises(KeycloakGetError) as err:
  307. admin.get_user_groups(user_id="does-not-exist")
  308. assert err.match(USER_NOT_FOUND_REGEX)
  309. # Test logout
  310. res = admin.user_logout(user_id=user["id"])
  311. assert res == dict(), res
  312. # Test logout fail
  313. with pytest.raises(KeycloakPostError) as err:
  314. admin.user_logout(user_id="non-existent-id")
  315. assert err.match(USER_NOT_FOUND_REGEX)
  316. # Test consents
  317. res = admin.user_consents(user_id=user["id"])
  318. assert len(res) == 0, res
  319. # Test consents fail
  320. with pytest.raises(KeycloakGetError) as err:
  321. admin.user_consents(user_id="non-existent-id")
  322. assert err.match(USER_NOT_FOUND_REGEX)
  323. # Test delete user
  324. res = admin.delete_user(user_id=user_id)
  325. assert res == dict(), res
  326. with pytest.raises(KeycloakGetError) as err:
  327. admin.get_user(user_id=user_id)
  328. err.match(USER_NOT_FOUND_REGEX)
  329. # Test delete fail
  330. with pytest.raises(KeycloakDeleteError) as err:
  331. admin.delete_user(user_id="non-existent-id")
  332. assert err.match(USER_NOT_FOUND_REGEX)
  333. def test_enable_disable_all_users(admin: KeycloakAdmin, realm: str):
  334. """Test enable and disable all users.
  335. :param admin: Keycloak Admin client
  336. :type admin: KeycloakAdmin
  337. :param realm: Keycloak realm
  338. :type realm: str
  339. """
  340. admin.change_current_realm(realm)
  341. user_id_1 = admin.create_user(
  342. payload={"username": "test", "email": "test@test.test", "enabled": True}
  343. )
  344. user_id_2 = admin.create_user(
  345. payload={"username": "test2", "email": "test2@test.test", "enabled": True}
  346. )
  347. user_id_3 = admin.create_user(
  348. payload={"username": "test3", "email": "test3@test.test", "enabled": True}
  349. )
  350. assert admin.get_user(user_id_1)["enabled"]
  351. assert admin.get_user(user_id_2)["enabled"]
  352. assert admin.get_user(user_id_3)["enabled"]
  353. admin.disable_all_users()
  354. assert not admin.get_user(user_id_1)["enabled"]
  355. assert not admin.get_user(user_id_2)["enabled"]
  356. assert not admin.get_user(user_id_3)["enabled"]
  357. admin.enable_all_users()
  358. assert admin.get_user(user_id_1)["enabled"]
  359. assert admin.get_user(user_id_2)["enabled"]
  360. assert admin.get_user(user_id_3)["enabled"]
  361. def test_users_roles(admin: KeycloakAdmin, realm: str):
  362. """Test users roles.
  363. :param admin: Keycloak Admin client
  364. :type admin: KeycloakAdmin
  365. :param realm: Keycloak realm
  366. :type realm: str
  367. """
  368. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  369. # Test all level user roles
  370. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  371. admin.create_client_role(client_role_id=client_id, payload={"name": "test-role"})
  372. admin.assign_client_role(
  373. client_id=client_id,
  374. user_id=user_id,
  375. roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
  376. )
  377. all_roles = admin.get_all_roles_of_user(user_id=user_id)
  378. realm_roles = all_roles["realmMappings"]
  379. assert len(realm_roles) == 1, realm_roles
  380. client_roles = all_roles["clientMappings"]
  381. assert len(client_roles) == 1, client_roles
  382. # Test all level user roles fail
  383. with pytest.raises(KeycloakGetError) as err:
  384. admin.get_all_roles_of_user(user_id="non-existent-id")
  385. err.match('404: b\'{"error":"User not found"')
  386. admin.delete_user(user_id)
  387. admin.delete_client(client_id)
  388. def test_users_pagination(admin: KeycloakAdmin, realm: str):
  389. """Test user pagination.
  390. :param admin: Keycloak Admin client
  391. :type admin: KeycloakAdmin
  392. :param realm: Keycloak realm
  393. :type realm: str
  394. """
  395. admin.change_current_realm(realm)
  396. for ind in range(admin.PAGE_SIZE + 50):
  397. username = f"user_{ind}"
  398. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  399. users = admin.get_users()
  400. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  401. users = admin.get_users(query={"first": 100})
  402. assert len(users) == 50, len(users)
  403. users = admin.get_users(query={"max": 20})
  404. assert len(users) == 20, len(users)
  405. def test_user_groups_pagination(admin: KeycloakAdmin, realm: str):
  406. """Test user groups pagination.
  407. :param admin: Keycloak Admin client
  408. :type admin: KeycloakAdmin
  409. :param realm: Keycloak realm
  410. :type realm: str
  411. """
  412. admin.change_current_realm(realm)
  413. user_id = admin.create_user(
  414. payload={"username": "username_1", "email": "username_1@test.test"}
  415. )
  416. for ind in range(admin.PAGE_SIZE + 50):
  417. group_name = f"group_{ind}"
  418. group_id = admin.create_group(payload={"name": group_name})
  419. admin.group_user_add(user_id=user_id, group_id=group_id)
  420. groups = admin.get_user_groups(user_id=user_id)
  421. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  422. groups = admin.get_user_groups(user_id=user_id, query={"first": 100, "max": -1, "search": ""})
  423. assert len(groups) == 50, len(groups)
  424. groups = admin.get_user_groups(user_id=user_id, query={"max": 20, "first": -1, "search": ""})
  425. assert len(groups) == 20, len(groups)
  426. def test_idps(admin: KeycloakAdmin, realm: str):
  427. """Test IDPs.
  428. :param admin: Keycloak Admin client
  429. :type admin: KeycloakAdmin
  430. :param realm: Keycloak realm
  431. :type realm: str
  432. """
  433. admin.change_current_realm(realm)
  434. # Create IDP
  435. res = admin.create_idp(
  436. payload=dict(
  437. providerId="github", alias="github", config=dict(clientId="test", clientSecret="test")
  438. )
  439. )
  440. assert res == b"", res
  441. # Test create idp fail
  442. with pytest.raises(KeycloakPostError) as err:
  443. admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  444. assert err.match("Invalid identity provider id"), err
  445. # Test listing
  446. idps = admin.get_idps()
  447. assert len(idps) == 1
  448. assert "github" == idps[0]["alias"]
  449. # Test get idp
  450. idp = admin.get_idp("github")
  451. assert "github" == idp["alias"]
  452. assert idp.get("config")
  453. assert "test" == idp["config"]["clientId"]
  454. assert "**********" == idp["config"]["clientSecret"]
  455. # Test get idp fail
  456. with pytest.raises(KeycloakGetError) as err:
  457. admin.get_idp("does-not-exist")
  458. assert err.match(HTTP_404_REGEX)
  459. # Test IdP update
  460. res = admin.update_idp(idp_alias="github", payload=idps[0])
  461. assert res == {}, res
  462. # Test adding a mapper
  463. res = admin.add_mapper_to_idp(
  464. idp_alias="github",
  465. payload={
  466. "identityProviderAlias": "github",
  467. "identityProviderMapper": "github-user-attribute-mapper",
  468. "name": "test",
  469. },
  470. )
  471. assert res == b"", res
  472. # Test mapper fail
  473. with pytest.raises(KeycloakPostError) as err:
  474. admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
  475. assert err.match(HTTP_404_REGEX)
  476. # Test IdP mappers listing
  477. idp_mappers = admin.get_idp_mappers(idp_alias="github")
  478. assert len(idp_mappers) == 1
  479. # Test IdP mapper update
  480. res = admin.update_mapper_in_idp(
  481. idp_alias="github",
  482. mapper_id=idp_mappers[0]["id"],
  483. # For an obscure reason, keycloak expect all fields
  484. payload={
  485. "id": idp_mappers[0]["id"],
  486. "identityProviderAlias": "github-alias",
  487. "identityProviderMapper": "github-user-attribute-mapper",
  488. "name": "test",
  489. "config": idp_mappers[0]["config"],
  490. },
  491. )
  492. assert res == dict(), res
  493. # Test delete
  494. res = admin.delete_idp(idp_alias="github")
  495. assert res == dict(), res
  496. # Test delete fail
  497. with pytest.raises(KeycloakDeleteError) as err:
  498. admin.delete_idp(idp_alias="does-not-exist")
  499. assert err.match(HTTP_404_REGEX)
  500. def test_user_credentials(admin: KeycloakAdmin, user: str):
  501. """Test user credentials.
  502. :param admin: Keycloak Admin client
  503. :type admin: KeycloakAdmin
  504. :param user: Keycloak user
  505. :type user: str
  506. """
  507. res = admin.set_user_password(user_id=user, password="booya", temporary=True)
  508. assert res == dict(), res
  509. # Test user password set fail
  510. with pytest.raises(KeycloakPutError) as err:
  511. admin.set_user_password(user_id="does-not-exist", password="")
  512. assert err.match(USER_NOT_FOUND_REGEX)
  513. credentials = admin.get_credentials(user_id=user)
  514. assert len(credentials) == 1
  515. assert credentials[0]["type"] == "password", credentials
  516. # Test get credentials fail
  517. with pytest.raises(KeycloakGetError) as err:
  518. admin.get_credentials(user_id="does-not-exist")
  519. assert err.match(USER_NOT_FOUND_REGEX)
  520. res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"])
  521. assert res == dict(), res
  522. # Test delete fail
  523. with pytest.raises(KeycloakDeleteError) as err:
  524. admin.delete_credential(user_id=user, credential_id="does-not-exist")
  525. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  526. def test_social_logins(admin: KeycloakAdmin, user: str):
  527. """Test social logins.
  528. :param admin: Keycloak Admin client
  529. :type admin: KeycloakAdmin
  530. :param user: Keycloak user
  531. :type user: str
  532. """
  533. res = admin.add_user_social_login(
  534. user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
  535. )
  536. assert res == dict(), res
  537. admin.add_user_social_login(
  538. user_id=user, provider_id="github", provider_userid="test", provider_username="test"
  539. )
  540. assert res == dict(), res
  541. # Test add social login fail
  542. with pytest.raises(KeycloakPostError) as err:
  543. admin.add_user_social_login(
  544. user_id="does-not-exist",
  545. provider_id="does-not-exist",
  546. provider_userid="test",
  547. provider_username="test",
  548. )
  549. assert err.match(USER_NOT_FOUND_REGEX)
  550. res = admin.get_user_social_logins(user_id=user)
  551. assert res == list(), res
  552. # Test get social logins fail
  553. with pytest.raises(KeycloakGetError) as err:
  554. admin.get_user_social_logins(user_id="does-not-exist")
  555. assert err.match(USER_NOT_FOUND_REGEX)
  556. res = admin.delete_user_social_login(user_id=user, provider_id="gitlab")
  557. assert res == {}, res
  558. res = admin.delete_user_social_login(user_id=user, provider_id="github")
  559. assert res == {}, res
  560. with pytest.raises(KeycloakDeleteError) as err:
  561. admin.delete_user_social_login(user_id=user, provider_id="instagram")
  562. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  563. def test_server_info(admin: KeycloakAdmin):
  564. """Test server info.
  565. :param admin: Keycloak Admin client
  566. :type admin: KeycloakAdmin
  567. """
  568. info = admin.get_server_info()
  569. assert set(info.keys()).issubset(
  570. {
  571. "systemInfo",
  572. "memoryInfo",
  573. "profileInfo",
  574. "features",
  575. "themes",
  576. "socialProviders",
  577. "identityProviders",
  578. "providers",
  579. "protocolMapperTypes",
  580. "builtinProtocolMappers",
  581. "clientInstallations",
  582. "componentTypes",
  583. "passwordPolicies",
  584. "enums",
  585. "cryptoInfo",
  586. "features",
  587. }
  588. ), info.keys()
  589. def test_groups(admin: KeycloakAdmin, user: str):
  590. """Test groups.
  591. :param admin: Keycloak Admin client
  592. :type admin: KeycloakAdmin
  593. :param user: Keycloak user
  594. :type user: str
  595. """
  596. # Test get groups
  597. groups = admin.get_groups()
  598. assert len(groups) == 0
  599. # Test create group
  600. group_id = admin.create_group(payload={"name": "main-group"})
  601. assert group_id is not None, group_id
  602. # Test group count
  603. count = admin.groups_count()
  604. assert count.get("count") == 1, count
  605. # Test group count with query
  606. count = admin.groups_count(query={"search": "notpresent"})
  607. assert count.get("count") == 0
  608. # Test create subgroups
  609. subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  610. subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id)
  611. # Test create group fail
  612. with pytest.raises(KeycloakPostError) as err:
  613. admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  614. assert err.match("409"), err
  615. # Test skip exists OK
  616. subgroup_id_1_eq = admin.create_group(
  617. payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
  618. )
  619. assert subgroup_id_1_eq is None
  620. # Test get groups again
  621. groups = admin.get_groups()
  622. assert len(groups) == 1, groups
  623. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  624. assert groups[0]["id"] == group_id
  625. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  626. # Test get groups query
  627. groups = admin.get_groups(query={"max": 10})
  628. assert len(groups) == 1, groups
  629. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  630. assert groups[0]["id"] == group_id
  631. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  632. # Test get group
  633. res = admin.get_group(group_id=subgroup_id_1)
  634. assert res["id"] == subgroup_id_1, res
  635. assert res["name"] == "subgroup-1"
  636. assert res["path"] == "/main-group/subgroup-1"
  637. # Test get group fail
  638. with pytest.raises(KeycloakGetError) as err:
  639. admin.get_group(group_id="does-not-exist")
  640. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  641. # Create 1 more subgroup
  642. subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2)
  643. main_group = admin.get_group(group_id=group_id)
  644. # Test nested searches
  645. subgroup_2 = admin.get_group(group_id=subgroup_id_2)
  646. res = admin.get_subgroups(group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1")
  647. assert res is not None, res
  648. assert res["id"] == subsubgroup_id_1
  649. # Test nested search from main group
  650. res = admin.get_subgroups(
  651. group=admin.get_group(group_id=group_id, full_hierarchy=True),
  652. path="/main-group/subgroup-2/subsubgroup-1",
  653. )
  654. assert res["id"] == subsubgroup_id_1
  655. # Test nested search from all groups
  656. res = admin.get_groups(full_hierarchy=True)
  657. assert len(res) == 1
  658. assert len(res[0]["subGroups"]) == 2
  659. assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1][0]["subGroups"]) == 0
  660. assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2][0]["subGroups"]) == 1
  661. # Test that query params are not allowed for full hierarchy
  662. with pytest.raises(ValueError) as err:
  663. admin.get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
  664. # Test that query params are passed
  665. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  666. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"]
  667. ) >= Version("23"):
  668. res = admin.get_group_children(group_id=group_id, query={"max": 1})
  669. assert len(res) == 1
  670. assert err.match("Cannot use both query and full_hierarchy parameters")
  671. main_group_id_2 = admin.create_group(payload={"name": "main-group-2"})
  672. assert len(admin.get_groups(full_hierarchy=True)) == 2
  673. # Test empty search
  674. res = admin.get_subgroups(group=main_group, path="/none")
  675. assert res is None, res
  676. # Test get group by path
  677. res = admin.get_group_by_path(path="/main-group/subgroup-1")
  678. assert res is not None, res
  679. assert res["id"] == subgroup_id_1, res
  680. with pytest.raises(KeycloakGetError) as err:
  681. admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  682. assert err.match('404: b\'{"error":"Group path does not exist".*}\'')
  683. res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  684. assert res is not None, res
  685. assert res["id"] == subsubgroup_id_1
  686. res = admin.get_group_by_path(path="/main-group")
  687. assert res is not None, res
  688. assert res["id"] == group_id, res
  689. # Test group members
  690. res = admin.get_group_members(group_id=subgroup_id_2)
  691. assert len(res) == 0, res
  692. # Test fail group members
  693. with pytest.raises(KeycloakGetError) as err:
  694. admin.get_group_members(group_id="does-not-exist")
  695. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  696. res = admin.group_user_add(user_id=user, group_id=subgroup_id_2)
  697. assert res == dict(), res
  698. res = admin.get_group_members(group_id=subgroup_id_2)
  699. assert len(res) == 1, res
  700. assert res[0]["id"] == user
  701. # Test get group members query
  702. res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10})
  703. assert len(res) == 1, res
  704. assert res[0]["id"] == user
  705. with pytest.raises(KeycloakDeleteError) as err:
  706. admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  707. assert err.match(USER_NOT_FOUND_REGEX), err
  708. res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2)
  709. assert res == dict(), res
  710. # Test set permissions
  711. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True)
  712. assert res["enabled"], res
  713. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False)
  714. assert not res["enabled"], res
  715. with pytest.raises(KeycloakPutError) as err:
  716. admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  717. assert err.match(UNKOWN_ERROR_REGEX), err
  718. # Test update group
  719. res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  720. assert res == dict(), res
  721. assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2"
  722. # test update fail
  723. with pytest.raises(KeycloakPutError) as err:
  724. admin.update_group(group_id="does-not-exist", payload=dict())
  725. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  726. # Test delete
  727. res = admin.delete_group(group_id=group_id)
  728. assert res == dict(), res
  729. res = admin.delete_group(group_id=main_group_id_2)
  730. assert res == dict(), res
  731. assert len(admin.get_groups()) == 0
  732. # Test delete fail
  733. with pytest.raises(KeycloakDeleteError) as err:
  734. admin.delete_group(group_id="does-not-exist")
  735. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  736. def test_clients(admin: KeycloakAdmin, realm: str):
  737. """Test clients.
  738. :param admin: Keycloak Admin client
  739. :type admin: KeycloakAdmin
  740. :param realm: Keycloak realm
  741. :type realm: str
  742. """
  743. admin.change_current_realm(realm)
  744. # Test get clients
  745. clients = admin.get_clients()
  746. assert len(clients) == 6, clients
  747. assert {x["name"] for x in clients} == set(
  748. [
  749. "${client_admin-cli}",
  750. "${client_security-admin-console}",
  751. "${client_account-console}",
  752. "${client_broker}",
  753. "${client_account}",
  754. "${client_realm-management}",
  755. ]
  756. ), clients
  757. # Test create client
  758. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  759. assert client_id, client_id
  760. with pytest.raises(KeycloakPostError) as err:
  761. admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  762. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  763. client_id_2 = admin.create_client(
  764. payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True
  765. )
  766. assert client_id == client_id_2, client_id_2
  767. # Test get client
  768. res = admin.get_client(client_id=client_id)
  769. assert res["clientId"] == "test-client", res
  770. assert res["name"] == "test-client", res
  771. assert res["id"] == client_id, res
  772. with pytest.raises(KeycloakGetError) as err:
  773. admin.get_client(client_id="does-not-exist")
  774. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  775. assert len(admin.get_clients()) == 7
  776. # Test get client id
  777. assert admin.get_client_id(client_id="test-client") == client_id
  778. assert admin.get_client_id(client_id="does-not-exist") is None
  779. # Test update client
  780. res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"})
  781. assert res == dict(), res
  782. with pytest.raises(KeycloakPutError) as err:
  783. admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"})
  784. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  785. # Test client mappers
  786. res = admin.get_mappers_from_client(client_id=client_id)
  787. assert len(res) == 0
  788. with pytest.raises(KeycloakPostError) as err:
  789. admin.add_mapper_to_client(client_id="does-not-exist", payload=dict())
  790. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  791. res = admin.add_mapper_to_client(
  792. client_id=client_id,
  793. payload={
  794. "name": "test-mapper",
  795. "protocol": "openid-connect",
  796. "protocolMapper": "oidc-usermodel-attribute-mapper",
  797. },
  798. )
  799. assert res == b""
  800. assert len(admin.get_mappers_from_client(client_id=client_id)) == 1
  801. mapper = admin.get_mappers_from_client(client_id=client_id)[0]
  802. with pytest.raises(KeycloakPutError) as err:
  803. admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload=dict())
  804. assert err.match('404: b\'{"error":"Model not found".*}\'')
  805. mapper["config"]["user.attribute"] = "test"
  806. res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper)
  807. assert res == dict()
  808. res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  809. assert res == dict()
  810. with pytest.raises(KeycloakDeleteError) as err:
  811. admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  812. assert err.match('404: b\'{"error":"Model not found".*}\'')
  813. # Test client sessions
  814. with pytest.raises(KeycloakGetError) as err:
  815. admin.get_client_all_sessions(client_id="does-not-exist")
  816. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  817. assert admin.get_client_all_sessions(client_id=client_id) == list()
  818. assert admin.get_client_sessions_stats() == list()
  819. # Test authz
  820. auth_client_id = admin.create_client(
  821. payload={
  822. "name": "authz-client",
  823. "clientId": "authz-client",
  824. "authorizationServicesEnabled": True,
  825. "serviceAccountsEnabled": True,
  826. }
  827. )
  828. res = admin.get_client_authz_settings(client_id=auth_client_id)
  829. assert res["allowRemoteResourceManagement"]
  830. assert res["decisionStrategy"] == "UNANIMOUS"
  831. assert len(res["policies"]) >= 0
  832. with pytest.raises(KeycloakGetError) as err:
  833. admin.get_client_authz_settings(client_id=client_id)
  834. assert err.match(HTTP_404_REGEX)
  835. # Authz resources
  836. res = admin.get_client_authz_resources(client_id=auth_client_id)
  837. assert len(res) == 1
  838. assert res[0]["name"] == "Default Resource"
  839. with pytest.raises(KeycloakGetError) as err:
  840. admin.get_client_authz_resources(client_id=client_id)
  841. assert err.match(HTTP_404_REGEX)
  842. res = admin.create_client_authz_resource(
  843. client_id=auth_client_id, payload={"name": "test-resource"}
  844. )
  845. assert res["name"] == "test-resource", res
  846. test_resource_id = res["_id"]
  847. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=test_resource_id)
  848. assert res["_id"] == test_resource_id, res
  849. assert res["name"] == "test-resource", res
  850. with pytest.raises(KeycloakPostError) as err:
  851. admin.create_client_authz_resource(
  852. client_id=auth_client_id, payload={"name": "test-resource"}
  853. )
  854. assert err.match('409: b\'{"error":"invalid_request"')
  855. assert admin.create_client_authz_resource(
  856. client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
  857. ) == {"msg": "Already exists"}
  858. res = admin.get_client_authz_resources(client_id=auth_client_id)
  859. assert len(res) == 2
  860. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  861. res = admin.create_client_authz_resource(
  862. client_id=auth_client_id, payload={"name": "temp-resource"}
  863. )
  864. assert res["name"] == "temp-resource", res
  865. temp_resource_id: str = res["_id"]
  866. # Test update authz resources
  867. admin.update_client_authz_resource(
  868. client_id=auth_client_id,
  869. resource_id=temp_resource_id,
  870. payload={"name": "temp-updated-resource"},
  871. )
  872. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  873. assert res["name"] == "temp-updated-resource", res
  874. with pytest.raises(KeycloakPutError) as err:
  875. admin.update_client_authz_resource(
  876. client_id=auth_client_id,
  877. resource_id="invalid_resource_id",
  878. payload={"name": "temp-updated-resource"},
  879. )
  880. assert err.match("404: b''"), err
  881. admin.delete_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  882. with pytest.raises(KeycloakGetError) as err:
  883. admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  884. assert err.match("404: b''")
  885. # Authz policies
  886. res = admin.get_client_authz_policies(client_id=auth_client_id)
  887. assert len(res) == 1, res
  888. assert res[0]["name"] == "Default Policy"
  889. with pytest.raises(KeycloakGetError) as err:
  890. admin.get_client_authz_policies(client_id="does-not-exist")
  891. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  892. role_id = admin.get_realm_role(role_name="offline_access")["id"]
  893. res = admin.create_client_authz_role_based_policy(
  894. client_id=auth_client_id,
  895. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  896. )
  897. assert res["name"] == "test-authz-rb-policy", res
  898. with pytest.raises(KeycloakPostError) as err:
  899. admin.create_client_authz_role_based_policy(
  900. client_id=auth_client_id,
  901. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  902. )
  903. assert err.match('409: b\'{"error":"Policy with name')
  904. assert admin.create_client_authz_role_based_policy(
  905. client_id=auth_client_id,
  906. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  907. skip_exists=True,
  908. ) == {"msg": "Already exists"}
  909. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2
  910. res = admin.create_client_authz_role_based_policy(
  911. client_id=auth_client_id,
  912. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  913. )
  914. res2 = admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  915. assert res["id"] == res2["id"]
  916. admin.delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  917. with pytest.raises(KeycloakGetError) as err:
  918. admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  919. assert err.match("404: b''")
  920. res = admin.create_client_authz_policy(
  921. client_id=auth_client_id,
  922. payload={
  923. "name": "test-authz-policy",
  924. "type": "time",
  925. "config": {"hourEnd": "18", "hour": "9"},
  926. },
  927. )
  928. assert res["name"] == "test-authz-policy", res
  929. with pytest.raises(KeycloakPostError) as err:
  930. admin.create_client_authz_policy(
  931. client_id=auth_client_id,
  932. payload={
  933. "name": "test-authz-policy",
  934. "type": "time",
  935. "config": {"hourEnd": "18", "hour": "9"},
  936. },
  937. )
  938. assert err.match('409: b\'{"error":"Policy with name')
  939. assert admin.create_client_authz_policy(
  940. client_id=auth_client_id,
  941. payload={
  942. "name": "test-authz-policy",
  943. "type": "time",
  944. "config": {"hourEnd": "18", "hour": "9"},
  945. },
  946. skip_exists=True,
  947. ) == {"msg": "Already exists"}
  948. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 3
  949. # Test authz permissions
  950. res = admin.get_client_authz_permissions(client_id=auth_client_id)
  951. assert len(res) == 1, res
  952. assert res[0]["name"] == "Default Permission"
  953. with pytest.raises(KeycloakGetError) as err:
  954. admin.get_client_authz_permissions(client_id="does-not-exist")
  955. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  956. res = admin.create_client_authz_resource_based_permission(
  957. client_id=auth_client_id,
  958. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  959. )
  960. assert res, res
  961. assert res["name"] == "test-permission-rb"
  962. assert res["resources"] == [test_resource_id]
  963. with pytest.raises(KeycloakPostError) as err:
  964. admin.create_client_authz_resource_based_permission(
  965. client_id=auth_client_id,
  966. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  967. )
  968. assert err.match('409: b\'{"error":"Policy with name')
  969. assert admin.create_client_authz_resource_based_permission(
  970. client_id=auth_client_id,
  971. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  972. skip_exists=True,
  973. ) == {"msg": "Already exists"}
  974. assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2
  975. # Test authz scopes
  976. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  977. assert len(res) == 0, res
  978. with pytest.raises(KeycloakGetError) as err:
  979. admin.get_client_authz_scopes(client_id=client_id)
  980. assert err.match(HTTP_404_REGEX)
  981. res = admin.create_client_authz_scopes(
  982. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  983. )
  984. assert res["name"] == "test-authz-scope", res
  985. with pytest.raises(KeycloakPostError) as err:
  986. admin.create_client_authz_scopes(
  987. client_id="invalid_client_id", payload={"name": "test-authz-scope"}
  988. )
  989. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  990. assert admin.create_client_authz_scopes(
  991. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  992. )
  993. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  994. assert len(res) == 1
  995. assert {x["name"] for x in res} == {"test-authz-scope"}
  996. # Test service account user
  997. res = admin.get_client_service_account_user(client_id=auth_client_id)
  998. assert res["username"] == "service-account-authz-client", res
  999. with pytest.raises(KeycloakGetError) as err:
  1000. admin.get_client_service_account_user(client_id=client_id)
  1001. assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
  1002. UNKOWN_ERROR_REGEX
  1003. )
  1004. # Test delete client
  1005. res = admin.delete_client(client_id=auth_client_id)
  1006. assert res == dict(), res
  1007. with pytest.raises(KeycloakDeleteError) as err:
  1008. admin.delete_client(client_id=auth_client_id)
  1009. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1010. # Test client credentials
  1011. admin.create_client(
  1012. payload={
  1013. "name": "test-confidential",
  1014. "enabled": True,
  1015. "protocol": "openid-connect",
  1016. "publicClient": False,
  1017. "redirectUris": ["http://localhost/*"],
  1018. "webOrigins": ["+"],
  1019. "clientId": "test-confidential",
  1020. "secret": "test-secret",
  1021. "clientAuthenticatorType": "client-secret",
  1022. }
  1023. )
  1024. with pytest.raises(KeycloakGetError) as err:
  1025. admin.get_client_secrets(client_id="does-not-exist")
  1026. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1027. secrets = admin.get_client_secrets(
  1028. client_id=admin.get_client_id(client_id="test-confidential")
  1029. )
  1030. assert secrets == {"type": "secret", "value": "test-secret"}
  1031. with pytest.raises(KeycloakPostError) as err:
  1032. admin.generate_client_secrets(client_id="does-not-exist")
  1033. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1034. res = admin.generate_client_secrets(
  1035. client_id=admin.get_client_id(client_id="test-confidential")
  1036. )
  1037. assert res
  1038. assert (
  1039. admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
  1040. == res
  1041. )
  1042. def test_realm_roles(admin: KeycloakAdmin, realm: str):
  1043. """Test realm roles.
  1044. :param admin: Keycloak Admin client
  1045. :type admin: KeycloakAdmin
  1046. :param realm: Keycloak realm
  1047. :type realm: str
  1048. """
  1049. admin.change_current_realm(realm)
  1050. # Test get realm roles
  1051. roles = admin.get_realm_roles()
  1052. assert len(roles) == 3, roles
  1053. role_names = [x["name"] for x in roles]
  1054. assert "uma_authorization" in role_names, role_names
  1055. assert "offline_access" in role_names, role_names
  1056. # Test get realm roles with search text
  1057. searched_roles = admin.get_realm_roles(search_text="uma_a")
  1058. searched_role_names = [x["name"] for x in searched_roles]
  1059. assert "uma_authorization" in searched_role_names, searched_role_names
  1060. assert "offline_access" not in searched_role_names, searched_role_names
  1061. # Test empty members
  1062. with pytest.raises(KeycloakGetError) as err:
  1063. admin.get_realm_role_members(role_name="does-not-exist")
  1064. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1065. members = admin.get_realm_role_members(role_name="offline_access")
  1066. assert members == list(), members
  1067. # Test create realm role
  1068. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1069. assert role_id, role_id
  1070. with pytest.raises(KeycloakPostError) as err:
  1071. admin.create_realm_role(payload={"name": "test-realm-role"})
  1072. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  1073. role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1074. assert role_id == role_id_2
  1075. # Test get realm role by its id
  1076. role_id = admin.get_realm_role(role_name="test-realm-role")["id"]
  1077. res = admin.get_realm_role_by_id(role_id)
  1078. assert res["name"] == "test-realm-role"
  1079. # Test update realm role
  1080. res = admin.update_realm_role(
  1081. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  1082. )
  1083. assert res == dict(), res
  1084. with pytest.raises(KeycloakPutError) as err:
  1085. admin.update_realm_role(
  1086. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  1087. )
  1088. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1089. # Test realm role user assignment
  1090. user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"})
  1091. with pytest.raises(KeycloakPostError) as err:
  1092. admin.assign_realm_roles(user_id=user_id, roles=["bad"])
  1093. assert err.match(UNKOWN_ERROR_REGEX), err
  1094. res = admin.assign_realm_roles(
  1095. user_id=user_id,
  1096. roles=[
  1097. admin.get_realm_role(role_name="offline_access"),
  1098. admin.get_realm_role(role_name="test-realm-role-update"),
  1099. ],
  1100. )
  1101. assert res == dict(), res
  1102. assert admin.get_user(user_id=user_id)["username"] in [
  1103. x["username"] for x in admin.get_realm_role_members(role_name="offline_access")
  1104. ]
  1105. assert admin.get_user(user_id=user_id)["username"] in [
  1106. x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update")
  1107. ]
  1108. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1109. assert len(roles) == 3
  1110. assert "offline_access" in [x["name"] for x in roles]
  1111. assert "test-realm-role-update" in [x["name"] for x in roles]
  1112. with pytest.raises(KeycloakDeleteError) as err:
  1113. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  1114. assert err.match(UNKOWN_ERROR_REGEX), err
  1115. res = admin.delete_realm_roles_of_user(
  1116. user_id=user_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1117. )
  1118. assert res == dict(), res
  1119. assert admin.get_realm_role_members(role_name="offline_access") == list()
  1120. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1121. assert len(roles) == 2
  1122. assert "offline_access" not in [x["name"] for x in roles]
  1123. assert "test-realm-role-update" in [x["name"] for x in roles]
  1124. roles = admin.get_available_realm_roles_of_user(user_id=user_id)
  1125. assert len(roles) == 2
  1126. assert "offline_access" in [x["name"] for x in roles]
  1127. assert "uma_authorization" in [x["name"] for x in roles]
  1128. # Test realm role group assignment
  1129. group_id = admin.create_group(payload={"name": "test-group"})
  1130. with pytest.raises(KeycloakPostError) as err:
  1131. admin.assign_group_realm_roles(group_id=group_id, roles=["bad"])
  1132. assert err.match(UNKOWN_ERROR_REGEX), err
  1133. res = admin.assign_group_realm_roles(
  1134. group_id=group_id,
  1135. roles=[
  1136. admin.get_realm_role(role_name="offline_access"),
  1137. admin.get_realm_role(role_name="test-realm-role-update"),
  1138. ],
  1139. )
  1140. assert res == dict(), res
  1141. roles = admin.get_group_realm_roles(group_id=group_id)
  1142. assert len(roles) == 2
  1143. assert "offline_access" in [x["name"] for x in roles]
  1144. assert "test-realm-role-update" in [x["name"] for x in roles]
  1145. with pytest.raises(KeycloakDeleteError) as err:
  1146. admin.delete_group_realm_roles(group_id=group_id, roles=["bad"])
  1147. assert err.match(UNKOWN_ERROR_REGEX)
  1148. res = admin.delete_group_realm_roles(
  1149. group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1150. )
  1151. assert res == dict(), res
  1152. roles = admin.get_group_realm_roles(group_id=group_id)
  1153. assert len(roles) == 1
  1154. assert "test-realm-role-update" in [x["name"] for x in roles]
  1155. # Test composite realm roles
  1156. composite_role = admin.create_realm_role(payload={"name": "test-composite-role"})
  1157. with pytest.raises(KeycloakPostError) as err:
  1158. admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1159. assert err.match(UNKOWN_ERROR_REGEX), err
  1160. res = admin.add_composite_realm_roles_to_role(
  1161. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1162. )
  1163. assert res == dict(), res
  1164. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1165. assert len(res) == 1
  1166. assert "test-realm-role-update" in res[0]["name"]
  1167. with pytest.raises(KeycloakGetError) as err:
  1168. admin.get_composite_realm_roles_of_role(role_name="bad")
  1169. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1170. res = admin.get_composite_realm_roles_of_user(user_id=user_id)
  1171. assert len(res) == 4
  1172. assert "offline_access" in {x["name"] for x in res}
  1173. assert "test-realm-role-update" in {x["name"] for x in res}
  1174. assert "uma_authorization" in {x["name"] for x in res}
  1175. with pytest.raises(KeycloakGetError) as err:
  1176. admin.get_composite_realm_roles_of_user(user_id="bad")
  1177. assert err.match(USER_NOT_FOUND_REGEX), err
  1178. with pytest.raises(KeycloakDeleteError) as err:
  1179. admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1180. assert err.match(UNKOWN_ERROR_REGEX), err
  1181. res = admin.remove_composite_realm_roles_to_role(
  1182. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1183. )
  1184. assert res == dict(), res
  1185. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1186. assert len(res) == 0
  1187. # Test realm role group list
  1188. res = admin.get_realm_role_groups(role_name="test-realm-role-update")
  1189. assert len(res) == 1
  1190. assert res[0]["id"] == group_id
  1191. with pytest.raises(KeycloakGetError) as err:
  1192. admin.get_realm_role_groups(role_name="non-existent-role")
  1193. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1194. # Test with query params
  1195. res = admin.get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
  1196. assert len(res) == 1
  1197. # Test delete realm role
  1198. res = admin.delete_realm_role(role_name=composite_role)
  1199. assert res == dict(), res
  1200. with pytest.raises(KeycloakDeleteError) as err:
  1201. admin.delete_realm_role(role_name=composite_role)
  1202. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1203. @pytest.mark.parametrize(
  1204. "testcase, arg_brief_repr, includes_attributes",
  1205. [
  1206. ("brief True", {"brief_representation": True}, False),
  1207. ("brief False", {"brief_representation": False}, True),
  1208. ("default", {}, False),
  1209. ],
  1210. )
  1211. def test_role_attributes(
  1212. admin: KeycloakAdmin,
  1213. realm: str,
  1214. client: str,
  1215. arg_brief_repr: dict,
  1216. includes_attributes: bool,
  1217. testcase: str,
  1218. ):
  1219. """Test getting role attributes for bulk calls.
  1220. :param admin: Keycloak admin
  1221. :type admin: KeycloakAdmin
  1222. :param realm: Keycloak realm
  1223. :type realm: str
  1224. :param client: Keycloak client
  1225. :type client: str
  1226. :param arg_brief_repr: Brief representation
  1227. :type arg_brief_repr: dict
  1228. :param includes_attributes: Indicator whether to include attributes
  1229. :type includes_attributes: bool
  1230. :param testcase: Test case
  1231. :type testcase: str
  1232. """
  1233. # setup
  1234. attribute_role = "test-realm-role-w-attr"
  1235. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  1236. role_id = admin.create_realm_role(
  1237. payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1238. )
  1239. assert role_id, role_id
  1240. cli_role_id = admin.create_client_role(
  1241. client, payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1242. )
  1243. assert cli_role_id, cli_role_id
  1244. if not includes_attributes:
  1245. test_attrs = None
  1246. # tests
  1247. roles = admin.get_realm_roles(**arg_brief_repr)
  1248. roles_filtered = [role for role in roles if role["name"] == role_id]
  1249. assert roles_filtered, roles_filtered
  1250. role = roles_filtered[0]
  1251. assert role.get("attributes") == test_attrs, testcase
  1252. roles = admin.get_client_roles(client, **arg_brief_repr)
  1253. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  1254. assert roles_filtered, roles_filtered
  1255. role = roles_filtered[0]
  1256. assert role.get("attributes") == test_attrs, testcase
  1257. # cleanup
  1258. res = admin.delete_realm_role(role_name=attribute_role)
  1259. assert res == dict(), res
  1260. res = admin.delete_client_role(client, role_name=attribute_role)
  1261. assert res == dict(), res
  1262. def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
  1263. """Test client realm roles.
  1264. :param admin: Keycloak admin
  1265. :type admin: KeycloakAdmin
  1266. :param realm: Keycloak realm
  1267. :type realm: str
  1268. """
  1269. admin.change_current_realm(realm)
  1270. # Test get realm roles
  1271. roles = admin.get_realm_roles()
  1272. assert len(roles) == 3, roles
  1273. role_names = [x["name"] for x in roles]
  1274. assert "uma_authorization" in role_names, role_names
  1275. assert "offline_access" in role_names, role_names
  1276. # create realm role for test
  1277. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1278. assert role_id, role_id
  1279. # Test realm role client assignment
  1280. client_id = admin.create_client(
  1281. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1282. )
  1283. with pytest.raises(KeycloakPostError) as err:
  1284. admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  1285. assert err.match(UNKOWN_ERROR_REGEX), err
  1286. res = admin.assign_realm_roles_to_client_scope(
  1287. client_id=client_id,
  1288. roles=[
  1289. admin.get_realm_role(role_name="offline_access"),
  1290. admin.get_realm_role(role_name="test-realm-role"),
  1291. ],
  1292. )
  1293. assert res == dict(), res
  1294. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1295. assert len(roles) == 2
  1296. client_role_names = [x["name"] for x in roles]
  1297. assert "offline_access" in client_role_names, client_role_names
  1298. assert "test-realm-role" in client_role_names, client_role_names
  1299. assert "uma_authorization" not in client_role_names, client_role_names
  1300. # Test remove realm role of client
  1301. with pytest.raises(KeycloakDeleteError) as err:
  1302. admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  1303. assert err.match(UNKOWN_ERROR_REGEX), err
  1304. res = admin.delete_realm_roles_of_client_scope(
  1305. client_id=client_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1306. )
  1307. assert res == dict(), res
  1308. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1309. assert len(roles) == 1
  1310. assert "test-realm-role" in [x["name"] for x in roles]
  1311. res = admin.delete_realm_roles_of_client_scope(
  1312. client_id=client_id, roles=[admin.get_realm_role(role_name="test-realm-role")]
  1313. )
  1314. assert res == dict(), res
  1315. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1316. assert len(roles) == 0
  1317. def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
  1318. """Test client assignment of other client roles.
  1319. :param admin: Keycloak admin
  1320. :type admin: KeycloakAdmin
  1321. :param realm: Keycloak realm
  1322. :type realm: str
  1323. :param client: Keycloak client
  1324. :type client: str
  1325. """
  1326. admin.change_current_realm(realm)
  1327. client_id = admin.create_client(
  1328. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1329. )
  1330. # Test get client roles
  1331. roles = admin.get_client_roles_of_client_scope(client_id, client)
  1332. assert len(roles) == 0, roles
  1333. # create client role for test
  1334. client_role_id = admin.create_client_role(
  1335. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1336. )
  1337. assert client_role_id, client_role_id
  1338. # Test client role assignment to other client
  1339. with pytest.raises(KeycloakPostError) as err:
  1340. admin.assign_client_roles_to_client_scope(
  1341. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1342. )
  1343. assert err.match(UNKOWN_ERROR_REGEX), err
  1344. res = admin.assign_client_roles_to_client_scope(
  1345. client_id=client_id,
  1346. client_roles_owner_id=client,
  1347. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1348. )
  1349. assert res == dict(), res
  1350. roles = admin.get_client_roles_of_client_scope(
  1351. client_id=client_id, client_roles_owner_id=client
  1352. )
  1353. assert len(roles) == 1
  1354. client_role_names = [x["name"] for x in roles]
  1355. assert "client-role-test" in client_role_names, client_role_names
  1356. # Test remove realm role of client
  1357. with pytest.raises(KeycloakDeleteError) as err:
  1358. admin.delete_client_roles_of_client_scope(
  1359. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1360. )
  1361. assert err.match(UNKOWN_ERROR_REGEX), err
  1362. res = admin.delete_client_roles_of_client_scope(
  1363. client_id=client_id,
  1364. client_roles_owner_id=client,
  1365. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1366. )
  1367. assert res == dict(), res
  1368. roles = admin.get_client_roles_of_client_scope(
  1369. client_id=client_id, client_roles_owner_id=client
  1370. )
  1371. assert len(roles) == 0
  1372. def test_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1373. """Test client assignment of default client scopes.
  1374. :param admin: Keycloak admin
  1375. :type admin: KeycloakAdmin
  1376. :param realm: Keycloak realm
  1377. :type realm: str
  1378. :param client: Keycloak client
  1379. :type client: str
  1380. """
  1381. admin.change_current_realm(realm)
  1382. client_id = admin.create_client(
  1383. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1384. )
  1385. # Test get client default scopes
  1386. # keycloak default roles: web-origins, acr, profile, roles, email
  1387. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1388. assert len(default_client_scopes) in [6, 5], default_client_scopes
  1389. # Test add a client scope to client default scopes
  1390. default_client_scope = "test-client-default-scope"
  1391. new_client_scope = {
  1392. "name": default_client_scope,
  1393. "description": f"Test Client Scope: {default_client_scope}",
  1394. "protocol": "openid-connect",
  1395. "attributes": {},
  1396. }
  1397. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1398. new_default_client_scope_data = {
  1399. "realm": realm,
  1400. "client": client_id,
  1401. "clientScopeId": new_client_scope_id,
  1402. }
  1403. admin.add_client_default_client_scope(
  1404. client_id, new_client_scope_id, new_default_client_scope_data
  1405. )
  1406. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1407. assert len(default_client_scopes) in [6, 7], default_client_scopes
  1408. # Test remove a client default scope
  1409. admin.delete_client_default_client_scope(client_id, new_client_scope_id)
  1410. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1411. assert len(default_client_scopes) in [5, 6], default_client_scopes
  1412. def test_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1413. """Test client assignment of optional client scopes.
  1414. :param admin: Keycloak admin
  1415. :type admin: KeycloakAdmin
  1416. :param realm: Keycloak realm
  1417. :type realm: str
  1418. :param client: Keycloak client
  1419. :type client: str
  1420. """
  1421. admin.change_current_realm(realm)
  1422. client_id = admin.create_client(
  1423. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1424. )
  1425. # Test get client optional scopes
  1426. # keycloak optional roles: microprofile-jwt, offline_access, address, phone
  1427. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1428. assert len(optional_client_scopes) == 4, optional_client_scopes
  1429. # Test add a client scope to client optional scopes
  1430. optional_client_scope = "test-client-optional-scope"
  1431. new_client_scope = {
  1432. "name": optional_client_scope,
  1433. "description": f"Test Client Scope: {optional_client_scope}",
  1434. "protocol": "openid-connect",
  1435. "attributes": {},
  1436. }
  1437. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1438. new_optional_client_scope_data = {
  1439. "realm": realm,
  1440. "client": client_id,
  1441. "clientScopeId": new_client_scope_id,
  1442. }
  1443. admin.add_client_optional_client_scope(
  1444. client_id, new_client_scope_id, new_optional_client_scope_data
  1445. )
  1446. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1447. assert len(optional_client_scopes) == 5, optional_client_scopes
  1448. # Test remove a client optional scope
  1449. admin.delete_client_optional_client_scope(client_id, new_client_scope_id)
  1450. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1451. assert len(optional_client_scopes) == 4, optional_client_scopes
  1452. def test_client_roles(admin: KeycloakAdmin, client: str):
  1453. """Test client roles.
  1454. :param admin: Keycloak Admin client
  1455. :type admin: KeycloakAdmin
  1456. :param client: Keycloak client
  1457. :type client: str
  1458. """
  1459. # Test get client roles
  1460. res = admin.get_client_roles(client_id=client)
  1461. assert len(res) == 0
  1462. with pytest.raises(KeycloakGetError) as err:
  1463. admin.get_client_roles(client_id="bad")
  1464. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1465. # Test create client role
  1466. client_role_id = admin.create_client_role(
  1467. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1468. )
  1469. with pytest.raises(KeycloakPostError) as err:
  1470. admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"})
  1471. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  1472. client_role_id_2 = admin.create_client_role(
  1473. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1474. )
  1475. assert client_role_id == client_role_id_2
  1476. # Test get client role
  1477. res = admin.get_client_role(client_id=client, role_name="client-role-test")
  1478. assert res["name"] == client_role_id
  1479. with pytest.raises(KeycloakGetError) as err:
  1480. admin.get_client_role(client_id=client, role_name="bad")
  1481. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1482. res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test")
  1483. assert res_ == res["id"]
  1484. with pytest.raises(KeycloakGetError) as err:
  1485. admin.get_client_role_id(client_id=client, role_name="bad")
  1486. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1487. assert len(admin.get_client_roles(client_id=client)) == 1
  1488. # Test update client role
  1489. res = admin.update_client_role(
  1490. client_id=client, role_name="client-role-test", payload={"name": "client-role-test-update"}
  1491. )
  1492. assert res == dict()
  1493. with pytest.raises(KeycloakPutError) as err:
  1494. res = admin.update_client_role(
  1495. client_id=client,
  1496. role_name="client-role-test",
  1497. payload={"name": "client-role-test-update"},
  1498. )
  1499. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1500. # Test user with client role
  1501. res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update")
  1502. assert len(res) == 0
  1503. with pytest.raises(KeycloakGetError) as err:
  1504. admin.get_client_role_members(client_id=client, role_name="bad")
  1505. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1506. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  1507. with pytest.raises(KeycloakPostError) as err:
  1508. admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  1509. assert err.match(UNKOWN_ERROR_REGEX), err
  1510. res = admin.assign_client_role(
  1511. user_id=user_id,
  1512. client_id=client,
  1513. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1514. )
  1515. assert res == dict()
  1516. assert (
  1517. len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update"))
  1518. == 1
  1519. )
  1520. roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client)
  1521. assert len(roles) == 1, roles
  1522. with pytest.raises(KeycloakGetError) as err:
  1523. admin.get_client_roles_of_user(user_id=user_id, client_id="bad")
  1524. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1525. roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  1526. assert len(roles) == 1, roles
  1527. with pytest.raises(KeycloakGetError) as err:
  1528. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1529. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1530. roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client)
  1531. assert len(roles) == 0, roles
  1532. with pytest.raises(KeycloakGetError) as err:
  1533. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1534. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1535. with pytest.raises(KeycloakDeleteError) as err:
  1536. admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  1537. assert err.match(UNKOWN_ERROR_REGEX), err
  1538. admin.delete_client_roles_of_user(
  1539. user_id=user_id,
  1540. client_id=client,
  1541. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1542. )
  1543. assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  1544. # Test groups and client roles
  1545. res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")
  1546. assert len(res) == 0
  1547. with pytest.raises(KeycloakGetError) as err:
  1548. admin.get_client_role_groups(client_id=client, role_name="bad")
  1549. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1550. group_id = admin.create_group(payload={"name": "test-group"})
  1551. res = admin.get_group_client_roles(group_id=group_id, client_id=client)
  1552. assert len(res) == 0
  1553. with pytest.raises(KeycloakGetError) as err:
  1554. admin.get_group_client_roles(group_id=group_id, client_id="bad")
  1555. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1556. with pytest.raises(KeycloakPostError) as err:
  1557. admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1558. assert err.match(UNKOWN_ERROR_REGEX), err
  1559. res = admin.assign_group_client_roles(
  1560. group_id=group_id,
  1561. client_id=client,
  1562. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1563. )
  1564. assert res == dict()
  1565. assert (
  1566. len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update"))
  1567. == 1
  1568. )
  1569. assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1
  1570. with pytest.raises(KeycloakDeleteError) as err:
  1571. admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1572. assert err.match(UNKOWN_ERROR_REGEX), err
  1573. res = admin.delete_group_client_roles(
  1574. group_id=group_id,
  1575. client_id=client,
  1576. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1577. )
  1578. assert res == dict()
  1579. # Test composite client roles
  1580. with pytest.raises(KeycloakPostError) as err:
  1581. admin.add_composite_client_roles_to_role(
  1582. client_role_id=client, role_name="client-role-test-update", roles=["bad"]
  1583. )
  1584. assert err.match(UNKOWN_ERROR_REGEX), err
  1585. res = admin.add_composite_client_roles_to_role(
  1586. client_role_id=client,
  1587. role_name="client-role-test-update",
  1588. roles=[admin.get_realm_role(role_name="offline_access")],
  1589. )
  1590. assert res == dict()
  1591. assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1592. "composite"
  1593. ]
  1594. # Test delete of client role
  1595. res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1596. assert res == dict()
  1597. with pytest.raises(KeycloakDeleteError) as err:
  1598. admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1599. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1600. # Test of roles by id - Get role
  1601. admin.create_client_role(
  1602. client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
  1603. )
  1604. role = admin.get_client_role(client_id=client, role_name="client-role-by-id-test")
  1605. res = admin.get_role_by_id(role_id=role["id"])
  1606. assert res["name"] == "client-role-by-id-test"
  1607. with pytest.raises(KeycloakGetError) as err:
  1608. admin.get_role_by_id(role_id="bad")
  1609. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1610. # Test of roles by id - Update role
  1611. res = admin.update_role_by_id(
  1612. role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
  1613. )
  1614. assert res == dict()
  1615. with pytest.raises(KeycloakPutError) as err:
  1616. res = admin.update_role_by_id(
  1617. role_id="bad", payload={"name": "client-role-by-id-test-update"}
  1618. )
  1619. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1620. # Test of roles by id - Delete role
  1621. res = admin.delete_role_by_id(role_id=role["id"])
  1622. assert res == dict()
  1623. with pytest.raises(KeycloakDeleteError) as err:
  1624. admin.delete_role_by_id(role_id="bad")
  1625. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1626. def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
  1627. """Test enable token exchange.
  1628. :param admin: Keycloak Admin client
  1629. :type admin: KeycloakAdmin
  1630. :param realm: Keycloak realm
  1631. :type realm: str
  1632. :raises AssertionError: In case of bad configuration
  1633. """
  1634. # Test enabling token exchange between two confidential clients
  1635. admin.change_current_realm(realm)
  1636. # Create test clients
  1637. source_client_id = admin.create_client(
  1638. payload={"name": "Source Client", "clientId": "source-client"}
  1639. )
  1640. target_client_id = admin.create_client(
  1641. payload={"name": "Target Client", "clientId": "target-client"}
  1642. )
  1643. for c in admin.get_clients():
  1644. if c["clientId"] == "realm-management":
  1645. realm_management_id = c["id"]
  1646. break
  1647. else:
  1648. raise AssertionError("Missing realm management client")
  1649. # Enable permissions on the Superset client
  1650. admin.update_client_management_permissions(
  1651. payload={"enabled": True}, client_id=target_client_id
  1652. )
  1653. # Fetch various IDs and strings needed when creating the permission
  1654. token_exchange_permission_id = admin.get_client_management_permissions(
  1655. client_id=target_client_id
  1656. )["scopePermissions"]["token-exchange"]
  1657. scopes = admin.get_client_authz_policy_scopes(
  1658. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1659. )
  1660. for s in scopes:
  1661. if s["name"] == "token-exchange":
  1662. token_exchange_scope_id = s["id"]
  1663. break
  1664. else:
  1665. raise AssertionError("Missing token-exchange scope")
  1666. resources = admin.get_client_authz_policy_resources(
  1667. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1668. )
  1669. for r in resources:
  1670. if r["name"] == f"client.resource.{target_client_id}":
  1671. token_exchange_resource_id = r["_id"]
  1672. break
  1673. else:
  1674. raise AssertionError("Missing client resource")
  1675. # Create a client policy for source client
  1676. policy_name = "Exchange source client token with target client token"
  1677. client_policy_id = admin.create_client_authz_client_policy(
  1678. payload={
  1679. "type": "client",
  1680. "logic": "POSITIVE",
  1681. "decisionStrategy": "UNANIMOUS",
  1682. "name": policy_name,
  1683. "clients": [source_client_id],
  1684. },
  1685. client_id=realm_management_id,
  1686. )["id"]
  1687. policies = admin.get_client_authz_client_policies(client_id=realm_management_id)
  1688. for policy in policies:
  1689. if policy["name"] == policy_name:
  1690. assert policy["clients"] == [source_client_id]
  1691. break
  1692. else:
  1693. raise AssertionError("Missing client policy")
  1694. # Update permissions on the target client to reference this policy
  1695. permission_name = admin.get_client_authz_scope_permission(
  1696. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1697. )["name"]
  1698. admin.update_client_authz_scope_permission(
  1699. payload={
  1700. "id": token_exchange_permission_id,
  1701. "name": permission_name,
  1702. "type": "scope",
  1703. "logic": "POSITIVE",
  1704. "decisionStrategy": "UNANIMOUS",
  1705. "resources": [token_exchange_resource_id],
  1706. "scopes": [token_exchange_scope_id],
  1707. "policies": [client_policy_id],
  1708. },
  1709. client_id=realm_management_id,
  1710. scope_id=token_exchange_permission_id,
  1711. )
  1712. # Create permissions on the target client to reference this policy
  1713. admin.create_client_authz_scope_permission(
  1714. payload={
  1715. "id": "some-id",
  1716. "name": "test-permission",
  1717. "type": "scope",
  1718. "logic": "POSITIVE",
  1719. "decisionStrategy": "UNANIMOUS",
  1720. "resources": [token_exchange_resource_id],
  1721. "scopes": [token_exchange_scope_id],
  1722. "policies": [client_policy_id],
  1723. },
  1724. client_id=realm_management_id,
  1725. )
  1726. permission_name = admin.get_client_authz_scope_permission(
  1727. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1728. )["name"]
  1729. assert permission_name.startswith("token-exchange.permission.client.")
  1730. with pytest.raises(KeycloakPostError) as err:
  1731. admin.create_client_authz_scope_permission(
  1732. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  1733. client_id="realm_management_id",
  1734. )
  1735. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1736. def test_email(admin: KeycloakAdmin, user: str):
  1737. """Test email.
  1738. :param admin: Keycloak Admin client
  1739. :type admin: KeycloakAdmin
  1740. :param user: Keycloak user
  1741. :type user: str
  1742. """
  1743. # Emails will fail as we don't have SMTP test setup
  1744. with pytest.raises(KeycloakPutError) as err:
  1745. admin.send_update_account(user_id=user, payload=dict())
  1746. assert err.match(UNKOWN_ERROR_REGEX), err
  1747. admin.update_user(user_id=user, payload={"enabled": True})
  1748. with pytest.raises(KeycloakPutError) as err:
  1749. admin.send_verify_email(user_id=user)
  1750. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  1751. def test_get_sessions(admin: KeycloakAdmin):
  1752. """Test get sessions.
  1753. :param admin: Keycloak Admin client
  1754. :type admin: KeycloakAdmin
  1755. """
  1756. sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.connection.username))
  1757. assert len(sessions) >= 1
  1758. with pytest.raises(KeycloakGetError) as err:
  1759. admin.get_sessions(user_id="bad")
  1760. assert err.match(USER_NOT_FOUND_REGEX)
  1761. def test_get_client_installation_provider(admin: KeycloakAdmin, client: str):
  1762. """Test get client installation provider.
  1763. :param admin: Keycloak Admin client
  1764. :type admin: KeycloakAdmin
  1765. :param client: Keycloak client
  1766. :type client: str
  1767. """
  1768. with pytest.raises(KeycloakGetError) as err:
  1769. admin.get_client_installation_provider(client_id=client, provider_id="bad")
  1770. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  1771. installation = admin.get_client_installation_provider(
  1772. client_id=client, provider_id="keycloak-oidc-keycloak-json"
  1773. )
  1774. assert set(installation.keys()) == {
  1775. "auth-server-url",
  1776. "confidential-port",
  1777. "credentials",
  1778. "realm",
  1779. "resource",
  1780. "ssl-required",
  1781. }
  1782. def test_auth_flows(admin: KeycloakAdmin, realm: str):
  1783. """Test auth flows.
  1784. :param admin: Keycloak Admin client
  1785. :type admin: KeycloakAdmin
  1786. :param realm: Keycloak realm
  1787. :type realm: str
  1788. """
  1789. admin.change_current_realm(realm)
  1790. res = admin.get_authentication_flows()
  1791. assert len(res) <= 8, res
  1792. default_flows = len(res)
  1793. assert {x["alias"] for x in res}.issubset(
  1794. {
  1795. "reset credentials",
  1796. "browser",
  1797. "registration",
  1798. "http challenge",
  1799. "docker auth",
  1800. "direct grant",
  1801. "first broker login",
  1802. "clients",
  1803. }
  1804. )
  1805. assert set(res[0].keys()) == {
  1806. "alias",
  1807. "authenticationExecutions",
  1808. "builtIn",
  1809. "description",
  1810. "id",
  1811. "providerId",
  1812. "topLevel",
  1813. }
  1814. assert {x["alias"] for x in res}.issubset(
  1815. {
  1816. "reset credentials",
  1817. "browser",
  1818. "registration",
  1819. "docker auth",
  1820. "direct grant",
  1821. "first broker login",
  1822. "clients",
  1823. "http challenge",
  1824. }
  1825. )
  1826. with pytest.raises(KeycloakGetError) as err:
  1827. admin.get_authentication_flow_for_id(flow_id="bad")
  1828. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  1829. browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"]
  1830. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  1831. assert res["alias"] == "browser"
  1832. # Test copying
  1833. with pytest.raises(KeycloakPostError) as err:
  1834. admin.copy_authentication_flow(payload=dict(), flow_alias="bad")
  1835. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  1836. res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser")
  1837. assert res == b"", res
  1838. assert len(admin.get_authentication_flows()) == (default_flows + 1)
  1839. # Test create
  1840. res = admin.create_authentication_flow(
  1841. payload={"alias": "test-create", "providerId": "basic-flow"}
  1842. )
  1843. assert res == b""
  1844. with pytest.raises(KeycloakPostError) as err:
  1845. admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False})
  1846. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  1847. assert admin.create_authentication_flow(
  1848. payload={"alias": "test-create"}, skip_exists=True
  1849. ) == {"msg": "Already exists"}
  1850. # Test flow executions
  1851. res = admin.get_authentication_flow_executions(flow_alias="browser")
  1852. assert len(res) == 8, res
  1853. with pytest.raises(KeycloakGetError) as err:
  1854. admin.get_authentication_flow_executions(flow_alias="bad")
  1855. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  1856. exec_id = res[0]["id"]
  1857. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  1858. assert set(res.keys()).issubset(
  1859. {
  1860. "alternative",
  1861. "authenticator",
  1862. "authenticatorFlow",
  1863. "autheticatorFlow",
  1864. "conditional",
  1865. "disabled",
  1866. "enabled",
  1867. "id",
  1868. "parentFlow",
  1869. "priority",
  1870. "required",
  1871. "requirement",
  1872. }
  1873. ), res.keys()
  1874. with pytest.raises(KeycloakGetError) as err:
  1875. admin.get_authentication_flow_execution(execution_id="bad")
  1876. assert err.match(ILLEGAL_EXECUTION_REGEX)
  1877. with pytest.raises(KeycloakPostError) as err:
  1878. admin.create_authentication_flow_execution(payload=dict(), flow_alias="browser")
  1879. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  1880. res = admin.create_authentication_flow_execution(
  1881. payload={"provider": "auth-cookie"}, flow_alias="test-create"
  1882. )
  1883. assert res == b""
  1884. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  1885. with pytest.raises(KeycloakPutError) as err:
  1886. admin.update_authentication_flow_executions(
  1887. payload={"required": "yes"}, flow_alias="test-create"
  1888. )
  1889. assert err.match('400: b\'{"error":"Unrecognized field')
  1890. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  1891. payload["displayName"] = "test"
  1892. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  1893. assert res or (res == {})
  1894. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  1895. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  1896. assert res == dict()
  1897. with pytest.raises(KeycloakDeleteError) as err:
  1898. admin.delete_authentication_flow_execution(execution_id=exec_id)
  1899. assert err.match(ILLEGAL_EXECUTION_REGEX)
  1900. # Test subflows
  1901. res = admin.create_authentication_flow_subflow(
  1902. payload={
  1903. "alias": "test-subflow",
  1904. "provider": "basic-flow",
  1905. "type": "something",
  1906. "description": "something",
  1907. },
  1908. flow_alias="test-browser",
  1909. )
  1910. assert res == b""
  1911. with pytest.raises(KeycloakPostError) as err:
  1912. admin.create_authentication_flow_subflow(
  1913. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  1914. flow_alias="test-browser",
  1915. )
  1916. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  1917. res = admin.create_authentication_flow_subflow(
  1918. payload={
  1919. "alias": "test-subflow",
  1920. "provider": "basic-flow",
  1921. "type": "something",
  1922. "description": "something",
  1923. },
  1924. flow_alias="test-create",
  1925. skip_exists=True,
  1926. )
  1927. assert res == {"msg": "Already exists"}
  1928. # Test delete auth flow
  1929. flow_id = [x for x in admin.get_authentication_flows() if x["alias"] == "test-browser"][0][
  1930. "id"
  1931. ]
  1932. res = admin.delete_authentication_flow(flow_id=flow_id)
  1933. assert res == dict()
  1934. with pytest.raises(KeycloakDeleteError) as err:
  1935. admin.delete_authentication_flow(flow_id=flow_id)
  1936. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  1937. 'b\'{"error":"Flow not found"' in str(err)
  1938. )
  1939. def test_authentication_configs(admin: KeycloakAdmin, realm: str):
  1940. """Test authentication configs.
  1941. :param admin: Keycloak Admin client
  1942. :type admin: KeycloakAdmin
  1943. :param realm: Keycloak realm
  1944. :type realm: str
  1945. """
  1946. admin.change_current_realm(realm)
  1947. # Test list of auth providers
  1948. res = admin.get_authenticator_providers()
  1949. assert len(res) <= 38
  1950. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  1951. assert res == {
  1952. "helpText": "Validates the SSO cookie set by the auth server.",
  1953. "name": "Cookie",
  1954. "properties": [],
  1955. "providerId": "auth-cookie",
  1956. }
  1957. # Test authenticator config
  1958. # Currently unable to find a sustainable way to fetch the config id,
  1959. # therefore testing only failures
  1960. with pytest.raises(KeycloakGetError) as err:
  1961. admin.get_authenticator_config(config_id="bad")
  1962. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1963. with pytest.raises(KeycloakPutError) as err:
  1964. admin.update_authenticator_config(payload=dict(), config_id="bad")
  1965. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1966. with pytest.raises(KeycloakDeleteError) as err:
  1967. admin.delete_authenticator_config(config_id="bad")
  1968. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1969. def test_sync_users(admin: KeycloakAdmin, realm: str):
  1970. """Test sync users.
  1971. :param admin: Keycloak Admin client
  1972. :type admin: KeycloakAdmin
  1973. :param realm: Keycloak realm
  1974. :type realm: str
  1975. """
  1976. admin.change_current_realm(realm)
  1977. # Only testing the error message
  1978. with pytest.raises(KeycloakPostError) as err:
  1979. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  1980. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  1981. def test_client_scopes(admin: KeycloakAdmin, realm: str):
  1982. """Test client scopes.
  1983. :param admin: Keycloak Admin client
  1984. :type admin: KeycloakAdmin
  1985. :param realm: Keycloak realm
  1986. :type realm: str
  1987. """
  1988. admin.change_current_realm(realm)
  1989. # Test get client scopes
  1990. res = admin.get_client_scopes()
  1991. scope_names = {x["name"] for x in res}
  1992. assert len(res) in [10, 11]
  1993. assert "email" in scope_names
  1994. assert "profile" in scope_names
  1995. assert "offline_access" in scope_names
  1996. with pytest.raises(KeycloakGetError) as err:
  1997. admin.get_client_scope(client_scope_id="does-not-exist")
  1998. assert err.match(NO_CLIENT_SCOPE_REGEX)
  1999. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  2000. assert res[0] == scope
  2001. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  2002. assert res[0] == scope
  2003. # Test create client scope
  2004. res = admin.create_client_scope(
  2005. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
  2006. )
  2007. assert res
  2008. res2 = admin.create_client_scope(
  2009. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
  2010. )
  2011. assert res == res2
  2012. with pytest.raises(KeycloakPostError) as err:
  2013. admin.create_client_scope(
  2014. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=False
  2015. )
  2016. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  2017. # Test update client scope
  2018. with pytest.raises(KeycloakPutError) as err:
  2019. admin.update_client_scope(client_scope_id="does-not-exist", payload=dict())
  2020. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2021. res_update = admin.update_client_scope(
  2022. client_scope_id=res, payload={"name": "test-scope-update"}
  2023. )
  2024. assert res_update == dict()
  2025. assert admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  2026. # Test get mappers
  2027. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  2028. assert mappers == list()
  2029. # Test add mapper
  2030. with pytest.raises(KeycloakPostError) as err:
  2031. admin.add_mapper_to_client_scope(client_scope_id=res, payload=dict())
  2032. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  2033. res_add = admin.add_mapper_to_client_scope(
  2034. client_scope_id=res,
  2035. payload={
  2036. "name": "test-mapper",
  2037. "protocol": "openid-connect",
  2038. "protocolMapper": "oidc-usermodel-attribute-mapper",
  2039. },
  2040. )
  2041. assert res_add == b""
  2042. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  2043. # Test update mapper
  2044. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  2045. with pytest.raises(KeycloakPutError) as err:
  2046. admin.update_mapper_in_client_scope(
  2047. client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
  2048. )
  2049. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2050. test_mapper["config"]["user.attribute"] = "test"
  2051. res_update = admin.update_mapper_in_client_scope(
  2052. client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
  2053. )
  2054. assert res_update == dict()
  2055. assert (
  2056. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  2057. == "test"
  2058. )
  2059. # Test delete mapper
  2060. res_del = admin.delete_mapper_from_client_scope(
  2061. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  2062. )
  2063. assert res_del == dict()
  2064. with pytest.raises(KeycloakDeleteError) as err:
  2065. admin.delete_mapper_from_client_scope(
  2066. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  2067. )
  2068. assert err.match('404: b\'{"error":"Model not found".*}\'')
  2069. # Test default default scopes
  2070. res_defaults = admin.get_default_default_client_scopes()
  2071. assert len(res_defaults) in [6, 7]
  2072. with pytest.raises(KeycloakPutError) as err:
  2073. admin.add_default_default_client_scope(scope_id="does-not-exist")
  2074. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2075. res_add = admin.add_default_default_client_scope(scope_id=res)
  2076. assert res_add == dict()
  2077. assert len(admin.get_default_default_client_scopes()) in [7, 8]
  2078. with pytest.raises(KeycloakDeleteError) as err:
  2079. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  2080. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2081. res_del = admin.delete_default_default_client_scope(scope_id=res)
  2082. assert res_del == dict()
  2083. assert len(admin.get_default_default_client_scopes()) in [6, 7]
  2084. # Test default optional scopes
  2085. res_defaults = admin.get_default_optional_client_scopes()
  2086. assert len(res_defaults) == 4
  2087. with pytest.raises(KeycloakPutError) as err:
  2088. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  2089. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2090. res_add = admin.add_default_optional_client_scope(scope_id=res)
  2091. assert res_add == dict()
  2092. assert len(admin.get_default_optional_client_scopes()) == 5
  2093. with pytest.raises(KeycloakDeleteError) as err:
  2094. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  2095. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  2096. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  2097. assert res_del == dict()
  2098. assert len(admin.get_default_optional_client_scopes()) == 4
  2099. # Test client scope delete
  2100. res_del = admin.delete_client_scope(client_scope_id=res)
  2101. assert res_del == dict()
  2102. with pytest.raises(KeycloakDeleteError) as err:
  2103. admin.delete_client_scope(client_scope_id=res)
  2104. assert err.match(NO_CLIENT_SCOPE_REGEX)
  2105. def test_components(admin: KeycloakAdmin, realm: str):
  2106. """Test components.
  2107. :param admin: Keycloak Admin client
  2108. :type admin: KeycloakAdmin
  2109. :param realm: Keycloak realm
  2110. :type realm: str
  2111. """
  2112. admin.change_current_realm(realm)
  2113. # Test get components
  2114. res = admin.get_components()
  2115. assert len(res) == 12
  2116. with pytest.raises(KeycloakGetError) as err:
  2117. admin.get_component(component_id="does-not-exist")
  2118. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2119. res_get = admin.get_component(component_id=res[0]["id"])
  2120. assert res_get == res[0]
  2121. # Test create component
  2122. with pytest.raises(KeycloakPostError) as err:
  2123. admin.create_component(payload={"bad": "dict"})
  2124. assert err.match('400: b\'{"error":"Unrecognized field')
  2125. res = admin.create_component(
  2126. payload={
  2127. "name": "Test Component",
  2128. "providerId": "max-clients",
  2129. "providerType": "org.keycloak.services.clientregistration."
  2130. + "policy.ClientRegistrationPolicy",
  2131. "config": {"max-clients": ["1000"]},
  2132. }
  2133. )
  2134. assert res
  2135. assert admin.get_component(component_id=res)["name"] == "Test Component"
  2136. # Test update component
  2137. component = admin.get_component(component_id=res)
  2138. component["name"] = "Test Component Update"
  2139. with pytest.raises(KeycloakPutError) as err:
  2140. admin.update_component(component_id="does-not-exist", payload=dict())
  2141. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2142. res_upd = admin.update_component(component_id=res, payload=component)
  2143. assert res_upd == dict()
  2144. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  2145. # Test delete component
  2146. res_del = admin.delete_component(component_id=res)
  2147. assert res_del == dict()
  2148. with pytest.raises(KeycloakDeleteError) as err:
  2149. admin.delete_component(component_id=res)
  2150. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2151. def test_keys(admin: KeycloakAdmin, realm: str):
  2152. """Test keys.
  2153. :param admin: Keycloak Admin client
  2154. :type admin: KeycloakAdmin
  2155. :param realm: Keycloak realm
  2156. :type realm: str
  2157. """
  2158. admin.change_current_realm(realm)
  2159. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} or set(
  2160. admin.get_keys()["active"].keys()
  2161. ) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  2162. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2163. "HS256",
  2164. "RSA-OAEP",
  2165. "AES",
  2166. "RS256",
  2167. } or {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2168. "HS512",
  2169. "RSA-OAEP",
  2170. "AES",
  2171. "RS256",
  2172. }
  2173. def test_admin_events(admin: KeycloakAdmin, realm: str):
  2174. """Test events.
  2175. :param admin: Keycloak Admin client
  2176. :type admin: KeycloakAdmin
  2177. :param realm: Keycloak realm
  2178. :type realm: str
  2179. """
  2180. admin.change_current_realm(realm)
  2181. admin.create_client(payload={"name": "test", "clientId": "test"})
  2182. events = admin.get_admin_events()
  2183. assert events == list()
  2184. def test_user_events(admin: KeycloakAdmin, realm: str):
  2185. """Test events.
  2186. :param admin: Keycloak Admin client
  2187. :type admin: KeycloakAdmin
  2188. :param realm: Keycloak realm
  2189. :type realm: str
  2190. """
  2191. admin.change_current_realm(realm)
  2192. events = admin.get_events()
  2193. assert events == list()
  2194. with pytest.raises(KeycloakPutError) as err:
  2195. admin.set_events(payload={"bad": "conf"})
  2196. assert err.match('400: b\'{"error":"Unrecognized field')
  2197. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  2198. assert res == dict()
  2199. admin.create_client(payload={"name": "test", "clientId": "test"})
  2200. events = admin.get_events()
  2201. assert events == list()
  2202. @freezegun.freeze_time("2023-02-25 10:00:00")
  2203. def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
  2204. """Test auto refresh token.
  2205. :param admin_frozen: Keycloak Admin client with time frozen in place
  2206. :type admin_frozen: KeycloakAdmin
  2207. :param realm: Keycloak realm
  2208. :type realm: str
  2209. """
  2210. admin = admin_frozen
  2211. admin.get_realm(realm_name=realm)
  2212. # Test get refresh
  2213. admin.connection.custom_headers = {
  2214. "Authorization": "Bearer bad",
  2215. "Content-Type": "application/json",
  2216. }
  2217. with pytest.raises(KeycloakAuthenticationError) as err:
  2218. admin.get_realm(realm_name=realm)
  2219. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized".*}\'')
  2220. # Freeze time to simulate the access token expiring
  2221. with freezegun.freeze_time("2023-02-25 10:05:00"):
  2222. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
  2223. assert admin.get_realm(realm_name=realm)
  2224. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
  2225. # Test bad refresh token, but first make sure access token has expired again
  2226. with freezegun.freeze_time("2023-02-25 10:10:00"):
  2227. admin.connection.custom_headers = {"Content-Type": "application/json"}
  2228. admin.connection.token["refresh_token"] = "bad"
  2229. with pytest.raises(KeycloakPostError) as err:
  2230. admin.get_realm(realm_name="test-refresh")
  2231. assert err.match(
  2232. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
  2233. )
  2234. admin.connection.get_token()
  2235. # Test post refresh
  2236. with freezegun.freeze_time("2023-02-25 10:15:00"):
  2237. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
  2238. admin.connection.token = None
  2239. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  2240. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
  2241. # Test update refresh
  2242. with freezegun.freeze_time("2023-02-25 10:25:00"):
  2243. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
  2244. admin.connection.token = None
  2245. assert (
  2246. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  2247. == dict()
  2248. )
  2249. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
  2250. # Test delete refresh
  2251. with freezegun.freeze_time("2023-02-25 10:35:00"):
  2252. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
  2253. admin.connection.token = None
  2254. assert admin.delete_realm(realm_name="test-refresh") == dict()
  2255. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
  2256. def test_get_required_actions(admin: KeycloakAdmin, realm: str):
  2257. """Test required actions.
  2258. :param admin: Keycloak Admin client
  2259. :type admin: KeycloakAdmin
  2260. :param realm: Keycloak realm
  2261. :type realm: str
  2262. """
  2263. admin.change_current_realm(realm)
  2264. ractions = admin.get_required_actions()
  2265. assert isinstance(ractions, list)
  2266. for ra in ractions:
  2267. for key in [
  2268. "alias",
  2269. "name",
  2270. "providerId",
  2271. "enabled",
  2272. "defaultAction",
  2273. "priority",
  2274. "config",
  2275. ]:
  2276. assert key in ra
  2277. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
  2278. """Test get required action by alias.
  2279. :param admin: Keycloak Admin client
  2280. :type admin: KeycloakAdmin
  2281. :param realm: Keycloak realm
  2282. :type realm: str
  2283. """
  2284. admin.change_current_realm(realm)
  2285. ractions = admin.get_required_actions()
  2286. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2287. assert ra in ractions
  2288. assert ra["alias"] == "UPDATE_PASSWORD"
  2289. assert admin.get_required_action_by_alias("does-not-exist") is None
  2290. def test_update_required_action(admin: KeycloakAdmin, realm: str):
  2291. """Test update required action.
  2292. :param admin: Keycloak Admin client
  2293. :type admin: KeycloakAdmin
  2294. :param realm: Keycloak realm
  2295. :type realm: str
  2296. """
  2297. admin.change_current_realm(realm)
  2298. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2299. old = copy.deepcopy(ra)
  2300. ra["enabled"] = False
  2301. admin.update_required_action("UPDATE_PASSWORD", ra)
  2302. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2303. assert old != newra
  2304. assert newra["enabled"] is False
  2305. def test_get_composite_client_roles_of_group(
  2306. admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str
  2307. ):
  2308. """Test get composite client roles of group.
  2309. :param admin: Keycloak Admin client
  2310. :type admin: KeycloakAdmin
  2311. :param realm: Keycloak realm
  2312. :type realm: str
  2313. :param client: Keycloak client
  2314. :type client: str
  2315. :param group: Keycloak group
  2316. :type group: str
  2317. :param composite_client_role: Composite client role
  2318. :type composite_client_role: str
  2319. """
  2320. admin.change_current_realm(realm)
  2321. role = admin.get_client_role(client, composite_client_role)
  2322. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  2323. result = admin.get_composite_client_roles_of_group(client, group)
  2324. assert role["id"] in [x["id"] for x in result]
  2325. def test_get_role_client_level_children(
  2326. admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str
  2327. ):
  2328. """Test get children of composite client role.
  2329. :param admin: Keycloak Admin client
  2330. :type admin: KeycloakAdmin
  2331. :param realm: Keycloak realm
  2332. :type realm: str
  2333. :param client: Keycloak client
  2334. :type client: str
  2335. :param composite_client_role: Composite client role
  2336. :type composite_client_role: str
  2337. :param client_role: Client role
  2338. :type client_role: str
  2339. """
  2340. admin.change_current_realm(realm)
  2341. child = admin.get_client_role(client, client_role)
  2342. parent = admin.get_client_role(client, composite_client_role)
  2343. res = admin.get_role_client_level_children(client, parent["id"])
  2344. assert child["id"] in [x["id"] for x in res]
  2345. def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple):
  2346. """Test upload certificate.
  2347. :param admin: Keycloak Admin client
  2348. :type admin: KeycloakAdmin
  2349. :param realm: Keycloak realm
  2350. :type realm: str
  2351. :param client: Keycloak client
  2352. :type client: str
  2353. :param selfsigned_cert: Selfsigned certificates
  2354. :type selfsigned_cert: tuple
  2355. """
  2356. admin.change_current_realm(realm)
  2357. cert, _ = selfsigned_cert
  2358. cert = cert.decode("utf-8").strip()
  2359. admin.upload_certificate(client, cert)
  2360. cl = admin.get_client(client)
  2361. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  2362. def test_get_bruteforce_status_for_user(
  2363. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2364. ):
  2365. """Test users.
  2366. :param admin: Keycloak Admin client
  2367. :type admin: KeycloakAdmin
  2368. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2369. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2370. :param realm: Keycloak realm
  2371. :type realm: str
  2372. """
  2373. oid, username, password = oid_with_credentials
  2374. admin.change_current_realm(realm)
  2375. # Turn on bruteforce protection
  2376. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2377. res = admin.get_realm(realm_name=realm)
  2378. assert res["bruteForceProtected"] is True
  2379. # Test login user with wrong credentials
  2380. try:
  2381. oid.token(username=username, password="wrongpassword")
  2382. except KeycloakAuthenticationError:
  2383. pass
  2384. user_id = admin.get_user_id(username)
  2385. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2386. assert bruteforce_status["numFailures"] == 1
  2387. # Cleanup
  2388. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2389. res = admin.get_realm(realm_name=realm)
  2390. assert res["bruteForceProtected"] is False
  2391. def test_clear_bruteforce_attempts_for_user(
  2392. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2393. ):
  2394. """Test users.
  2395. :param admin: Keycloak Admin client
  2396. :type admin: KeycloakAdmin
  2397. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2398. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2399. :param realm: Keycloak realm
  2400. :type realm: str
  2401. """
  2402. oid, username, password = oid_with_credentials
  2403. admin.change_current_realm(realm)
  2404. # Turn on bruteforce protection
  2405. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2406. res = admin.get_realm(realm_name=realm)
  2407. assert res["bruteForceProtected"] is True
  2408. # Test login user with wrong credentials
  2409. try:
  2410. oid.token(username=username, password="wrongpassword")
  2411. except KeycloakAuthenticationError:
  2412. pass
  2413. user_id = admin.get_user_id(username)
  2414. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2415. assert bruteforce_status["numFailures"] == 1
  2416. res = admin.clear_bruteforce_attempts_for_user(user_id)
  2417. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2418. assert bruteforce_status["numFailures"] == 0
  2419. # Cleanup
  2420. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2421. res = admin.get_realm(realm_name=realm)
  2422. assert res["bruteForceProtected"] is False
  2423. def test_clear_bruteforce_attempts_for_all_users(
  2424. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2425. ):
  2426. """Test users.
  2427. :param admin: Keycloak Admin client
  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. :param realm: Keycloak realm
  2432. :type realm: str
  2433. """
  2434. oid, username, password = oid_with_credentials
  2435. admin.change_current_realm(realm)
  2436. # Turn on bruteforce protection
  2437. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2438. res = admin.get_realm(realm_name=realm)
  2439. assert res["bruteForceProtected"] is True
  2440. # Test login user with wrong credentials
  2441. try:
  2442. oid.token(username=username, password="wrongpassword")
  2443. except KeycloakAuthenticationError:
  2444. pass
  2445. user_id = admin.get_user_id(username)
  2446. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2447. assert bruteforce_status["numFailures"] == 1
  2448. res = admin.clear_all_bruteforce_attempts()
  2449. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2450. assert bruteforce_status["numFailures"] == 0
  2451. # Cleanup
  2452. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2453. res = admin.get_realm(realm_name=realm)
  2454. assert res["bruteForceProtected"] is False
  2455. def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  2456. """Test that the default realm role is present in a brand new realm.
  2457. :param realm: Realm name
  2458. :type realm: str
  2459. :param admin: Keycloak admin
  2460. :type admin: KeycloakAdmin
  2461. """
  2462. admin.change_current_realm(realm)
  2463. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  2464. assert (
  2465. len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"])
  2466. == 1
  2467. )
  2468. def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  2469. """Test getter for the ID of the default realm role.
  2470. :param realm: Realm name
  2471. :type realm: str
  2472. :param admin: Keycloak admin
  2473. :type admin: KeycloakAdmin
  2474. """
  2475. admin.change_current_realm(realm)
  2476. assert (
  2477. admin.get_default_realm_role_id()
  2478. == [x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"][0]
  2479. )
  2480. def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  2481. """Test getting, adding and deleting default realm roles.
  2482. :param realm: Realm name
  2483. :type realm: str
  2484. :param admin: Keycloak admin
  2485. :type admin: KeycloakAdmin
  2486. """
  2487. admin.change_current_realm(realm)
  2488. # Test listing all default realm roles
  2489. roles = admin.get_realm_default_roles()
  2490. assert len(roles) == 2
  2491. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  2492. with pytest.raises(KeycloakGetError) as err:
  2493. admin.change_current_realm("doesnotexist")
  2494. admin.get_realm_default_roles()
  2495. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2496. admin.change_current_realm(realm)
  2497. # Test removing a default realm role
  2498. res = admin.remove_realm_default_roles(payload=[roles[0]])
  2499. assert res == {}
  2500. assert roles[0] not in admin.get_realm_default_roles()
  2501. assert len(admin.get_realm_default_roles()) == 1
  2502. with pytest.raises(KeycloakDeleteError) as err:
  2503. admin.remove_realm_default_roles(payload=[{"id": "bad id"}])
  2504. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2505. # Test adding a default realm role
  2506. res = admin.add_realm_default_roles(payload=[roles[0]])
  2507. assert res == {}
  2508. assert roles[0] in admin.get_realm_default_roles()
  2509. assert len(admin.get_realm_default_roles()) == 2
  2510. with pytest.raises(KeycloakPostError) as err:
  2511. admin.add_realm_default_roles(payload=[{"id": "bad id"}])
  2512. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2513. def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  2514. """Test clearing the keys cache.
  2515. :param realm: Realm name
  2516. :type realm: str
  2517. :param admin: Keycloak admin
  2518. :type admin: KeycloakAdmin
  2519. """
  2520. admin.change_current_realm(realm)
  2521. res = admin.clear_keys_cache()
  2522. assert res == {}
  2523. def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  2524. """Test clearing the realm cache.
  2525. :param realm: Realm name
  2526. :type realm: str
  2527. :param admin: Keycloak admin
  2528. :type admin: KeycloakAdmin
  2529. """
  2530. admin.change_current_realm(realm)
  2531. res = admin.clear_realm_cache()
  2532. assert res == {}
  2533. def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  2534. """Test clearing the user cache.
  2535. :param realm: Realm name
  2536. :type realm: str
  2537. :param admin: Keycloak admin
  2538. :type admin: KeycloakAdmin
  2539. """
  2540. admin.change_current_realm(realm)
  2541. res = admin.clear_user_cache()
  2542. assert res == {}
  2543. def test_initial_access_token(
  2544. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2545. ) -> None:
  2546. """Test initial access token and client creation.
  2547. :param admin: Keycloak admin
  2548. :type admin: KeycloakAdmin
  2549. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2550. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2551. """
  2552. res = admin.create_initial_access_token(2, 3)
  2553. assert "token" in res
  2554. assert res["count"] == 2
  2555. assert res["expiration"] == 3
  2556. oid, username, password = oid_with_credentials
  2557. client = str(uuid.uuid4())
  2558. secret = str(uuid.uuid4())
  2559. res = oid.register_client(
  2560. token=res["token"],
  2561. payload={
  2562. "name": "DynamicRegisteredClient",
  2563. "clientId": client,
  2564. "enabled": True,
  2565. "publicClient": False,
  2566. "protocol": "openid-connect",
  2567. "secret": secret,
  2568. "clientAuthenticatorType": "client-secret",
  2569. },
  2570. )
  2571. assert res["clientId"] == client
  2572. new_secret = str(uuid.uuid4())
  2573. res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
  2574. assert res["secret"] == new_secret
  2575. def test_refresh_token(admin: KeycloakAdmin):
  2576. """Test refresh token on connection even if it is expired.
  2577. :param admin: Keycloak admin
  2578. :type admin: KeycloakAdmin
  2579. """
  2580. admin.get_realms()
  2581. assert admin.connection.token is not None
  2582. admin.user_logout(admin.get_user_id(admin.connection.username))
  2583. admin.connection.refresh_token()
  2584. # async function start
  2585. @pytest.mark.asyncio
  2586. async def test_a_realms(admin: KeycloakAdmin):
  2587. """Test realms.
  2588. :param admin: Keycloak Admin client
  2589. :type admin: KeycloakAdmin
  2590. """
  2591. # Get realms
  2592. realms = await admin.a_get_realms()
  2593. assert len(realms) == 1, realms
  2594. assert "master" == realms[0]["realm"]
  2595. # Create a test realm
  2596. res = await admin.a_create_realm(payload={"realm": "test"})
  2597. assert res == b"", res
  2598. # Create the same realm, should fail
  2599. with pytest.raises(KeycloakPostError) as err:
  2600. res = await admin.a_create_realm(payload={"realm": "test"})
  2601. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  2602. # Create the same realm, skip_exists true
  2603. res = await admin.a_create_realm(payload={"realm": "test"}, skip_exists=True)
  2604. assert res == {"msg": "Already exists"}, res
  2605. # Get a single realm
  2606. res = await admin.a_get_realm(realm_name="test")
  2607. assert res["realm"] == "test"
  2608. # Get non-existing realm
  2609. with pytest.raises(KeycloakGetError) as err:
  2610. await admin.a_get_realm(realm_name="non-existent")
  2611. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  2612. # Update realm
  2613. res = await admin.a_update_realm(realm_name="test", payload={"accountTheme": "test"})
  2614. assert res == dict(), res
  2615. # Check that the update worked
  2616. res = await admin.a_get_realm(realm_name="test")
  2617. assert res["realm"] == "test"
  2618. assert res["accountTheme"] == "test"
  2619. # Update wrong payload
  2620. with pytest.raises(KeycloakPutError) as err:
  2621. await admin.a_update_realm(realm_name="test", payload={"wrong": "payload"})
  2622. assert err.match('400: b\'{"error":"Unrecognized field')
  2623. # Check that get realms returns both realms
  2624. realms = await admin.a_get_realms()
  2625. realm_names = [x["realm"] for x in realms]
  2626. assert len(realms) == 2, realms
  2627. assert "master" in realm_names, realm_names
  2628. assert "test" in realm_names, realm_names
  2629. # Delete the realm
  2630. res = await admin.a_delete_realm(realm_name="test")
  2631. assert res == dict(), res
  2632. # Check that the realm does not exist anymore
  2633. with pytest.raises(KeycloakGetError) as err:
  2634. await admin.a_get_realm(realm_name="test")
  2635. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2636. # Delete non-existing realm
  2637. with pytest.raises(KeycloakDeleteError) as err:
  2638. await admin.a_delete_realm(realm_name="non-existent")
  2639. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2640. @pytest.mark.asyncio
  2641. async def test_a_changing_of_realms(admin: KeycloakAdmin, realm: str):
  2642. """Test changing of realms.
  2643. :param admin: Keycloak Admin client
  2644. :type admin: KeycloakAdmin
  2645. :param realm: Keycloak realm
  2646. :type realm: str
  2647. """
  2648. assert await admin.a_get_current_realm() == "master"
  2649. await admin.a_change_current_realm(realm)
  2650. assert await admin.a_get_current_realm() == realm
  2651. @pytest.mark.asyncio
  2652. async def test_a_import_export_realms(admin: KeycloakAdmin, realm: str):
  2653. """Test import and export of realms.
  2654. :param admin: Keycloak Admin client
  2655. :type admin: KeycloakAdmin
  2656. :param realm: Keycloak realm
  2657. :type realm: str
  2658. """
  2659. await admin.a_change_current_realm(realm)
  2660. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=True)
  2661. assert realm_export != dict(), realm_export
  2662. await admin.a_delete_realm(realm_name=realm)
  2663. admin.realm_name = "master"
  2664. res = await admin.a_import_realm(payload=realm_export)
  2665. assert res == b"", res
  2666. # Test bad import
  2667. with pytest.raises(KeycloakPostError) as err:
  2668. await admin.a_import_realm(payload=dict())
  2669. assert err.match(
  2670. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'' # noqa: E501
  2671. )
  2672. @pytest.mark.asyncio
  2673. async def test_a_partial_import_realm(admin: KeycloakAdmin, realm: str):
  2674. """Test partial import of realm configuration.
  2675. :param admin: Keycloak Admin client
  2676. :type admin: KeycloakAdmin
  2677. :param realm: Keycloak realm
  2678. :type realm: str
  2679. """
  2680. test_realm_role = str(uuid.uuid4())
  2681. test_user = str(uuid.uuid4())
  2682. test_client = str(uuid.uuid4())
  2683. await admin.a_change_current_realm(realm)
  2684. client_id = await admin.a_create_client(payload={"name": test_client, "clientId": test_client})
  2685. realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=False)
  2686. client_config = [
  2687. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  2688. ][0]
  2689. # delete before partial import
  2690. await admin.a_delete_client(client_id)
  2691. payload = {
  2692. "ifResourceExists": "SKIP",
  2693. "id": realm_export["id"],
  2694. "realm": realm,
  2695. "clients": [client_config],
  2696. "roles": {"realm": [{"name": test_realm_role}]},
  2697. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  2698. }
  2699. # check add
  2700. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  2701. assert res["added"] == 3
  2702. # check skip
  2703. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  2704. assert res["skipped"] == 3
  2705. # check overwrite
  2706. payload["ifResourceExists"] = "OVERWRITE"
  2707. res = await admin.a_partial_import_realm(realm_name=realm, payload=payload)
  2708. assert res["overwritten"] == 3
  2709. @pytest.mark.asyncio
  2710. async def test_a_users(admin: KeycloakAdmin, realm: str):
  2711. """Test users.
  2712. :param admin: Keycloak Admin client
  2713. :type admin: KeycloakAdmin
  2714. :param realm: Keycloak realm
  2715. :type realm: str
  2716. """
  2717. await admin.a_change_current_realm(realm)
  2718. # Check no users present
  2719. users = await admin.a_get_users()
  2720. assert users == list(), users
  2721. # Test create user
  2722. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  2723. assert user_id is not None, user_id
  2724. # Test create the same user
  2725. with pytest.raises(KeycloakPostError) as err:
  2726. await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  2727. assert err.match(".*User exists with same.*")
  2728. # Test create the same user, exists_ok true
  2729. user_id_2 = await admin.a_create_user(
  2730. payload={"username": "test", "email": "test@test.test"}, exist_ok=True
  2731. )
  2732. assert user_id == user_id_2
  2733. # Test get user
  2734. user = await admin.a_get_user(user_id=user_id)
  2735. assert user["username"] == "test", user["username"]
  2736. assert user["email"] == "test@test.test", user["email"]
  2737. # Test update user
  2738. res = await admin.a_update_user(user_id=user_id, payload={"firstName": "Test"})
  2739. assert res == dict(), res
  2740. user = await admin.a_get_user(user_id=user_id)
  2741. assert user["firstName"] == "Test"
  2742. # Test update user fail
  2743. with pytest.raises(KeycloakPutError) as err:
  2744. await admin.a_update_user(user_id=user_id, payload={"wrong": "payload"})
  2745. assert err.match('400: b\'{"error":"Unrecognized field')
  2746. # Test disable user
  2747. res = await admin.a_disable_user(user_id=user_id)
  2748. assert res == {}, res
  2749. assert not (await admin.a_get_user(user_id=user_id))["enabled"]
  2750. # Test enable user
  2751. res = await admin.a_enable_user(user_id=user_id)
  2752. assert res == {}, res
  2753. assert (await admin.a_get_user(user_id=user_id))["enabled"]
  2754. # Test get users again
  2755. users = await admin.a_get_users()
  2756. usernames = [x["username"] for x in users]
  2757. assert "test" in usernames
  2758. # Test users counts
  2759. count = await admin.a_users_count()
  2760. assert count == 1, count
  2761. # Test users count with query
  2762. count = await admin.a_users_count(query={"username": "notpresent"})
  2763. assert count == 0
  2764. # Test user groups
  2765. groups = await admin.a_get_user_groups(user_id=user["id"])
  2766. assert len(groups) == 0
  2767. # Test user groups bad id
  2768. with pytest.raises(KeycloakGetError) as err:
  2769. await admin.a_get_user_groups(user_id="does-not-exist")
  2770. assert err.match(USER_NOT_FOUND_REGEX)
  2771. # Test logout
  2772. res = await admin.a_user_logout(user_id=user["id"])
  2773. assert res == dict(), res
  2774. # Test logout fail
  2775. with pytest.raises(KeycloakPostError) as err:
  2776. await admin.a_user_logout(user_id="non-existent-id")
  2777. assert err.match(USER_NOT_FOUND_REGEX)
  2778. # Test consents
  2779. res = await admin.a_user_consents(user_id=user["id"])
  2780. assert len(res) == 0, res
  2781. # Test consents fail
  2782. with pytest.raises(KeycloakGetError) as err:
  2783. await admin.a_user_consents(user_id="non-existent-id")
  2784. assert err.match(USER_NOT_FOUND_REGEX)
  2785. # Test delete user
  2786. res = await admin.a_delete_user(user_id=user_id)
  2787. assert res == dict(), res
  2788. with pytest.raises(KeycloakGetError) as err:
  2789. await admin.a_get_user(user_id=user_id)
  2790. err.match(USER_NOT_FOUND_REGEX)
  2791. # Test delete fail
  2792. with pytest.raises(KeycloakDeleteError) as err:
  2793. await admin.a_delete_user(user_id="non-existent-id")
  2794. assert err.match(USER_NOT_FOUND_REGEX)
  2795. @pytest.mark.asyncio
  2796. async def test_a_enable_disable_all_users(admin: KeycloakAdmin, realm: str):
  2797. """Test enable and disable all users.
  2798. :param admin: Keycloak Admin client
  2799. :type admin: KeycloakAdmin
  2800. :param realm: Keycloak realm
  2801. :type realm: str
  2802. """
  2803. admin.change_current_realm(realm)
  2804. user_id_1 = await admin.a_create_user(
  2805. payload={"username": "test", "email": "test@test.test", "enabled": True}
  2806. )
  2807. user_id_2 = await admin.a_create_user(
  2808. payload={"username": "test2", "email": "test2@test.test", "enabled": True}
  2809. )
  2810. user_id_3 = await admin.a_create_user(
  2811. payload={"username": "test3", "email": "test3@test.test", "enabled": True}
  2812. )
  2813. assert (await admin.a_get_user(user_id_1))["enabled"]
  2814. assert (await admin.a_get_user(user_id_2))["enabled"]
  2815. assert (await admin.a_get_user(user_id_3))["enabled"]
  2816. await admin.a_disable_all_users()
  2817. assert not (await admin.a_get_user(user_id_1))["enabled"]
  2818. assert not (await admin.a_get_user(user_id_2))["enabled"]
  2819. assert not (await admin.a_get_user(user_id_3))["enabled"]
  2820. await admin.a_enable_all_users()
  2821. assert (await admin.a_get_user(user_id_1))["enabled"]
  2822. assert (await admin.a_get_user(user_id_2))["enabled"]
  2823. assert (await admin.a_get_user(user_id_3))["enabled"]
  2824. @pytest.mark.asyncio
  2825. async def test_a_users_roles(admin: KeycloakAdmin, realm: str):
  2826. """Test users roles.
  2827. :param admin: Keycloak Admin client
  2828. :type admin: KeycloakAdmin
  2829. :param realm: Keycloak realm
  2830. :type realm: str
  2831. """
  2832. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  2833. # Test all level user roles
  2834. client_id = await admin.a_create_client(
  2835. payload={"name": "test-client", "clientId": "test-client"}
  2836. )
  2837. await admin.a_create_client_role(client_role_id=client_id, payload={"name": "test-role"})
  2838. await admin.a_assign_client_role(
  2839. client_id=client_id,
  2840. user_id=user_id,
  2841. roles=[admin.get_client_role(client_id=client_id, role_name="test-role")],
  2842. )
  2843. all_roles = await admin.a_get_all_roles_of_user(user_id=user_id)
  2844. realm_roles = all_roles["realmMappings"]
  2845. assert len(realm_roles) == 1, realm_roles
  2846. client_roles = all_roles["clientMappings"]
  2847. assert len(client_roles) == 1, client_roles
  2848. # Test all level user roles fail
  2849. with pytest.raises(KeycloakGetError) as err:
  2850. await admin.a_get_all_roles_of_user(user_id="non-existent-id")
  2851. err.match('404: b\'{"error":"User not found"')
  2852. await admin.a_delete_user(user_id)
  2853. await admin.a_delete_client(client_id)
  2854. @pytest.mark.asyncio
  2855. async def test_a_users_pagination(admin: KeycloakAdmin, realm: str):
  2856. """Test user pagination.
  2857. :param admin: Keycloak Admin client
  2858. :type admin: KeycloakAdmin
  2859. :param realm: Keycloak realm
  2860. :type realm: str
  2861. """
  2862. await admin.a_change_current_realm(realm)
  2863. for ind in range(admin.PAGE_SIZE + 50):
  2864. username = f"user_{ind}"
  2865. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  2866. users = await admin.a_get_users()
  2867. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  2868. users = await admin.a_get_users(query={"first": 100})
  2869. assert len(users) == 50, len(users)
  2870. users = await admin.a_get_users(query={"max": 20})
  2871. assert len(users) == 20, len(users)
  2872. @pytest.mark.asyncio
  2873. async def test_a_user_groups_pagination(admin: KeycloakAdmin, realm: str):
  2874. """Test user groups pagination.
  2875. :param admin: Keycloak Admin client
  2876. :type admin: KeycloakAdmin
  2877. :param realm: Keycloak realm
  2878. :type realm: str
  2879. """
  2880. await admin.a_change_current_realm(realm)
  2881. user_id = await admin.a_create_user(
  2882. payload={"username": "username_1", "email": "username_1@test.test"}
  2883. )
  2884. for ind in range(admin.PAGE_SIZE + 50):
  2885. group_name = f"group_{ind}"
  2886. group_id = await admin.a_create_group(payload={"name": group_name})
  2887. await admin.a_group_user_add(user_id=user_id, group_id=group_id)
  2888. groups = await admin.a_get_user_groups(user_id=user_id)
  2889. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  2890. groups = await admin.a_get_user_groups(
  2891. user_id=user_id, query={"first": 100, "max": -1, "search": ""}
  2892. )
  2893. assert len(groups) == 50, len(groups)
  2894. groups = await admin.a_get_user_groups(
  2895. user_id=user_id, query={"max": 20, "first": -1, "search": ""}
  2896. )
  2897. assert len(groups) == 20, len(groups)
  2898. @pytest.mark.asyncio
  2899. async def test_a_idps(admin: KeycloakAdmin, realm: str):
  2900. """Test IDPs.
  2901. :param admin: Keycloak Admin client
  2902. :type admin: KeycloakAdmin
  2903. :param realm: Keycloak realm
  2904. :type realm: str
  2905. """
  2906. await admin.a_change_current_realm(realm)
  2907. # Create IDP
  2908. res = await admin.a_create_idp(
  2909. payload=dict(
  2910. providerId="github", alias="github", config=dict(clientId="test", clientSecret="test")
  2911. )
  2912. )
  2913. assert res == b"", res
  2914. # Test create idp fail
  2915. with pytest.raises(KeycloakPostError) as err:
  2916. await admin.a_create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  2917. assert err.match("Invalid identity provider id"), err
  2918. # Test listing
  2919. idps = await admin.a_get_idps()
  2920. assert len(idps) == 1
  2921. assert "github" == idps[0]["alias"]
  2922. # Test get idp
  2923. idp = await admin.a_get_idp("github")
  2924. assert "github" == idp["alias"]
  2925. assert idp.get("config")
  2926. assert "test" == idp["config"]["clientId"]
  2927. assert "**********" == idp["config"]["clientSecret"]
  2928. # Test get idp fail
  2929. with pytest.raises(KeycloakGetError) as err:
  2930. await admin.a_get_idp("does-not-exist")
  2931. assert err.match(HTTP_404_REGEX)
  2932. # Test IdP update
  2933. res = await admin.a_update_idp(idp_alias="github", payload=idps[0])
  2934. assert res == {}, res
  2935. # Test adding a mapper
  2936. res = await admin.a_add_mapper_to_idp(
  2937. idp_alias="github",
  2938. payload={
  2939. "identityProviderAlias": "github",
  2940. "identityProviderMapper": "github-user-attribute-mapper",
  2941. "name": "test",
  2942. },
  2943. )
  2944. assert res == b"", res
  2945. # Test mapper fail
  2946. with pytest.raises(KeycloakPostError) as err:
  2947. await admin.a_add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
  2948. assert err.match(HTTP_404_REGEX)
  2949. # Test IdP mappers listing
  2950. idp_mappers = await admin.a_get_idp_mappers(idp_alias="github")
  2951. assert len(idp_mappers) == 1
  2952. # Test IdP mapper update
  2953. res = await admin.a_update_mapper_in_idp(
  2954. idp_alias="github",
  2955. mapper_id=idp_mappers[0]["id"],
  2956. # For an obscure reason, keycloak expect all fields
  2957. payload={
  2958. "id": idp_mappers[0]["id"],
  2959. "identityProviderAlias": "github-alias",
  2960. "identityProviderMapper": "github-user-attribute-mapper",
  2961. "name": "test",
  2962. "config": idp_mappers[0]["config"],
  2963. },
  2964. )
  2965. assert res == dict(), res
  2966. # Test delete
  2967. res = await admin.a_delete_idp(idp_alias="github")
  2968. assert res == dict(), res
  2969. # Test delete fail
  2970. with pytest.raises(KeycloakDeleteError) as err:
  2971. await admin.a_delete_idp(idp_alias="does-not-exist")
  2972. assert err.match(HTTP_404_REGEX)
  2973. @pytest.mark.asyncio
  2974. async def test_a_user_credentials(admin: KeycloakAdmin, user: str):
  2975. """Test user credentials.
  2976. :param admin: Keycloak Admin client
  2977. :type admin: KeycloakAdmin
  2978. :param user: Keycloak user
  2979. :type user: str
  2980. """
  2981. res = await admin.a_set_user_password(user_id=user, password="booya", temporary=True)
  2982. assert res == dict(), res
  2983. # Test user password set fail
  2984. with pytest.raises(KeycloakPutError) as err:
  2985. await admin.a_set_user_password(user_id="does-not-exist", password="")
  2986. assert err.match(USER_NOT_FOUND_REGEX)
  2987. credentials = await admin.a_get_credentials(user_id=user)
  2988. assert len(credentials) == 1
  2989. assert credentials[0]["type"] == "password", credentials
  2990. # Test get credentials fail
  2991. with pytest.raises(KeycloakGetError) as err:
  2992. await admin.a_get_credentials(user_id="does-not-exist")
  2993. assert err.match(USER_NOT_FOUND_REGEX)
  2994. res = await admin.a_delete_credential(user_id=user, credential_id=credentials[0]["id"])
  2995. assert res == dict(), res
  2996. # Test delete fail
  2997. with pytest.raises(KeycloakDeleteError) as err:
  2998. await admin.a_delete_credential(user_id=user, credential_id="does-not-exist")
  2999. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  3000. @pytest.mark.asyncio
  3001. async def test_a_social_logins(admin: KeycloakAdmin, user: str):
  3002. """Test social logins.
  3003. :param admin: Keycloak Admin client
  3004. :type admin: KeycloakAdmin
  3005. :param user: Keycloak user
  3006. :type user: str
  3007. """
  3008. res = await admin.a_add_user_social_login(
  3009. user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
  3010. )
  3011. assert res == dict(), res
  3012. await admin.a_add_user_social_login(
  3013. user_id=user, provider_id="github", provider_userid="test", provider_username="test"
  3014. )
  3015. assert res == dict(), res
  3016. # Test add social login fail
  3017. with pytest.raises(KeycloakPostError) as err:
  3018. await admin.a_add_user_social_login(
  3019. user_id="does-not-exist",
  3020. provider_id="does-not-exist",
  3021. provider_userid="test",
  3022. provider_username="test",
  3023. )
  3024. assert err.match(USER_NOT_FOUND_REGEX)
  3025. res = await admin.a_get_user_social_logins(user_id=user)
  3026. assert res == list(), res
  3027. # Test get social logins fail
  3028. with pytest.raises(KeycloakGetError) as err:
  3029. await admin.a_get_user_social_logins(user_id="does-not-exist")
  3030. assert err.match(USER_NOT_FOUND_REGEX)
  3031. res = await admin.a_delete_user_social_login(user_id=user, provider_id="gitlab")
  3032. assert res == {}, res
  3033. res = await admin.a_delete_user_social_login(user_id=user, provider_id="github")
  3034. assert res == {}, res
  3035. with pytest.raises(KeycloakDeleteError) as err:
  3036. await admin.a_delete_user_social_login(user_id=user, provider_id="instagram")
  3037. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  3038. @pytest.mark.asyncio
  3039. async def test_a_server_info(admin: KeycloakAdmin):
  3040. """Test server info.
  3041. :param admin: Keycloak Admin client
  3042. :type admin: KeycloakAdmin
  3043. """
  3044. info = await admin.a_get_server_info()
  3045. assert set(info.keys()).issubset(
  3046. {
  3047. "systemInfo",
  3048. "memoryInfo",
  3049. "profileInfo",
  3050. "features",
  3051. "themes",
  3052. "socialProviders",
  3053. "identityProviders",
  3054. "providers",
  3055. "protocolMapperTypes",
  3056. "builtinProtocolMappers",
  3057. "clientInstallations",
  3058. "componentTypes",
  3059. "passwordPolicies",
  3060. "enums",
  3061. "cryptoInfo",
  3062. "features",
  3063. }
  3064. ), info.keys()
  3065. @pytest.mark.asyncio
  3066. async def test_a_groups(admin: KeycloakAdmin, user: str):
  3067. """Test groups.
  3068. :param admin: Keycloak Admin client
  3069. :type admin: KeycloakAdmin
  3070. :param user: Keycloak user
  3071. :type user: str
  3072. """
  3073. # Test get groups
  3074. groups = await admin.a_get_groups()
  3075. assert len(groups) == 0
  3076. # Test create group
  3077. group_id = await admin.a_create_group(payload={"name": "main-group"})
  3078. assert group_id is not None, group_id
  3079. # Test group count
  3080. count = await admin.a_groups_count()
  3081. assert count.get("count") == 1, count
  3082. # Test group count with query
  3083. count = await admin.a_groups_count(query={"search": "notpresent"})
  3084. assert count.get("count") == 0
  3085. # Test create subgroups
  3086. subgroup_id_1 = await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3087. subgroup_id_2 = await admin.a_create_group(payload={"name": "subgroup-2"}, parent=group_id)
  3088. # Test create group fail
  3089. with pytest.raises(KeycloakPostError) as err:
  3090. await admin.a_create_group(payload={"name": "subgroup-1"}, parent=group_id)
  3091. assert err.match("409"), err
  3092. # Test skip exists OK
  3093. subgroup_id_1_eq = await admin.a_create_group(
  3094. payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
  3095. )
  3096. assert subgroup_id_1_eq is None
  3097. # Test get groups again
  3098. groups = await admin.a_get_groups()
  3099. assert len(groups) == 1, groups
  3100. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3101. assert groups[0]["id"] == group_id
  3102. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3103. # Test get groups query
  3104. groups = await admin.a_get_groups(query={"max": 10})
  3105. assert len(groups) == 1, groups
  3106. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  3107. assert groups[0]["id"] == group_id
  3108. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  3109. # Test get group
  3110. res = await admin.a_get_group(group_id=subgroup_id_1)
  3111. assert res["id"] == subgroup_id_1, res
  3112. assert res["name"] == "subgroup-1"
  3113. assert res["path"] == "/main-group/subgroup-1"
  3114. # Test get group fail
  3115. with pytest.raises(KeycloakGetError) as err:
  3116. await admin.a_get_group(group_id="does-not-exist")
  3117. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3118. # Create 1 more subgroup
  3119. subsubgroup_id_1 = await admin.a_create_group(
  3120. payload={"name": "subsubgroup-1"}, parent=subgroup_id_2
  3121. )
  3122. main_group = await admin.a_get_group(group_id=group_id)
  3123. # Test nested searches
  3124. subgroup_2 = await admin.a_get_group(group_id=subgroup_id_2)
  3125. res = await admin.a_get_subgroups(
  3126. group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1"
  3127. )
  3128. assert res is not None, res
  3129. assert res["id"] == subsubgroup_id_1
  3130. # Test nested search from main group
  3131. res = await admin.a_get_subgroups(
  3132. group=await admin.a_get_group(group_id=group_id, full_hierarchy=True),
  3133. path="/main-group/subgroup-2/subsubgroup-1",
  3134. )
  3135. assert res["id"] == subsubgroup_id_1
  3136. # Test nested search from all groups
  3137. res = await admin.a_get_groups(full_hierarchy=True)
  3138. assert len(res) == 1
  3139. assert len(res[0]["subGroups"]) == 2
  3140. assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_1][0]["subGroups"]) == 0
  3141. assert len([x for x in res[0]["subGroups"] if x["id"] == subgroup_id_2][0]["subGroups"]) == 1
  3142. # Test that query params are not allowed for full hierarchy
  3143. with pytest.raises(ValueError) as err:
  3144. await admin.a_get_group_children(group_id=group_id, full_hierarchy=True, query={"max": 10})
  3145. # Test that query params are passed
  3146. if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] == "latest" or Version(
  3147. os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"]
  3148. ) >= Version("23"):
  3149. res = await admin.a_get_group_children(group_id=group_id, query={"max": 1})
  3150. assert len(res) == 1
  3151. assert err.match("Cannot use both query and full_hierarchy parameters")
  3152. main_group_id_2 = await admin.a_create_group(payload={"name": "main-group-2"})
  3153. assert len(await admin.a_get_groups(full_hierarchy=True)) == 2
  3154. # Test empty search
  3155. res = await admin.a_get_subgroups(group=main_group, path="/none")
  3156. assert res is None, res
  3157. # Test get group by path
  3158. res = await admin.a_get_group_by_path(path="/main-group/subgroup-1")
  3159. assert res is not None, res
  3160. assert res["id"] == subgroup_id_1, res
  3161. with pytest.raises(KeycloakGetError) as err:
  3162. await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  3163. assert err.match('404: b\'{"error":"Group path does not exist".*}\'')
  3164. res = await admin.a_get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  3165. assert res is not None, res
  3166. assert res["id"] == subsubgroup_id_1
  3167. res = await admin.a_get_group_by_path(path="/main-group")
  3168. assert res is not None, res
  3169. assert res["id"] == group_id, res
  3170. # Test group members
  3171. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3172. assert len(res) == 0, res
  3173. # Test fail group members
  3174. with pytest.raises(KeycloakGetError) as err:
  3175. await admin.a_get_group_members(group_id="does-not-exist")
  3176. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  3177. res = await admin.a_group_user_add(user_id=user, group_id=subgroup_id_2)
  3178. assert res == dict(), res
  3179. res = await admin.a_get_group_members(group_id=subgroup_id_2)
  3180. assert len(res) == 1, res
  3181. assert res[0]["id"] == user
  3182. # Test get group members query
  3183. res = await admin.a_get_group_members(group_id=subgroup_id_2, query={"max": 10})
  3184. assert len(res) == 1, res
  3185. assert res[0]["id"] == user
  3186. with pytest.raises(KeycloakDeleteError) as err:
  3187. await admin.a_group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  3188. assert err.match(USER_NOT_FOUND_REGEX), err
  3189. res = await admin.a_group_user_remove(user_id=user, group_id=subgroup_id_2)
  3190. assert res == dict(), res
  3191. # Test set permissions
  3192. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=True)
  3193. assert res["enabled"], res
  3194. res = await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled=False)
  3195. assert not res["enabled"], res
  3196. with pytest.raises(KeycloakPutError) as err:
  3197. await admin.a_group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  3198. assert err.match(UNKOWN_ERROR_REGEX), err
  3199. # Test update group
  3200. res = await admin.a_update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  3201. assert res == dict(), res
  3202. assert (await admin.a_get_group(group_id=subgroup_id_2))["name"] == "new-subgroup-2"
  3203. # test update fail
  3204. with pytest.raises(KeycloakPutError) as err:
  3205. await admin.a_update_group(group_id="does-not-exist", payload=dict())
  3206. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3207. # Test delete
  3208. res = await admin.a_delete_group(group_id=group_id)
  3209. assert res == dict(), res
  3210. res = await admin.a_delete_group(group_id=main_group_id_2)
  3211. assert res == dict(), res
  3212. assert len(await admin.a_get_groups()) == 0
  3213. # Test delete fail
  3214. with pytest.raises(KeycloakDeleteError) as err:
  3215. await admin.a_delete_group(group_id="does-not-exist")
  3216. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  3217. @pytest.mark.asyncio
  3218. async def test_a_clients(admin: KeycloakAdmin, realm: str):
  3219. """Test clients.
  3220. :param admin: Keycloak Admin client
  3221. :type admin: KeycloakAdmin
  3222. :param realm: Keycloak realm
  3223. :type realm: str
  3224. """
  3225. await admin.a_change_current_realm(realm)
  3226. # Test get clients
  3227. clients = await admin.a_get_clients()
  3228. assert len(clients) == 6, clients
  3229. assert {x["name"] for x in clients} == set(
  3230. [
  3231. "${client_admin-cli}",
  3232. "${client_security-admin-console}",
  3233. "${client_account-console}",
  3234. "${client_broker}",
  3235. "${client_account}",
  3236. "${client_realm-management}",
  3237. ]
  3238. ), clients
  3239. # Test create client
  3240. client_id = await admin.a_create_client(
  3241. payload={"name": "test-client", "clientId": "test-client"}
  3242. )
  3243. assert client_id, client_id
  3244. with pytest.raises(KeycloakPostError) as err:
  3245. await admin.a_create_client(payload={"name": "test-client", "clientId": "test-client"})
  3246. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  3247. client_id_2 = await admin.a_create_client(
  3248. payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True
  3249. )
  3250. assert client_id == client_id_2, client_id_2
  3251. # Test get client
  3252. res = await admin.a_get_client(client_id=client_id)
  3253. assert res["clientId"] == "test-client", res
  3254. assert res["name"] == "test-client", res
  3255. assert res["id"] == client_id, res
  3256. with pytest.raises(KeycloakGetError) as err:
  3257. await admin.a_get_client(client_id="does-not-exist")
  3258. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3259. assert len(await admin.a_get_clients()) == 7
  3260. # Test get client id
  3261. assert await admin.a_get_client_id(client_id="test-client") == client_id
  3262. assert await admin.a_get_client_id(client_id="does-not-exist") is None
  3263. # Test update client
  3264. res = await admin.a_update_client(client_id=client_id, payload={"name": "test-client-change"})
  3265. assert res == dict(), res
  3266. with pytest.raises(KeycloakPutError) as err:
  3267. await admin.a_update_client(
  3268. client_id="does-not-exist", payload={"name": "test-client-change"}
  3269. )
  3270. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3271. # Test client mappers
  3272. res = await admin.a_get_mappers_from_client(client_id=client_id)
  3273. assert len(res) == 0
  3274. with pytest.raises(KeycloakPostError) as err:
  3275. await admin.a_add_mapper_to_client(client_id="does-not-exist", payload=dict())
  3276. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3277. res = await admin.a_add_mapper_to_client(
  3278. client_id=client_id,
  3279. payload={
  3280. "name": "test-mapper",
  3281. "protocol": "openid-connect",
  3282. "protocolMapper": "oidc-usermodel-attribute-mapper",
  3283. },
  3284. )
  3285. assert res == b""
  3286. assert len(await admin.a_get_mappers_from_client(client_id=client_id)) == 1
  3287. mapper = (await admin.a_get_mappers_from_client(client_id=client_id))[0]
  3288. with pytest.raises(KeycloakPutError) as err:
  3289. await admin.a_update_client_mapper(
  3290. client_id=client_id, mapper_id="does-not-exist", payload=dict()
  3291. )
  3292. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3293. mapper["config"]["user.attribute"] = "test"
  3294. res = await admin.a_update_client_mapper(
  3295. client_id=client_id, mapper_id=mapper["id"], payload=mapper
  3296. )
  3297. assert res == dict()
  3298. res = await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3299. assert res == dict()
  3300. with pytest.raises(KeycloakDeleteError) as err:
  3301. await admin.a_remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  3302. assert err.match('404: b\'{"error":"Model not found".*}\'')
  3303. # Test client sessions
  3304. with pytest.raises(KeycloakGetError) as err:
  3305. await admin.a_get_client_all_sessions(client_id="does-not-exist")
  3306. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3307. assert await admin.a_get_client_all_sessions(client_id=client_id) == list()
  3308. assert await admin.a_get_client_sessions_stats() == list()
  3309. # Test authz
  3310. auth_client_id = await admin.a_create_client(
  3311. payload={
  3312. "name": "authz-client",
  3313. "clientId": "authz-client",
  3314. "authorizationServicesEnabled": True,
  3315. "serviceAccountsEnabled": True,
  3316. }
  3317. )
  3318. res = await admin.a_get_client_authz_settings(client_id=auth_client_id)
  3319. assert res["allowRemoteResourceManagement"]
  3320. assert res["decisionStrategy"] == "UNANIMOUS"
  3321. assert len(res["policies"]) >= 0
  3322. with pytest.raises(KeycloakGetError) as err:
  3323. await admin.a_get_client_authz_settings(client_id=client_id)
  3324. assert err.match(HTTP_404_REGEX)
  3325. # Authz resources
  3326. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3327. assert len(res) == 1
  3328. assert res[0]["name"] == "Default Resource"
  3329. with pytest.raises(KeycloakGetError) as err:
  3330. await admin.a_get_client_authz_resources(client_id=client_id)
  3331. assert err.match(HTTP_404_REGEX)
  3332. res = await admin.a_create_client_authz_resource(
  3333. client_id=auth_client_id, payload={"name": "test-resource"}
  3334. )
  3335. assert res["name"] == "test-resource", res
  3336. test_resource_id = res["_id"]
  3337. res = await admin.a_get_client_authz_resource(
  3338. client_id=auth_client_id, resource_id=test_resource_id
  3339. )
  3340. assert res["_id"] == test_resource_id, res
  3341. assert res["name"] == "test-resource", res
  3342. with pytest.raises(KeycloakPostError) as err:
  3343. await admin.a_create_client_authz_resource(
  3344. client_id=auth_client_id, payload={"name": "test-resource"}
  3345. )
  3346. assert err.match('409: b\'{"error":"invalid_request"')
  3347. assert await admin.a_create_client_authz_resource(
  3348. client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
  3349. ) == {"msg": "Already exists"}
  3350. res = await admin.a_get_client_authz_resources(client_id=auth_client_id)
  3351. assert len(res) == 2
  3352. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  3353. res = await admin.a_create_client_authz_resource(
  3354. client_id=auth_client_id, payload={"name": "temp-resource"}
  3355. )
  3356. assert res["name"] == "temp-resource", res
  3357. temp_resource_id: str = res["_id"]
  3358. # Test update authz resources
  3359. await admin.a_update_client_authz_resource(
  3360. client_id=auth_client_id,
  3361. resource_id=temp_resource_id,
  3362. payload={"name": "temp-updated-resource"},
  3363. )
  3364. res = await admin.a_get_client_authz_resource(
  3365. client_id=auth_client_id, resource_id=temp_resource_id
  3366. )
  3367. assert res["name"] == "temp-updated-resource", res
  3368. with pytest.raises(KeycloakPutError) as err:
  3369. await admin.a_update_client_authz_resource(
  3370. client_id=auth_client_id,
  3371. resource_id="invalid_resource_id",
  3372. payload={"name": "temp-updated-resource"},
  3373. )
  3374. assert err.match("404: b''"), err
  3375. await admin.a_delete_client_authz_resource(
  3376. client_id=auth_client_id, resource_id=temp_resource_id
  3377. )
  3378. with pytest.raises(KeycloakGetError) as err:
  3379. await admin.a_get_client_authz_resource(
  3380. client_id=auth_client_id, resource_id=temp_resource_id
  3381. )
  3382. assert err.match("404: b''")
  3383. # Authz policies
  3384. res = await admin.a_get_client_authz_policies(client_id=auth_client_id)
  3385. assert len(res) == 1, res
  3386. assert res[0]["name"] == "Default Policy"
  3387. with pytest.raises(KeycloakGetError) as err:
  3388. await admin.a_get_client_authz_policies(client_id="does-not-exist")
  3389. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3390. role_id = (await admin.a_get_realm_role(role_name="offline_access"))["id"]
  3391. res = await admin.a_create_client_authz_role_based_policy(
  3392. client_id=auth_client_id,
  3393. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3394. )
  3395. assert res["name"] == "test-authz-rb-policy", res
  3396. with pytest.raises(KeycloakPostError) as err:
  3397. await admin.a_create_client_authz_role_based_policy(
  3398. client_id=auth_client_id,
  3399. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3400. )
  3401. assert err.match('409: b\'{"error":"Policy with name')
  3402. assert await admin.a_create_client_authz_role_based_policy(
  3403. client_id=auth_client_id,
  3404. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  3405. skip_exists=True,
  3406. ) == {"msg": "Already exists"}
  3407. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 2
  3408. res = await admin.a_create_client_authz_role_based_policy(
  3409. client_id=auth_client_id,
  3410. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  3411. )
  3412. res2 = await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3413. assert res["id"] == res2["id"]
  3414. await admin.a_delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3415. with pytest.raises(KeycloakGetError) as err:
  3416. await admin.a_get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  3417. assert err.match("404: b''")
  3418. res = await admin.a_create_client_authz_policy(
  3419. client_id=auth_client_id,
  3420. payload={
  3421. "name": "test-authz-policy",
  3422. "type": "time",
  3423. "config": {"hourEnd": "18", "hour": "9"},
  3424. },
  3425. )
  3426. assert res["name"] == "test-authz-policy", res
  3427. with pytest.raises(KeycloakPostError) as err:
  3428. await admin.a_create_client_authz_policy(
  3429. client_id=auth_client_id,
  3430. payload={
  3431. "name": "test-authz-policy",
  3432. "type": "time",
  3433. "config": {"hourEnd": "18", "hour": "9"},
  3434. },
  3435. )
  3436. assert err.match('409: b\'{"error":"Policy with name')
  3437. assert await admin.a_create_client_authz_policy(
  3438. client_id=auth_client_id,
  3439. payload={
  3440. "name": "test-authz-policy",
  3441. "type": "time",
  3442. "config": {"hourEnd": "18", "hour": "9"},
  3443. },
  3444. skip_exists=True,
  3445. ) == {"msg": "Already exists"}
  3446. assert len(await admin.a_get_client_authz_policies(client_id=auth_client_id)) == 3
  3447. # Test authz permissions
  3448. res = await admin.a_get_client_authz_permissions(client_id=auth_client_id)
  3449. assert len(res) == 1, res
  3450. assert res[0]["name"] == "Default Permission"
  3451. with pytest.raises(KeycloakGetError) as err:
  3452. await admin.a_get_client_authz_permissions(client_id="does-not-exist")
  3453. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3454. res = await admin.a_create_client_authz_resource_based_permission(
  3455. client_id=auth_client_id,
  3456. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3457. )
  3458. assert res, res
  3459. assert res["name"] == "test-permission-rb"
  3460. assert res["resources"] == [test_resource_id]
  3461. with pytest.raises(KeycloakPostError) as err:
  3462. await admin.a_create_client_authz_resource_based_permission(
  3463. client_id=auth_client_id,
  3464. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3465. )
  3466. assert err.match('409: b\'{"error":"Policy with name')
  3467. assert await admin.a_create_client_authz_resource_based_permission(
  3468. client_id=auth_client_id,
  3469. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  3470. skip_exists=True,
  3471. ) == {"msg": "Already exists"}
  3472. assert len(await admin.a_get_client_authz_permissions(client_id=auth_client_id)) == 2
  3473. # Test authz scopes
  3474. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3475. assert len(res) == 0, res
  3476. with pytest.raises(KeycloakGetError) as err:
  3477. await admin.a_get_client_authz_scopes(client_id=client_id)
  3478. assert err.match(HTTP_404_REGEX)
  3479. res = await admin.a_create_client_authz_scopes(
  3480. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  3481. )
  3482. assert res["name"] == "test-authz-scope", res
  3483. with pytest.raises(KeycloakPostError) as err:
  3484. await admin.a_create_client_authz_scopes(
  3485. client_id="invalid_client_id", payload={"name": "test-authz-scope"}
  3486. )
  3487. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3488. assert await admin.a_create_client_authz_scopes(
  3489. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  3490. )
  3491. res = await admin.a_get_client_authz_scopes(client_id=auth_client_id)
  3492. assert len(res) == 1
  3493. assert {x["name"] for x in res} == {"test-authz-scope"}
  3494. # Test service account user
  3495. res = await admin.a_get_client_service_account_user(client_id=auth_client_id)
  3496. assert res["username"] == "service-account-authz-client", res
  3497. with pytest.raises(KeycloakGetError) as err:
  3498. await admin.a_get_client_service_account_user(client_id=client_id)
  3499. assert ('b\'{"error":"Service account not enabled for the client' in str(err)) or err.match(
  3500. UNKOWN_ERROR_REGEX
  3501. )
  3502. # Test delete client
  3503. res = await admin.a_delete_client(client_id=auth_client_id)
  3504. assert res == dict(), res
  3505. with pytest.raises(KeycloakDeleteError) as err:
  3506. await admin.a_delete_client(client_id=auth_client_id)
  3507. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3508. # Test client credentials
  3509. await admin.a_create_client(
  3510. payload={
  3511. "name": "test-confidential",
  3512. "enabled": True,
  3513. "protocol": "openid-connect",
  3514. "publicClient": False,
  3515. "redirectUris": ["http://localhost/*"],
  3516. "webOrigins": ["+"],
  3517. "clientId": "test-confidential",
  3518. "secret": "test-secret",
  3519. "clientAuthenticatorType": "client-secret",
  3520. }
  3521. )
  3522. with pytest.raises(KeycloakGetError) as err:
  3523. await admin.a_get_client_secrets(client_id="does-not-exist")
  3524. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3525. secrets = await admin.a_get_client_secrets(
  3526. client_id=await admin.a_get_client_id(client_id="test-confidential")
  3527. )
  3528. assert secrets == {"type": "secret", "value": "test-secret"}
  3529. with pytest.raises(KeycloakPostError) as err:
  3530. await admin.a_generate_client_secrets(client_id="does-not-exist")
  3531. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3532. res = await admin.a_generate_client_secrets(
  3533. client_id=await admin.a_get_client_id(client_id="test-confidential")
  3534. )
  3535. assert res
  3536. assert (
  3537. await admin.a_get_client_secrets(
  3538. client_id=await admin.a_get_client_id(client_id="test-confidential")
  3539. )
  3540. == res
  3541. )
  3542. @pytest.mark.asyncio
  3543. async def test_a_realm_roles(admin: KeycloakAdmin, realm: str):
  3544. """Test realm roles.
  3545. :param admin: Keycloak Admin client
  3546. :type admin: KeycloakAdmin
  3547. :param realm: Keycloak realm
  3548. :type realm: str
  3549. """
  3550. await admin.a_change_current_realm(realm)
  3551. # Test get realm roles
  3552. roles = await admin.a_get_realm_roles()
  3553. assert len(roles) == 3, roles
  3554. role_names = [x["name"] for x in roles]
  3555. assert "uma_authorization" in role_names, role_names
  3556. assert "offline_access" in role_names, role_names
  3557. # Test get realm roles with search text
  3558. searched_roles = await admin.a_get_realm_roles(search_text="uma_a")
  3559. searched_role_names = [x["name"] for x in searched_roles]
  3560. assert "uma_authorization" in searched_role_names, searched_role_names
  3561. assert "offline_access" not in searched_role_names, searched_role_names
  3562. # Test empty members
  3563. with pytest.raises(KeycloakGetError) as err:
  3564. await admin.a_get_realm_role_members(role_name="does-not-exist")
  3565. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3566. members = await admin.a_get_realm_role_members(role_name="offline_access")
  3567. assert members == list(), members
  3568. # Test create realm role
  3569. role_id = await admin.a_create_realm_role(
  3570. payload={"name": "test-realm-role"}, skip_exists=True
  3571. )
  3572. assert role_id, role_id
  3573. with pytest.raises(KeycloakPostError) as err:
  3574. await admin.a_create_realm_role(payload={"name": "test-realm-role"})
  3575. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  3576. role_id_2 = await admin.a_create_realm_role(
  3577. payload={"name": "test-realm-role"}, skip_exists=True
  3578. )
  3579. assert role_id == role_id_2
  3580. # Test get realm role by its id
  3581. role_id = (await admin.a_get_realm_role(role_name="test-realm-role"))["id"]
  3582. res = await admin.a_get_realm_role_by_id(role_id)
  3583. assert res["name"] == "test-realm-role"
  3584. # Test update realm role
  3585. res = await admin.a_update_realm_role(
  3586. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  3587. )
  3588. assert res == dict(), res
  3589. with pytest.raises(KeycloakPutError) as err:
  3590. await admin.a_update_realm_role(
  3591. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  3592. )
  3593. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3594. # Test realm role user assignment
  3595. user_id = await admin.a_create_user(
  3596. payload={"username": "role-testing", "email": "test@test.test"}
  3597. )
  3598. with pytest.raises(KeycloakPostError) as err:
  3599. await admin.a_assign_realm_roles(user_id=user_id, roles=["bad"])
  3600. assert err.match(UNKOWN_ERROR_REGEX), err
  3601. res = await admin.a_assign_realm_roles(
  3602. user_id=user_id,
  3603. roles=[
  3604. await admin.a_get_realm_role(role_name="offline_access"),
  3605. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  3606. ],
  3607. )
  3608. assert res == dict(), res
  3609. assert admin.get_user(user_id=user_id)["username"] in [
  3610. x["username"] for x in await admin.a_get_realm_role_members(role_name="offline_access")
  3611. ]
  3612. assert admin.get_user(user_id=user_id)["username"] in [
  3613. x["username"]
  3614. for x in await admin.a_get_realm_role_members(role_name="test-realm-role-update")
  3615. ]
  3616. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  3617. assert len(roles) == 3
  3618. assert "offline_access" in [x["name"] for x in roles]
  3619. assert "test-realm-role-update" in [x["name"] for x in roles]
  3620. with pytest.raises(KeycloakDeleteError) as err:
  3621. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  3622. assert err.match(UNKOWN_ERROR_REGEX), err
  3623. res = await admin.a_delete_realm_roles_of_user(
  3624. user_id=user_id, roles=[await admin.a_get_realm_role(role_name="offline_access")]
  3625. )
  3626. assert res == dict(), res
  3627. assert await admin.a_get_realm_role_members(role_name="offline_access") == list()
  3628. roles = await admin.a_get_realm_roles_of_user(user_id=user_id)
  3629. assert len(roles) == 2
  3630. assert "offline_access" not in [x["name"] for x in roles]
  3631. assert "test-realm-role-update" in [x["name"] for x in roles]
  3632. roles = await admin.a_get_available_realm_roles_of_user(user_id=user_id)
  3633. assert len(roles) == 2
  3634. assert "offline_access" in [x["name"] for x in roles]
  3635. assert "uma_authorization" in [x["name"] for x in roles]
  3636. # Test realm role group assignment
  3637. group_id = await admin.a_create_group(payload={"name": "test-group"})
  3638. with pytest.raises(KeycloakPostError) as err:
  3639. await admin.a_assign_group_realm_roles(group_id=group_id, roles=["bad"])
  3640. assert err.match(UNKOWN_ERROR_REGEX), err
  3641. res = await admin.a_assign_group_realm_roles(
  3642. group_id=group_id,
  3643. roles=[
  3644. await admin.a_get_realm_role(role_name="offline_access"),
  3645. await admin.a_get_realm_role(role_name="test-realm-role-update"),
  3646. ],
  3647. )
  3648. assert res == dict(), res
  3649. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  3650. assert len(roles) == 2
  3651. assert "offline_access" in [x["name"] for x in roles]
  3652. assert "test-realm-role-update" in [x["name"] for x in roles]
  3653. with pytest.raises(KeycloakDeleteError) as err:
  3654. await admin.a_delete_group_realm_roles(group_id=group_id, roles=["bad"])
  3655. assert err.match(UNKOWN_ERROR_REGEX)
  3656. res = await admin.a_delete_group_realm_roles(
  3657. group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
  3658. )
  3659. assert res == dict(), res
  3660. roles = await admin.a_get_group_realm_roles(group_id=group_id)
  3661. assert len(roles) == 1
  3662. assert "test-realm-role-update" in [x["name"] for x in roles]
  3663. # Test composite realm roles
  3664. composite_role = await admin.a_create_realm_role(payload={"name": "test-composite-role"})
  3665. with pytest.raises(KeycloakPostError) as err:
  3666. await admin.a_add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  3667. assert err.match(UNKOWN_ERROR_REGEX), err
  3668. res = await admin.a_add_composite_realm_roles_to_role(
  3669. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  3670. )
  3671. assert res == dict(), res
  3672. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  3673. assert len(res) == 1
  3674. assert "test-realm-role-update" in res[0]["name"]
  3675. with pytest.raises(KeycloakGetError) as err:
  3676. await admin.a_get_composite_realm_roles_of_role(role_name="bad")
  3677. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3678. res = await admin.a_get_composite_realm_roles_of_user(user_id=user_id)
  3679. assert len(res) == 4
  3680. assert "offline_access" in {x["name"] for x in res}
  3681. assert "test-realm-role-update" in {x["name"] for x in res}
  3682. assert "uma_authorization" in {x["name"] for x in res}
  3683. with pytest.raises(KeycloakGetError) as err:
  3684. await admin.a_get_composite_realm_roles_of_user(user_id="bad")
  3685. assert err.match(USER_NOT_FOUND_REGEX), err
  3686. with pytest.raises(KeycloakDeleteError) as err:
  3687. await admin.a_remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  3688. assert err.match(UNKOWN_ERROR_REGEX), err
  3689. res = await admin.a_remove_composite_realm_roles_to_role(
  3690. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  3691. )
  3692. assert res == dict(), res
  3693. res = await admin.a_get_composite_realm_roles_of_role(role_name=composite_role)
  3694. assert len(res) == 0
  3695. # Test realm role group list
  3696. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update")
  3697. assert len(res) == 1
  3698. assert res[0]["id"] == group_id
  3699. with pytest.raises(KeycloakGetError) as err:
  3700. await admin.a_get_realm_role_groups(role_name="non-existent-role")
  3701. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3702. # Test with query params
  3703. res = await admin.a_get_realm_role_groups(role_name="test-realm-role-update", query={"max": 1})
  3704. assert len(res) == 1
  3705. # Test delete realm role
  3706. res = await admin.a_delete_realm_role(role_name=composite_role)
  3707. assert res == dict(), res
  3708. with pytest.raises(KeycloakDeleteError) as err:
  3709. await admin.a_delete_realm_role(role_name=composite_role)
  3710. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  3711. @pytest.mark.asyncio
  3712. @pytest.mark.parametrize(
  3713. "testcase, arg_brief_repr, includes_attributes",
  3714. [
  3715. ("brief True", {"brief_representation": True}, False),
  3716. ("brief False", {"brief_representation": False}, True),
  3717. ("default", {}, False),
  3718. ],
  3719. )
  3720. async def test_a_role_attributes(
  3721. admin: KeycloakAdmin,
  3722. realm: str,
  3723. client: str,
  3724. arg_brief_repr: dict,
  3725. includes_attributes: bool,
  3726. testcase: str,
  3727. ):
  3728. """Test getting role attributes for bulk calls.
  3729. :param admin: Keycloak admin
  3730. :type admin: KeycloakAdmin
  3731. :param realm: Keycloak realm
  3732. :type realm: str
  3733. :param client: Keycloak client
  3734. :type client: str
  3735. :param arg_brief_repr: Brief representation
  3736. :type arg_brief_repr: dict
  3737. :param includes_attributes: Indicator whether to include attributes
  3738. :type includes_attributes: bool
  3739. :param testcase: Test case
  3740. :type testcase: str
  3741. """
  3742. # setup
  3743. attribute_role = "test-realm-role-w-attr"
  3744. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  3745. role_id = await admin.a_create_realm_role(
  3746. payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  3747. )
  3748. assert role_id, role_id
  3749. cli_role_id = await admin.a_create_client_role(
  3750. client, payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  3751. )
  3752. assert cli_role_id, cli_role_id
  3753. if not includes_attributes:
  3754. test_attrs = None
  3755. # tests
  3756. roles = await admin.a_get_realm_roles(**arg_brief_repr)
  3757. roles_filtered = [role for role in roles if role["name"] == role_id]
  3758. assert roles_filtered, roles_filtered
  3759. role = roles_filtered[0]
  3760. assert role.get("attributes") == test_attrs, testcase
  3761. roles = await admin.a_get_client_roles(client, **arg_brief_repr)
  3762. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  3763. assert roles_filtered, roles_filtered
  3764. role = roles_filtered[0]
  3765. assert role.get("attributes") == test_attrs, testcase
  3766. # cleanup
  3767. res = await admin.a_delete_realm_role(role_name=attribute_role)
  3768. assert res == dict(), res
  3769. res = await admin.a_delete_client_role(client, role_name=attribute_role)
  3770. assert res == dict(), res
  3771. @pytest.mark.asyncio
  3772. async def test_a_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
  3773. """Test client realm roles.
  3774. :param admin: Keycloak admin
  3775. :type admin: KeycloakAdmin
  3776. :param realm: Keycloak realm
  3777. :type realm: str
  3778. """
  3779. await admin.a_change_current_realm(realm)
  3780. # Test get realm roles
  3781. roles = await admin.a_get_realm_roles()
  3782. assert len(roles) == 3, roles
  3783. role_names = [x["name"] for x in roles]
  3784. assert "uma_authorization" in role_names, role_names
  3785. assert "offline_access" in role_names, role_names
  3786. # create realm role for test
  3787. role_id = await admin.a_create_realm_role(
  3788. payload={"name": "test-realm-role"}, skip_exists=True
  3789. )
  3790. assert role_id, role_id
  3791. # Test realm role client assignment
  3792. client_id = await admin.a_create_client(
  3793. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  3794. )
  3795. with pytest.raises(KeycloakPostError) as err:
  3796. await admin.a_assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  3797. assert err.match(UNKOWN_ERROR_REGEX), err
  3798. res = await admin.a_assign_realm_roles_to_client_scope(
  3799. client_id=client_id,
  3800. roles=[
  3801. await admin.a_get_realm_role(role_name="offline_access"),
  3802. await admin.a_get_realm_role(role_name="test-realm-role"),
  3803. ],
  3804. )
  3805. assert res == dict(), res
  3806. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  3807. assert len(roles) == 2
  3808. client_role_names = [x["name"] for x in roles]
  3809. assert "offline_access" in client_role_names, client_role_names
  3810. assert "test-realm-role" in client_role_names, client_role_names
  3811. assert "uma_authorization" not in client_role_names, client_role_names
  3812. # Test remove realm role of client
  3813. with pytest.raises(KeycloakDeleteError) as err:
  3814. await admin.a_delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  3815. assert err.match(UNKOWN_ERROR_REGEX), err
  3816. res = await admin.a_delete_realm_roles_of_client_scope(
  3817. client_id=client_id, roles=[await admin.a_get_realm_role(role_name="offline_access")]
  3818. )
  3819. assert res == dict(), res
  3820. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  3821. assert len(roles) == 1
  3822. assert "test-realm-role" in [x["name"] for x in roles]
  3823. res = await admin.a_delete_realm_roles_of_client_scope(
  3824. client_id=client_id, roles=[await admin.a_get_realm_role(role_name="test-realm-role")]
  3825. )
  3826. assert res == dict(), res
  3827. roles = await admin.a_get_realm_roles_of_client_scope(client_id=client_id)
  3828. assert len(roles) == 0
  3829. @pytest.mark.asyncio
  3830. async def test_a_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
  3831. """Test client assignment of other client roles.
  3832. :param admin: Keycloak admin
  3833. :type admin: KeycloakAdmin
  3834. :param realm: Keycloak realm
  3835. :type realm: str
  3836. :param client: Keycloak client
  3837. :type client: str
  3838. """
  3839. await admin.a_change_current_realm(realm)
  3840. client_id = await admin.a_create_client(
  3841. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  3842. )
  3843. # Test get client roles
  3844. roles = await admin.a_get_client_roles_of_client_scope(client_id, client)
  3845. assert len(roles) == 0, roles
  3846. # create client role for test
  3847. client_role_id = await admin.a_create_client_role(
  3848. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  3849. )
  3850. assert client_role_id, client_role_id
  3851. # Test client role assignment to other client
  3852. with pytest.raises(KeycloakPostError) as err:
  3853. await admin.a_assign_client_roles_to_client_scope(
  3854. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  3855. )
  3856. assert err.match(UNKOWN_ERROR_REGEX), err
  3857. res = await admin.a_assign_client_roles_to_client_scope(
  3858. client_id=client_id,
  3859. client_roles_owner_id=client,
  3860. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  3861. )
  3862. assert res == dict(), res
  3863. roles = await admin.a_get_client_roles_of_client_scope(
  3864. client_id=client_id, client_roles_owner_id=client
  3865. )
  3866. assert len(roles) == 1
  3867. client_role_names = [x["name"] for x in roles]
  3868. assert "client-role-test" in client_role_names, client_role_names
  3869. # Test remove realm role of client
  3870. with pytest.raises(KeycloakDeleteError) as err:
  3871. await admin.a_delete_client_roles_of_client_scope(
  3872. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  3873. )
  3874. assert err.match(UNKOWN_ERROR_REGEX), err
  3875. res = await admin.a_delete_client_roles_of_client_scope(
  3876. client_id=client_id,
  3877. client_roles_owner_id=client,
  3878. roles=[await admin.a_get_client_role(client_id=client, role_name="client-role-test")],
  3879. )
  3880. assert res == dict(), res
  3881. roles = await admin.a_get_client_roles_of_client_scope(
  3882. client_id=client_id, client_roles_owner_id=client
  3883. )
  3884. assert len(roles) == 0
  3885. @pytest.mark.asyncio
  3886. async def test_a_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  3887. """Test client assignment of default client scopes.
  3888. :param admin: Keycloak admin
  3889. :type admin: KeycloakAdmin
  3890. :param realm: Keycloak realm
  3891. :type realm: str
  3892. :param client: Keycloak client
  3893. :type client: str
  3894. """
  3895. await admin.a_change_current_realm(realm)
  3896. client_id = await admin.a_create_client(
  3897. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  3898. )
  3899. # Test get client default scopes
  3900. # keycloak default roles: web-origins, acr, profile, roles, email
  3901. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  3902. assert len(default_client_scopes) in [6, 5], default_client_scopes
  3903. # Test add a client scope to client default scopes
  3904. default_client_scope = "test-client-default-scope"
  3905. new_client_scope = {
  3906. "name": default_client_scope,
  3907. "description": f"Test Client Scope: {default_client_scope}",
  3908. "protocol": "openid-connect",
  3909. "attributes": {},
  3910. }
  3911. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  3912. new_default_client_scope_data = {
  3913. "realm": realm,
  3914. "client": client_id,
  3915. "clientScopeId": new_client_scope_id,
  3916. }
  3917. await admin.a_add_client_default_client_scope(
  3918. client_id, new_client_scope_id, new_default_client_scope_data
  3919. )
  3920. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  3921. assert len(default_client_scopes) in [6, 7], default_client_scopes
  3922. # Test remove a client default scope
  3923. await admin.a_delete_client_default_client_scope(client_id, new_client_scope_id)
  3924. default_client_scopes = await admin.a_get_client_default_client_scopes(client_id)
  3925. assert len(default_client_scopes) in [5, 6], default_client_scopes
  3926. @pytest.mark.asyncio
  3927. async def test_a_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  3928. """Test client assignment of optional client scopes.
  3929. :param admin: Keycloak admin
  3930. :type admin: KeycloakAdmin
  3931. :param realm: Keycloak realm
  3932. :type realm: str
  3933. :param client: Keycloak client
  3934. :type client: str
  3935. """
  3936. await admin.a_change_current_realm(realm)
  3937. client_id = await admin.a_create_client(
  3938. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  3939. )
  3940. # Test get client optional scopes
  3941. # keycloak optional roles: microprofile-jwt, offline_access, address, phone
  3942. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  3943. assert len(optional_client_scopes) == 4, optional_client_scopes
  3944. # Test add a client scope to client optional scopes
  3945. optional_client_scope = "test-client-optional-scope"
  3946. new_client_scope = {
  3947. "name": optional_client_scope,
  3948. "description": f"Test Client Scope: {optional_client_scope}",
  3949. "protocol": "openid-connect",
  3950. "attributes": {},
  3951. }
  3952. new_client_scope_id = await admin.a_create_client_scope(new_client_scope, skip_exists=False)
  3953. new_optional_client_scope_data = {
  3954. "realm": realm,
  3955. "client": client_id,
  3956. "clientScopeId": new_client_scope_id,
  3957. }
  3958. await admin.a_add_client_optional_client_scope(
  3959. client_id, new_client_scope_id, new_optional_client_scope_data
  3960. )
  3961. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  3962. assert len(optional_client_scopes) == 5, optional_client_scopes
  3963. # Test remove a client optional scope
  3964. await admin.a_delete_client_optional_client_scope(client_id, new_client_scope_id)
  3965. optional_client_scopes = await admin.a_get_client_optional_client_scopes(client_id)
  3966. assert len(optional_client_scopes) == 4, optional_client_scopes
  3967. @pytest.mark.asyncio
  3968. async def test_a_client_roles(admin: KeycloakAdmin, client: str):
  3969. """Test client roles.
  3970. :param admin: Keycloak Admin client
  3971. :type admin: KeycloakAdmin
  3972. :param client: Keycloak client
  3973. :type client: str
  3974. """
  3975. # Test get client roles
  3976. res = await admin.a_get_client_roles(client_id=client)
  3977. assert len(res) == 0
  3978. with pytest.raises(KeycloakGetError) as err:
  3979. await admin.a_get_client_roles(client_id="bad")
  3980. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  3981. # Test create client role
  3982. client_role_id = await admin.a_create_client_role(
  3983. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  3984. )
  3985. with pytest.raises(KeycloakPostError) as err:
  3986. await admin.a_create_client_role(
  3987. client_role_id=client, payload={"name": "client-role-test"}
  3988. )
  3989. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  3990. client_role_id_2 = await admin.a_create_client_role(
  3991. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  3992. )
  3993. assert client_role_id == client_role_id_2
  3994. # Test get client role
  3995. res = await admin.a_get_client_role(client_id=client, role_name="client-role-test")
  3996. assert res["name"] == client_role_id
  3997. with pytest.raises(KeycloakGetError) as err:
  3998. await admin.a_get_client_role(client_id=client, role_name="bad")
  3999. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4000. res_ = await admin.a_get_client_role_id(client_id=client, role_name="client-role-test")
  4001. assert res_ == res["id"]
  4002. with pytest.raises(KeycloakGetError) as err:
  4003. await admin.a_get_client_role_id(client_id=client, role_name="bad")
  4004. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4005. assert len(await admin.a_get_client_roles(client_id=client)) == 1
  4006. # Test update client role
  4007. res = await admin.a_update_client_role(
  4008. client_id=client, role_name="client-role-test", payload={"name": "client-role-test-update"}
  4009. )
  4010. assert res == dict()
  4011. with pytest.raises(KeycloakPutError) as err:
  4012. res = await admin.a_update_client_role(
  4013. client_id=client,
  4014. role_name="client-role-test",
  4015. payload={"name": "client-role-test-update"},
  4016. )
  4017. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4018. # Test user with client role
  4019. res = await admin.a_get_client_role_members(
  4020. client_id=client, role_name="client-role-test-update"
  4021. )
  4022. assert len(res) == 0
  4023. with pytest.raises(KeycloakGetError) as err:
  4024. await admin.a_get_client_role_members(client_id=client, role_name="bad")
  4025. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4026. user_id = await admin.a_create_user(payload={"username": "test", "email": "test@test.test"})
  4027. with pytest.raises(KeycloakPostError) as err:
  4028. await admin.a_assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  4029. assert err.match(UNKOWN_ERROR_REGEX), err
  4030. res = await admin.a_assign_client_role(
  4031. user_id=user_id,
  4032. client_id=client,
  4033. roles=[
  4034. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4035. ],
  4036. )
  4037. assert res == dict()
  4038. assert (
  4039. len(
  4040. await admin.a_get_client_role_members(
  4041. client_id=client, role_name="client-role-test-update"
  4042. )
  4043. )
  4044. == 1
  4045. )
  4046. roles = await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)
  4047. assert len(roles) == 1, roles
  4048. with pytest.raises(KeycloakGetError) as err:
  4049. await admin.a_get_client_roles_of_user(user_id=user_id, client_id="bad")
  4050. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4051. roles = await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  4052. assert len(roles) == 1, roles
  4053. with pytest.raises(KeycloakGetError) as err:
  4054. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4055. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4056. roles = await admin.a_get_available_client_roles_of_user(user_id=user_id, client_id=client)
  4057. assert len(roles) == 0, roles
  4058. with pytest.raises(KeycloakGetError) as err:
  4059. await admin.a_get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  4060. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4061. with pytest.raises(KeycloakDeleteError) as err:
  4062. await admin.a_delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  4063. assert err.match(UNKOWN_ERROR_REGEX), err
  4064. await admin.a_delete_client_roles_of_user(
  4065. user_id=user_id,
  4066. client_id=client,
  4067. roles=[
  4068. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4069. ],
  4070. )
  4071. assert len(await admin.a_get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  4072. # Test groups and client roles
  4073. res = await admin.a_get_client_role_groups(
  4074. client_id=client, role_name="client-role-test-update"
  4075. )
  4076. assert len(res) == 0
  4077. with pytest.raises(KeycloakGetError) as err:
  4078. await admin.a_get_client_role_groups(client_id=client, role_name="bad")
  4079. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4080. group_id = await admin.a_create_group(payload={"name": "test-group"})
  4081. res = await admin.a_get_group_client_roles(group_id=group_id, client_id=client)
  4082. assert len(res) == 0
  4083. with pytest.raises(KeycloakGetError) as err:
  4084. await admin.a_get_group_client_roles(group_id=group_id, client_id="bad")
  4085. assert err.match(CLIENT_NOT_FOUND_REGEX)
  4086. with pytest.raises(KeycloakPostError) as err:
  4087. await admin.a_assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4088. assert err.match(UNKOWN_ERROR_REGEX), err
  4089. res = await admin.a_assign_group_client_roles(
  4090. group_id=group_id,
  4091. client_id=client,
  4092. roles=[
  4093. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4094. ],
  4095. )
  4096. assert res == dict()
  4097. assert (
  4098. len(
  4099. await admin.a_get_client_role_groups(
  4100. client_id=client, role_name="client-role-test-update"
  4101. )
  4102. )
  4103. == 1
  4104. )
  4105. assert len(await admin.a_get_group_client_roles(group_id=group_id, client_id=client)) == 1
  4106. with pytest.raises(KeycloakDeleteError) as err:
  4107. await admin.a_delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  4108. assert err.match(UNKOWN_ERROR_REGEX), err
  4109. res = await admin.a_delete_group_client_roles(
  4110. group_id=group_id,
  4111. client_id=client,
  4112. roles=[
  4113. await admin.a_get_client_role(client_id=client, role_name="client-role-test-update")
  4114. ],
  4115. )
  4116. assert res == dict()
  4117. # Test composite client roles
  4118. with pytest.raises(KeycloakPostError) as err:
  4119. await admin.a_add_composite_client_roles_to_role(
  4120. client_role_id=client, role_name="client-role-test-update", roles=["bad"]
  4121. )
  4122. assert err.match(UNKOWN_ERROR_REGEX), err
  4123. res = await admin.a_add_composite_client_roles_to_role(
  4124. client_role_id=client,
  4125. role_name="client-role-test-update",
  4126. roles=[await admin.a_get_realm_role(role_name="offline_access")],
  4127. )
  4128. assert res == dict()
  4129. assert (await admin.a_get_client_role(client_id=client, role_name="client-role-test-update"))[
  4130. "composite"
  4131. ]
  4132. # Test delete of client role
  4133. res = await admin.a_delete_client_role(
  4134. client_role_id=client, role_name="client-role-test-update"
  4135. )
  4136. assert res == dict()
  4137. with pytest.raises(KeycloakDeleteError) as err:
  4138. await admin.a_delete_client_role(
  4139. client_role_id=client, role_name="client-role-test-update"
  4140. )
  4141. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  4142. # Test of roles by id - Get role
  4143. await admin.a_create_client_role(
  4144. client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
  4145. )
  4146. role = await admin.a_get_client_role(client_id=client, role_name="client-role-by-id-test")
  4147. res = await admin.a_get_role_by_id(role_id=role["id"])
  4148. assert res["name"] == "client-role-by-id-test"
  4149. with pytest.raises(KeycloakGetError) as err:
  4150. await admin.a_get_role_by_id(role_id="bad")
  4151. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4152. # Test of roles by id - Update role
  4153. res = await admin.a_update_role_by_id(
  4154. role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
  4155. )
  4156. assert res == dict()
  4157. with pytest.raises(KeycloakPutError) as err:
  4158. res = await admin.a_update_role_by_id(
  4159. role_id="bad", payload={"name": "client-role-by-id-test-update"}
  4160. )
  4161. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4162. # Test of roles by id - Delete role
  4163. res = await admin.a_delete_role_by_id(role_id=role["id"])
  4164. assert res == dict()
  4165. with pytest.raises(KeycloakDeleteError) as err:
  4166. await admin.a_delete_role_by_id(role_id="bad")
  4167. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  4168. @pytest.mark.asyncio
  4169. async def test_a_enable_token_exchange(admin: KeycloakAdmin, realm: str):
  4170. """Test enable token exchange.
  4171. :param admin: Keycloak Admin client
  4172. :type admin: KeycloakAdmin
  4173. :param realm: Keycloak realm
  4174. :type realm: str
  4175. :raises AssertionError: In case of bad configuration
  4176. """
  4177. # Test enabling token exchange between two confidential clients
  4178. await admin.a_change_current_realm(realm)
  4179. # Create test clients
  4180. source_client_id = await admin.a_create_client(
  4181. payload={"name": "Source Client", "clientId": "source-client"}
  4182. )
  4183. target_client_id = await admin.a_create_client(
  4184. payload={"name": "Target Client", "clientId": "target-client"}
  4185. )
  4186. for c in await admin.a_get_clients():
  4187. if c["clientId"] == "realm-management":
  4188. realm_management_id = c["id"]
  4189. break
  4190. else:
  4191. raise AssertionError("Missing realm management client")
  4192. # Enable permissions on the Superset client
  4193. await admin.a_update_client_management_permissions(
  4194. payload={"enabled": True}, client_id=target_client_id
  4195. )
  4196. # Fetch various IDs and strings needed when creating the permission
  4197. token_exchange_permission_id = (
  4198. await admin.a_get_client_management_permissions(client_id=target_client_id)
  4199. )["scopePermissions"]["token-exchange"]
  4200. scopes = await admin.a_get_client_authz_policy_scopes(
  4201. client_id=realm_management_id, policy_id=token_exchange_permission_id
  4202. )
  4203. for s in scopes:
  4204. if s["name"] == "token-exchange":
  4205. token_exchange_scope_id = s["id"]
  4206. break
  4207. else:
  4208. raise AssertionError("Missing token-exchange scope")
  4209. resources = await admin.a_get_client_authz_policy_resources(
  4210. client_id=realm_management_id, policy_id=token_exchange_permission_id
  4211. )
  4212. for r in resources:
  4213. if r["name"] == f"client.resource.{target_client_id}":
  4214. token_exchange_resource_id = r["_id"]
  4215. break
  4216. else:
  4217. raise AssertionError("Missing client resource")
  4218. # Create a client policy for source client
  4219. policy_name = "Exchange source client token with target client token"
  4220. client_policy_id = (
  4221. await admin.a_create_client_authz_client_policy(
  4222. payload={
  4223. "type": "client",
  4224. "logic": "POSITIVE",
  4225. "decisionStrategy": "UNANIMOUS",
  4226. "name": policy_name,
  4227. "clients": [source_client_id],
  4228. },
  4229. client_id=realm_management_id,
  4230. )
  4231. )["id"]
  4232. policies = await admin.a_get_client_authz_client_policies(client_id=realm_management_id)
  4233. for policy in policies:
  4234. if policy["name"] == policy_name:
  4235. assert policy["clients"] == [source_client_id]
  4236. break
  4237. else:
  4238. raise AssertionError("Missing client policy")
  4239. # Update permissions on the target client to reference this policy
  4240. permission_name = (
  4241. await admin.a_get_client_authz_scope_permission(
  4242. client_id=realm_management_id, scope_id=token_exchange_permission_id
  4243. )
  4244. )["name"]
  4245. await admin.a_update_client_authz_scope_permission(
  4246. payload={
  4247. "id": token_exchange_permission_id,
  4248. "name": permission_name,
  4249. "type": "scope",
  4250. "logic": "POSITIVE",
  4251. "decisionStrategy": "UNANIMOUS",
  4252. "resources": [token_exchange_resource_id],
  4253. "scopes": [token_exchange_scope_id],
  4254. "policies": [client_policy_id],
  4255. },
  4256. client_id=realm_management_id,
  4257. scope_id=token_exchange_permission_id,
  4258. )
  4259. # Create permissions on the target client to reference this policy
  4260. await admin.a_create_client_authz_scope_permission(
  4261. payload={
  4262. "id": "some-id",
  4263. "name": "test-permission",
  4264. "type": "scope",
  4265. "logic": "POSITIVE",
  4266. "decisionStrategy": "UNANIMOUS",
  4267. "resources": [token_exchange_resource_id],
  4268. "scopes": [token_exchange_scope_id],
  4269. "policies": [client_policy_id],
  4270. },
  4271. client_id=realm_management_id,
  4272. )
  4273. permission_name = (
  4274. await admin.a_get_client_authz_scope_permission(
  4275. client_id=realm_management_id, scope_id=token_exchange_permission_id
  4276. )
  4277. )["name"]
  4278. assert permission_name.startswith("token-exchange.permission.client.")
  4279. with pytest.raises(KeycloakPostError) as err:
  4280. await admin.a_create_client_authz_scope_permission(
  4281. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  4282. client_id="realm_management_id",
  4283. )
  4284. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  4285. @pytest.mark.asyncio
  4286. async def test_a_email(admin: KeycloakAdmin, user: str):
  4287. """Test email.
  4288. :param admin: Keycloak Admin client
  4289. :type admin: KeycloakAdmin
  4290. :param user: Keycloak user
  4291. :type user: str
  4292. """
  4293. # Emails will fail as we don't have SMTP test setup
  4294. with pytest.raises(KeycloakPutError) as err:
  4295. await admin.a_send_update_account(user_id=user, payload=dict())
  4296. assert err.match(UNKOWN_ERROR_REGEX), err
  4297. admin.update_user(user_id=user, payload={"enabled": True})
  4298. with pytest.raises(KeycloakPutError) as err:
  4299. await admin.a_send_verify_email(user_id=user)
  4300. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  4301. @pytest.mark.asyncio
  4302. async def test_a_get_sessions(admin: KeycloakAdmin):
  4303. """Test get sessions.
  4304. :param admin: Keycloak Admin client
  4305. :type admin: KeycloakAdmin
  4306. """
  4307. sessions = await admin.a_get_sessions(
  4308. user_id=admin.get_user_id(username=admin.connection.username)
  4309. )
  4310. assert len(sessions) >= 1
  4311. with pytest.raises(KeycloakGetError) as err:
  4312. await admin.a_get_sessions(user_id="bad")
  4313. assert err.match(USER_NOT_FOUND_REGEX)
  4314. @pytest.mark.asyncio
  4315. async def test_a_get_client_installation_provider(admin: KeycloakAdmin, client: str):
  4316. """Test get client installation provider.
  4317. :param admin: Keycloak Admin client
  4318. :type admin: KeycloakAdmin
  4319. :param client: Keycloak client
  4320. :type client: str
  4321. """
  4322. with pytest.raises(KeycloakGetError) as err:
  4323. await admin.a_get_client_installation_provider(client_id=client, provider_id="bad")
  4324. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  4325. installation = await admin.a_get_client_installation_provider(
  4326. client_id=client, provider_id="keycloak-oidc-keycloak-json"
  4327. )
  4328. assert set(installation.keys()) == {
  4329. "auth-server-url",
  4330. "confidential-port",
  4331. "credentials",
  4332. "realm",
  4333. "resource",
  4334. "ssl-required",
  4335. }
  4336. @pytest.mark.asyncio
  4337. async def test_a_auth_flows(admin: KeycloakAdmin, realm: str):
  4338. """Test auth flows.
  4339. :param admin: Keycloak Admin client
  4340. :type admin: KeycloakAdmin
  4341. :param realm: Keycloak realm
  4342. :type realm: str
  4343. """
  4344. await admin.a_change_current_realm(realm)
  4345. res = await admin.a_get_authentication_flows()
  4346. assert len(res) <= 8, res
  4347. default_flows = len(res)
  4348. assert {x["alias"] for x in res}.issubset(
  4349. {
  4350. "reset credentials",
  4351. "browser",
  4352. "registration",
  4353. "http challenge",
  4354. "docker auth",
  4355. "direct grant",
  4356. "first broker login",
  4357. "clients",
  4358. }
  4359. )
  4360. assert set(res[0].keys()) == {
  4361. "alias",
  4362. "authenticationExecutions",
  4363. "builtIn",
  4364. "description",
  4365. "id",
  4366. "providerId",
  4367. "topLevel",
  4368. }
  4369. assert {x["alias"] for x in res}.issubset(
  4370. {
  4371. "reset credentials",
  4372. "browser",
  4373. "registration",
  4374. "docker auth",
  4375. "direct grant",
  4376. "first broker login",
  4377. "clients",
  4378. "http challenge",
  4379. }
  4380. )
  4381. with pytest.raises(KeycloakGetError) as err:
  4382. await admin.a_get_authentication_flow_for_id(flow_id="bad")
  4383. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  4384. browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"]
  4385. res = await admin.a_get_authentication_flow_for_id(flow_id=browser_flow_id)
  4386. assert res["alias"] == "browser"
  4387. # Test copying
  4388. with pytest.raises(KeycloakPostError) as err:
  4389. await admin.a_copy_authentication_flow(payload=dict(), flow_alias="bad")
  4390. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  4391. res = await admin.a_copy_authentication_flow(
  4392. payload={"newName": "test-browser"}, flow_alias="browser"
  4393. )
  4394. assert res == b"", res
  4395. assert len(await admin.a_get_authentication_flows()) == (default_flows + 1)
  4396. # Test create
  4397. res = await admin.a_create_authentication_flow(
  4398. payload={"alias": "test-create", "providerId": "basic-flow"}
  4399. )
  4400. assert res == b""
  4401. with pytest.raises(KeycloakPostError) as err:
  4402. await admin.a_create_authentication_flow(
  4403. payload={"alias": "test-create", "builtIn": False}
  4404. )
  4405. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  4406. assert await admin.a_create_authentication_flow(
  4407. payload={"alias": "test-create"}, skip_exists=True
  4408. ) == {"msg": "Already exists"}
  4409. # Test flow executions
  4410. res = await admin.a_get_authentication_flow_executions(flow_alias="browser")
  4411. assert len(res) == 8, res
  4412. with pytest.raises(KeycloakGetError) as err:
  4413. await admin.a_get_authentication_flow_executions(flow_alias="bad")
  4414. assert ('b\'{"error":"Flow not found"' in str(err)) or err.match("404: b''")
  4415. exec_id = res[0]["id"]
  4416. res = await admin.a_get_authentication_flow_execution(execution_id=exec_id)
  4417. assert set(res.keys()).issubset(
  4418. {
  4419. "alternative",
  4420. "authenticator",
  4421. "authenticatorFlow",
  4422. "autheticatorFlow",
  4423. "conditional",
  4424. "disabled",
  4425. "enabled",
  4426. "id",
  4427. "parentFlow",
  4428. "priority",
  4429. "required",
  4430. "requirement",
  4431. }
  4432. ), res.keys()
  4433. with pytest.raises(KeycloakGetError) as err:
  4434. await admin.a_get_authentication_flow_execution(execution_id="bad")
  4435. assert err.match(ILLEGAL_EXECUTION_REGEX)
  4436. with pytest.raises(KeycloakPostError) as err:
  4437. await admin.a_create_authentication_flow_execution(payload=dict(), flow_alias="browser")
  4438. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  4439. res = await admin.a_create_authentication_flow_execution(
  4440. payload={"provider": "auth-cookie"}, flow_alias="test-create"
  4441. )
  4442. assert res == b""
  4443. assert len(await admin.a_get_authentication_flow_executions(flow_alias="test-create")) == 1
  4444. with pytest.raises(KeycloakPutError) as err:
  4445. await admin.a_update_authentication_flow_executions(
  4446. payload={"required": "yes"}, flow_alias="test-create"
  4447. )
  4448. assert err.match('400: b\'{"error":"Unrecognized field')
  4449. payload = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]
  4450. payload["displayName"] = "test"
  4451. res = await admin.a_update_authentication_flow_executions(
  4452. payload=payload, flow_alias="test-create"
  4453. )
  4454. assert res or (res == {})
  4455. exec_id = (await admin.a_get_authentication_flow_executions(flow_alias="test-create"))[0]["id"]
  4456. res = await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  4457. assert res == dict()
  4458. with pytest.raises(KeycloakDeleteError) as err:
  4459. await admin.a_delete_authentication_flow_execution(execution_id=exec_id)
  4460. assert err.match(ILLEGAL_EXECUTION_REGEX)
  4461. # Test subflows
  4462. res = await admin.a_create_authentication_flow_subflow(
  4463. payload={
  4464. "alias": "test-subflow",
  4465. "provider": "basic-flow",
  4466. "type": "something",
  4467. "description": "something",
  4468. },
  4469. flow_alias="test-browser",
  4470. )
  4471. assert res == b""
  4472. with pytest.raises(KeycloakPostError) as err:
  4473. await admin.a_create_authentication_flow_subflow(
  4474. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  4475. flow_alias="test-browser",
  4476. )
  4477. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  4478. res = await admin.a_create_authentication_flow_subflow(
  4479. payload={
  4480. "alias": "test-subflow",
  4481. "provider": "basic-flow",
  4482. "type": "something",
  4483. "description": "something",
  4484. },
  4485. flow_alias="test-create",
  4486. skip_exists=True,
  4487. )
  4488. assert res == {"msg": "Already exists"}
  4489. # Test delete auth flow
  4490. flow_id = [
  4491. x for x in await admin.a_get_authentication_flows() if x["alias"] == "test-browser"
  4492. ][0]["id"]
  4493. res = await admin.a_delete_authentication_flow(flow_id=flow_id)
  4494. assert res == dict()
  4495. with pytest.raises(KeycloakDeleteError) as err:
  4496. await admin.a_delete_authentication_flow(flow_id=flow_id)
  4497. assert ('b\'{"error":"Could not find flow with id"' in str(err)) or (
  4498. 'b\'{"error":"Flow not found"' in str(err)
  4499. )
  4500. @pytest.mark.asyncio
  4501. async def test_a_authentication_configs(admin: KeycloakAdmin, realm: str):
  4502. """Test authentication configs.
  4503. :param admin: Keycloak Admin client
  4504. :type admin: KeycloakAdmin
  4505. :param realm: Keycloak realm
  4506. :type realm: str
  4507. """
  4508. admin.change_current_realm(realm)
  4509. # Test list of auth providers
  4510. res = await admin.a_get_authenticator_providers()
  4511. assert len(res) <= 38
  4512. res = await admin.a_get_authenticator_provider_config_description(provider_id="auth-cookie")
  4513. assert res == {
  4514. "helpText": "Validates the SSO cookie set by the auth server.",
  4515. "name": "Cookie",
  4516. "properties": [],
  4517. "providerId": "auth-cookie",
  4518. }
  4519. # Test authenticator config
  4520. # Currently unable to find a sustainable way to fetch the config id,
  4521. # therefore testing only failures
  4522. with pytest.raises(KeycloakGetError) as err:
  4523. await admin.a_get_authenticator_config(config_id="bad")
  4524. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  4525. with pytest.raises(KeycloakPutError) as err:
  4526. await admin.a_update_authenticator_config(payload=dict(), config_id="bad")
  4527. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  4528. with pytest.raises(KeycloakDeleteError) as err:
  4529. await admin.a_delete_authenticator_config(config_id="bad")
  4530. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  4531. @pytest.mark.asyncio
  4532. async def test_a_sync_users(admin: KeycloakAdmin, realm: str):
  4533. """Test sync users.
  4534. :param admin: Keycloak Admin client
  4535. :type admin: KeycloakAdmin
  4536. :param realm: Keycloak realm
  4537. :type realm: str
  4538. """
  4539. await admin.a_change_current_realm(realm)
  4540. # Only testing the error message
  4541. with pytest.raises(KeycloakPostError) as err:
  4542. await admin.a_sync_users(storage_id="does-not-exist", action="triggerFullSync")
  4543. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  4544. @pytest.mark.asyncio
  4545. async def test_a_client_scopes(admin: KeycloakAdmin, realm: str):
  4546. """Test client scopes.
  4547. :param admin: Keycloak Admin client
  4548. :type admin: KeycloakAdmin
  4549. :param realm: Keycloak realm
  4550. :type realm: str
  4551. """
  4552. await admin.a_change_current_realm(realm)
  4553. # Test get client scopes
  4554. res = await admin.a_get_client_scopes()
  4555. scope_names = {x["name"] for x in res}
  4556. assert len(res) in [10, 11]
  4557. assert "email" in scope_names
  4558. assert "profile" in scope_names
  4559. assert "offline_access" in scope_names
  4560. with pytest.raises(KeycloakGetError) as err:
  4561. await admin.a_get_client_scope(client_scope_id="does-not-exist")
  4562. assert err.match(NO_CLIENT_SCOPE_REGEX)
  4563. scope = await admin.a_get_client_scope(client_scope_id=res[0]["id"])
  4564. assert res[0] == scope
  4565. scope = await admin.a_get_client_scope_by_name(client_scope_name=res[0]["name"])
  4566. assert res[0] == scope
  4567. # Test create client scope
  4568. res = await admin.a_create_client_scope(
  4569. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
  4570. )
  4571. assert res
  4572. res2 = await admin.a_create_client_scope(
  4573. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=True
  4574. )
  4575. assert res == res2
  4576. with pytest.raises(KeycloakPostError) as err:
  4577. await admin.a_create_client_scope(
  4578. payload={"name": "test-scope", "protocol": "openid-connect"}, skip_exists=False
  4579. )
  4580. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  4581. # Test update client scope
  4582. with pytest.raises(KeycloakPutError) as err:
  4583. await admin.a_update_client_scope(client_scope_id="does-not-exist", payload=dict())
  4584. assert err.match(NO_CLIENT_SCOPE_REGEX)
  4585. res_update = await admin.a_update_client_scope(
  4586. client_scope_id=res, payload={"name": "test-scope-update"}
  4587. )
  4588. assert res_update == dict()
  4589. assert (await admin.a_get_client_scope(client_scope_id=res))["name"] == "test-scope-update"
  4590. # Test get mappers
  4591. mappers = await admin.a_get_mappers_from_client_scope(client_scope_id=res)
  4592. assert mappers == list()
  4593. # Test add mapper
  4594. with pytest.raises(KeycloakPostError) as err:
  4595. await admin.a_add_mapper_to_client_scope(client_scope_id=res, payload=dict())
  4596. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  4597. res_add = await admin.a_add_mapper_to_client_scope(
  4598. client_scope_id=res,
  4599. payload={
  4600. "name": "test-mapper",
  4601. "protocol": "openid-connect",
  4602. "protocolMapper": "oidc-usermodel-attribute-mapper",
  4603. },
  4604. )
  4605. assert res_add == b""
  4606. assert len(await admin.a_get_mappers_from_client_scope(client_scope_id=res)) == 1
  4607. # Test update mapper
  4608. test_mapper = (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]
  4609. with pytest.raises(KeycloakPutError) as err:
  4610. await admin.a_update_mapper_in_client_scope(
  4611. client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
  4612. )
  4613. assert err.match(NO_CLIENT_SCOPE_REGEX)
  4614. test_mapper["config"]["user.attribute"] = "test"
  4615. res_update = await admin.a_update_mapper_in_client_scope(
  4616. client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
  4617. )
  4618. assert res_update == dict()
  4619. assert (await admin.a_get_mappers_from_client_scope(client_scope_id=res))[0]["config"][
  4620. "user.attribute"
  4621. ] == "test"
  4622. # Test delete mapper
  4623. res_del = await admin.a_delete_mapper_from_client_scope(
  4624. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  4625. )
  4626. assert res_del == dict()
  4627. with pytest.raises(KeycloakDeleteError) as err:
  4628. await admin.a_delete_mapper_from_client_scope(
  4629. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  4630. )
  4631. assert err.match('404: b\'{"error":"Model not found".*}\'')
  4632. # Test default default scopes
  4633. res_defaults = await admin.a_get_default_default_client_scopes()
  4634. assert len(res_defaults) in [6, 7]
  4635. with pytest.raises(KeycloakPutError) as err:
  4636. await admin.a_add_default_default_client_scope(scope_id="does-not-exist")
  4637. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  4638. res_add = await admin.a_add_default_default_client_scope(scope_id=res)
  4639. assert res_add == dict()
  4640. assert len(admin.get_default_default_client_scopes()) in [7, 8]
  4641. with pytest.raises(KeycloakDeleteError) as err:
  4642. await admin.a_delete_default_default_client_scope(scope_id="does-not-exist")
  4643. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  4644. res_del = await admin.a_delete_default_default_client_scope(scope_id=res)
  4645. assert res_del == dict()
  4646. assert len(admin.get_default_default_client_scopes()) in [6, 7]
  4647. # Test default optional scopes
  4648. res_defaults = await admin.a_get_default_optional_client_scopes()
  4649. assert len(res_defaults) == 4
  4650. with pytest.raises(KeycloakPutError) as err:
  4651. await admin.a_add_default_optional_client_scope(scope_id="does-not-exist")
  4652. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  4653. res_add = await admin.a_add_default_optional_client_scope(scope_id=res)
  4654. assert res_add == dict()
  4655. assert len(await admin.a_get_default_optional_client_scopes()) == 5
  4656. with pytest.raises(KeycloakDeleteError) as err:
  4657. await admin.a_delete_default_optional_client_scope(scope_id="does-not-exist")
  4658. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  4659. res_del = await admin.a_delete_default_optional_client_scope(scope_id=res)
  4660. assert res_del == dict()
  4661. assert len(await admin.a_get_default_optional_client_scopes()) == 4
  4662. # Test client scope delete
  4663. res_del = await admin.a_delete_client_scope(client_scope_id=res)
  4664. assert res_del == dict()
  4665. with pytest.raises(KeycloakDeleteError) as err:
  4666. await admin.a_delete_client_scope(client_scope_id=res)
  4667. assert err.match(NO_CLIENT_SCOPE_REGEX)
  4668. @pytest.mark.asyncio
  4669. async def test_a_components(admin: KeycloakAdmin, realm: str):
  4670. """Test components.
  4671. :param admin: Keycloak Admin client
  4672. :type admin: KeycloakAdmin
  4673. :param realm: Keycloak realm
  4674. :type realm: str
  4675. """
  4676. await admin.a_change_current_realm(realm)
  4677. # Test get components
  4678. res = await admin.a_get_components()
  4679. assert len(res) == 12
  4680. with pytest.raises(KeycloakGetError) as err:
  4681. await admin.a_get_component(component_id="does-not-exist")
  4682. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  4683. res_get = await admin.a_get_component(component_id=res[0]["id"])
  4684. assert res_get == res[0]
  4685. # Test create component
  4686. with pytest.raises(KeycloakPostError) as err:
  4687. await admin.a_create_component(payload={"bad": "dict"})
  4688. assert err.match('400: b\'{"error":"Unrecognized field')
  4689. res = await admin.a_create_component(
  4690. payload={
  4691. "name": "Test Component",
  4692. "providerId": "max-clients",
  4693. "providerType": "org.keycloak.services.clientregistration."
  4694. + "policy.ClientRegistrationPolicy",
  4695. "config": {"max-clients": ["1000"]},
  4696. }
  4697. )
  4698. assert res
  4699. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component"
  4700. # Test update component
  4701. component = await admin.a_get_component(component_id=res)
  4702. component["name"] = "Test Component Update"
  4703. with pytest.raises(KeycloakPutError) as err:
  4704. await admin.a_update_component(component_id="does-not-exist", payload=dict())
  4705. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  4706. res_upd = await admin.a_update_component(component_id=res, payload=component)
  4707. assert res_upd == dict()
  4708. assert (await admin.a_get_component(component_id=res))["name"] == "Test Component Update"
  4709. # Test delete component
  4710. res_del = await admin.a_delete_component(component_id=res)
  4711. assert res_del == dict()
  4712. with pytest.raises(KeycloakDeleteError) as err:
  4713. await admin.a_delete_component(component_id=res)
  4714. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  4715. @pytest.mark.asyncio
  4716. async def test_a_keys(admin: KeycloakAdmin, realm: str):
  4717. """Test keys.
  4718. :param admin: Keycloak Admin client
  4719. :type admin: KeycloakAdmin
  4720. :param realm: Keycloak realm
  4721. :type realm: str
  4722. """
  4723. await admin.a_change_current_realm(realm)
  4724. assert set((await admin.a_get_keys())["active"].keys()) == {
  4725. "AES",
  4726. "HS256",
  4727. "RS256",
  4728. "RSA-OAEP",
  4729. } or set((await admin.a_get_keys())["active"].keys()) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  4730. assert {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  4731. "HS256",
  4732. "RSA-OAEP",
  4733. "AES",
  4734. "RS256",
  4735. } or {k["algorithm"] for k in (await admin.a_get_keys())["keys"]} == {
  4736. "HS512",
  4737. "RSA-OAEP",
  4738. "AES",
  4739. "RS256",
  4740. }
  4741. @pytest.mark.asyncio
  4742. async def test_a_admin_events(admin: KeycloakAdmin, realm: str):
  4743. """Test events.
  4744. :param admin: Keycloak Admin client
  4745. :type admin: KeycloakAdmin
  4746. :param realm: Keycloak realm
  4747. :type realm: str
  4748. """
  4749. await admin.a_change_current_realm(realm)
  4750. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  4751. events = await admin.a_get_admin_events()
  4752. assert events == list()
  4753. @pytest.mark.asyncio
  4754. async def test_a_user_events(admin: KeycloakAdmin, realm: str):
  4755. """Test events.
  4756. :param admin: Keycloak Admin client
  4757. :type admin: KeycloakAdmin
  4758. :param realm: Keycloak realm
  4759. :type realm: str
  4760. """
  4761. await admin.a_change_current_realm(realm)
  4762. events = await admin.a_get_events()
  4763. assert events == list()
  4764. with pytest.raises(KeycloakPutError) as err:
  4765. await admin.a_set_events(payload={"bad": "conf"})
  4766. assert err.match('400: b\'{"error":"Unrecognized field')
  4767. res = await admin.a_set_events(
  4768. payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True}
  4769. )
  4770. assert res == dict()
  4771. await admin.a_create_client(payload={"name": "test", "clientId": "test"})
  4772. events = await admin.a_get_events()
  4773. assert events == list()
  4774. @pytest.mark.asyncio
  4775. @freezegun.freeze_time("2023-02-25 10:00:00")
  4776. async def test_a_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
  4777. """Test auto refresh token.
  4778. :param admin_frozen: Keycloak Admin client with time frozen in place
  4779. :type admin_frozen: KeycloakAdmin
  4780. :param realm: Keycloak realm
  4781. :type realm: str
  4782. """
  4783. admin = admin_frozen
  4784. admin.get_realm(realm)
  4785. # Test get refresh
  4786. admin.connection.custom_headers = {
  4787. "Authorization": "Bearer bad",
  4788. "Content-Type": "application/json",
  4789. }
  4790. with pytest.raises(KeycloakAuthenticationError) as err:
  4791. await admin.a_get_realm(realm_name=realm)
  4792. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized".*}\'')
  4793. # Freeze time to simulate the access token expiring
  4794. with freezegun.freeze_time("2023-02-25 10:05:00"):
  4795. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
  4796. assert await admin.a_get_realm(realm_name=realm)
  4797. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
  4798. # Test bad refresh token, but first make sure access token has expired again
  4799. with freezegun.freeze_time("2023-02-25 10:10:00"):
  4800. admin.connection.custom_headers = {"Content-Type": "application/json"}
  4801. admin.connection.token["refresh_token"] = "bad"
  4802. with pytest.raises(KeycloakPostError) as err:
  4803. await admin.a_get_realm(realm_name="test-refresh")
  4804. assert err.match(
  4805. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
  4806. )
  4807. admin.connection.get_token()
  4808. # Test post refresh
  4809. with freezegun.freeze_time("2023-02-25 10:15:00"):
  4810. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
  4811. admin.connection.token = None
  4812. assert await admin.a_create_realm(payload={"realm": "test-refresh"}) == b""
  4813. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
  4814. # Test update refresh
  4815. with freezegun.freeze_time("2023-02-25 10:25:00"):
  4816. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
  4817. admin.connection.token = None
  4818. assert (
  4819. await admin.a_update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  4820. == dict()
  4821. )
  4822. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
  4823. # Test delete refresh
  4824. with freezegun.freeze_time("2023-02-25 10:35:00"):
  4825. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
  4826. admin.connection.token = None
  4827. assert await admin.a_delete_realm(realm_name="test-refresh") == dict()
  4828. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
  4829. @pytest.mark.asyncio
  4830. async def test_a_get_required_actions(admin: KeycloakAdmin, realm: str):
  4831. """Test required actions.
  4832. :param admin: Keycloak Admin client
  4833. :type admin: KeycloakAdmin
  4834. :param realm: Keycloak realm
  4835. :type realm: str
  4836. """
  4837. await admin.a_change_current_realm(realm)
  4838. ractions = await admin.a_get_required_actions()
  4839. assert isinstance(ractions, list)
  4840. for ra in ractions:
  4841. for key in [
  4842. "alias",
  4843. "name",
  4844. "providerId",
  4845. "enabled",
  4846. "defaultAction",
  4847. "priority",
  4848. "config",
  4849. ]:
  4850. assert key in ra
  4851. @pytest.mark.asyncio
  4852. async def test_a_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
  4853. """Test get required action by alias.
  4854. :param admin: Keycloak Admin client
  4855. :type admin: KeycloakAdmin
  4856. :param realm: Keycloak realm
  4857. :type realm: str
  4858. """
  4859. await admin.a_change_current_realm(realm)
  4860. ractions = await admin.a_get_required_actions()
  4861. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  4862. assert ra in ractions
  4863. assert ra["alias"] == "UPDATE_PASSWORD"
  4864. assert await admin.a_get_required_action_by_alias("does-not-exist") is None
  4865. @pytest.mark.asyncio
  4866. async def test_a_update_required_action(admin: KeycloakAdmin, realm: str):
  4867. """Test update required action.
  4868. :param admin: Keycloak Admin client
  4869. :type admin: KeycloakAdmin
  4870. :param realm: Keycloak realm
  4871. :type realm: str
  4872. """
  4873. await admin.a_change_current_realm(realm)
  4874. ra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  4875. old = copy.deepcopy(ra)
  4876. ra["enabled"] = False
  4877. admin.update_required_action("UPDATE_PASSWORD", ra)
  4878. newra = await admin.a_get_required_action_by_alias("UPDATE_PASSWORD")
  4879. assert old != newra
  4880. assert newra["enabled"] is False
  4881. @pytest.mark.asyncio
  4882. async def test_a_get_composite_client_roles_of_group(
  4883. admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str
  4884. ):
  4885. """Test get composite client roles of group.
  4886. :param admin: Keycloak Admin client
  4887. :type admin: KeycloakAdmin
  4888. :param realm: Keycloak realm
  4889. :type realm: str
  4890. :param client: Keycloak client
  4891. :type client: str
  4892. :param group: Keycloak group
  4893. :type group: str
  4894. :param composite_client_role: Composite client role
  4895. :type composite_client_role: str
  4896. """
  4897. await admin.a_change_current_realm(realm)
  4898. role = await admin.a_get_client_role(client, composite_client_role)
  4899. await admin.a_assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  4900. result = await admin.a_get_composite_client_roles_of_group(client, group)
  4901. assert role["id"] in [x["id"] for x in result]
  4902. @pytest.mark.asyncio
  4903. async def test_a_get_role_client_level_children(
  4904. admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str
  4905. ):
  4906. """Test get children of composite client role.
  4907. :param admin: Keycloak Admin client
  4908. :type admin: KeycloakAdmin
  4909. :param realm: Keycloak realm
  4910. :type realm: str
  4911. :param client: Keycloak client
  4912. :type client: str
  4913. :param composite_client_role: Composite client role
  4914. :type composite_client_role: str
  4915. :param client_role: Client role
  4916. :type client_role: str
  4917. """
  4918. await admin.a_change_current_realm(realm)
  4919. child = await admin.a_get_client_role(client, client_role)
  4920. parent = await admin.a_get_client_role(client, composite_client_role)
  4921. res = await admin.a_get_role_client_level_children(client, parent["id"])
  4922. assert child["id"] in [x["id"] for x in res]
  4923. @pytest.mark.asyncio
  4924. async def test_a_upload_certificate(
  4925. admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple
  4926. ):
  4927. """Test upload certificate.
  4928. :param admin: Keycloak Admin client
  4929. :type admin: KeycloakAdmin
  4930. :param realm: Keycloak realm
  4931. :type realm: str
  4932. :param client: Keycloak client
  4933. :type client: str
  4934. :param selfsigned_cert: Selfsigned certificates
  4935. :type selfsigned_cert: tuple
  4936. """
  4937. await admin.a_change_current_realm(realm)
  4938. cert, _ = selfsigned_cert
  4939. cert = cert.decode("utf-8").strip()
  4940. admin.upload_certificate(client, cert)
  4941. cl = await admin.a_get_client(client)
  4942. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  4943. @pytest.mark.asyncio
  4944. async def test_a_get_bruteforce_status_for_user(
  4945. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  4946. ):
  4947. """Test users.
  4948. :param admin: Keycloak Admin client
  4949. :type admin: KeycloakAdmin
  4950. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  4951. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  4952. :param realm: Keycloak realm
  4953. :type realm: str
  4954. """
  4955. oid, username, password = oid_with_credentials
  4956. await admin.a_change_current_realm(realm)
  4957. # Turn on bruteforce protection
  4958. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  4959. res = await admin.a_get_realm(realm_name=realm)
  4960. assert res["bruteForceProtected"] is True
  4961. # Test login user with wrong credentials
  4962. try:
  4963. oid.token(username=username, password="wrongpassword")
  4964. except KeycloakAuthenticationError:
  4965. pass
  4966. user_id = await admin.a_get_user_id(username)
  4967. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  4968. assert bruteforce_status["numFailures"] == 1
  4969. # Cleanup
  4970. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  4971. res = await admin.a_get_realm(realm_name=realm)
  4972. assert res["bruteForceProtected"] is False
  4973. @pytest.mark.asyncio
  4974. async def test_a_clear_bruteforce_attempts_for_user(
  4975. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  4976. ):
  4977. """Test users.
  4978. :param admin: Keycloak Admin client
  4979. :type admin: KeycloakAdmin
  4980. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  4981. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  4982. :param realm: Keycloak realm
  4983. :type realm: str
  4984. """
  4985. oid, username, password = oid_with_credentials
  4986. await admin.a_change_current_realm(realm)
  4987. # Turn on bruteforce protection
  4988. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  4989. res = await admin.a_get_realm(realm_name=realm)
  4990. assert res["bruteForceProtected"] is True
  4991. # Test login user with wrong credentials
  4992. try:
  4993. oid.token(username=username, password="wrongpassword")
  4994. except KeycloakAuthenticationError:
  4995. pass
  4996. user_id = await admin.a_get_user_id(username)
  4997. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  4998. assert bruteforce_status["numFailures"] == 1
  4999. res = await admin.a_clear_bruteforce_attempts_for_user(user_id)
  5000. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5001. assert bruteforce_status["numFailures"] == 0
  5002. # Cleanup
  5003. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5004. res = await admin.a_get_realm(realm_name=realm)
  5005. assert res["bruteForceProtected"] is False
  5006. @pytest.mark.asyncio
  5007. async def test_a_clear_bruteforce_attempts_for_all_users(
  5008. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  5009. ):
  5010. """Test users.
  5011. :param admin: Keycloak Admin client
  5012. :type admin: KeycloakAdmin
  5013. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5014. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5015. :param realm: Keycloak realm
  5016. :type realm: str
  5017. """
  5018. oid, username, password = oid_with_credentials
  5019. await admin.a_change_current_realm(realm)
  5020. # Turn on bruteforce protection
  5021. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  5022. res = await admin.a_get_realm(realm_name=realm)
  5023. assert res["bruteForceProtected"] is True
  5024. # Test login user with wrong credentials
  5025. try:
  5026. oid.token(username=username, password="wrongpassword")
  5027. except KeycloakAuthenticationError:
  5028. pass
  5029. user_id = await admin.a_get_user_id(username)
  5030. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5031. assert bruteforce_status["numFailures"] == 1
  5032. res = await admin.a_clear_all_bruteforce_attempts()
  5033. bruteforce_status = await admin.a_get_bruteforce_detection_status(user_id)
  5034. assert bruteforce_status["numFailures"] == 0
  5035. # Cleanup
  5036. res = await admin.a_update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  5037. res = await admin.a_get_realm(realm_name=realm)
  5038. assert res["bruteForceProtected"] is False
  5039. @pytest.mark.asyncio
  5040. async def test_a_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  5041. """Test that the default realm role is present in a brand new realm.
  5042. :param realm: Realm name
  5043. :type realm: str
  5044. :param admin: Keycloak admin
  5045. :type admin: KeycloakAdmin
  5046. """
  5047. await admin.a_change_current_realm(realm)
  5048. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  5049. assert (
  5050. len(
  5051. [
  5052. x["name"]
  5053. for x in await admin.a_get_realm_roles()
  5054. if x["name"] == f"default-roles-{realm}"
  5055. ]
  5056. )
  5057. == 1
  5058. )
  5059. @pytest.mark.asyncio
  5060. async def test_a_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  5061. """Test getter for the ID of the default realm role.
  5062. :param realm: Realm name
  5063. :type realm: str
  5064. :param admin: Keycloak admin
  5065. :type admin: KeycloakAdmin
  5066. """
  5067. await admin.a_change_current_realm(realm)
  5068. assert (
  5069. await admin.a_get_default_realm_role_id()
  5070. == [
  5071. x["id"]
  5072. for x in await admin.a_get_realm_roles()
  5073. if x["name"] == f"default-roles-{realm}"
  5074. ][0]
  5075. )
  5076. @pytest.mark.asyncio
  5077. async def test_a_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  5078. """Test getting, adding and deleting default realm roles.
  5079. :param realm: Realm name
  5080. :type realm: str
  5081. :param admin: Keycloak admin
  5082. :type admin: KeycloakAdmin
  5083. """
  5084. await admin.a_change_current_realm(realm)
  5085. # Test listing all default realm roles
  5086. roles = await admin.a_get_realm_default_roles()
  5087. assert len(roles) == 2
  5088. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  5089. with pytest.raises(KeycloakGetError) as err:
  5090. await admin.a_change_current_realm("doesnotexist")
  5091. await admin.a_get_realm_default_roles()
  5092. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  5093. await admin.a_change_current_realm(realm)
  5094. # Test removing a default realm role
  5095. res = await admin.a_remove_realm_default_roles(payload=[roles[0]])
  5096. assert res == {}
  5097. assert roles[0] not in await admin.a_get_realm_default_roles()
  5098. assert len(await admin.a_get_realm_default_roles()) == 1
  5099. with pytest.raises(KeycloakDeleteError) as err:
  5100. await admin.a_remove_realm_default_roles(payload=[{"id": "bad id"}])
  5101. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5102. # Test adding a default realm role
  5103. res = await admin.a_add_realm_default_roles(payload=[roles[0]])
  5104. assert res == {}
  5105. assert roles[0] in await admin.a_get_realm_default_roles()
  5106. assert len(await admin.a_get_realm_default_roles()) == 2
  5107. with pytest.raises(KeycloakPostError) as err:
  5108. await admin.a_add_realm_default_roles(payload=[{"id": "bad id"}])
  5109. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  5110. @pytest.mark.asyncio
  5111. async def test_a_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  5112. """Test clearing the keys cache.
  5113. :param realm: Realm name
  5114. :type realm: str
  5115. :param admin: Keycloak admin
  5116. :type admin: KeycloakAdmin
  5117. """
  5118. await admin.a_change_current_realm(realm)
  5119. res = await admin.a_clear_keys_cache()
  5120. assert res == {}
  5121. @pytest.mark.asyncio
  5122. async def test_a_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  5123. """Test clearing the realm cache.
  5124. :param realm: Realm name
  5125. :type realm: str
  5126. :param admin: Keycloak admin
  5127. :type admin: KeycloakAdmin
  5128. """
  5129. await admin.a_change_current_realm(realm)
  5130. res = await admin.a_clear_realm_cache()
  5131. assert res == {}
  5132. @pytest.mark.asyncio
  5133. async def test_a_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  5134. """Test clearing the user cache.
  5135. :param realm: Realm name
  5136. :type realm: str
  5137. :param admin: Keycloak admin
  5138. :type admin: KeycloakAdmin
  5139. """
  5140. await admin.a_change_current_realm(realm)
  5141. res = await admin.a_clear_user_cache()
  5142. assert res == {}
  5143. @pytest.mark.asyncio
  5144. async def test_a_initial_access_token(
  5145. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5146. ) -> None:
  5147. """Test initial access token and client creation.
  5148. :param admin: Keycloak admin
  5149. :type admin: KeycloakAdmin
  5150. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  5151. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  5152. """
  5153. res = await admin.a_create_initial_access_token(2, 3)
  5154. assert "token" in res
  5155. assert res["count"] == 2
  5156. assert res["expiration"] == 3
  5157. oid, username, password = oid_with_credentials
  5158. client = str(uuid.uuid4())
  5159. secret = str(uuid.uuid4())
  5160. res = await oid.a_register_client(
  5161. token=res["token"],
  5162. payload={
  5163. "name": "DynamicRegisteredClient",
  5164. "clientId": client,
  5165. "enabled": True,
  5166. "publicClient": False,
  5167. "protocol": "openid-connect",
  5168. "secret": secret,
  5169. "clientAuthenticatorType": "client-secret",
  5170. },
  5171. )
  5172. assert res["clientId"] == client
  5173. new_secret = str(uuid.uuid4())
  5174. res = await oid.a_update_client(
  5175. res["registrationAccessToken"], client, payload={"secret": new_secret}
  5176. )
  5177. assert res["secret"] == new_secret
  5178. @pytest.mark.asyncio
  5179. async def test_a_refresh_token(admin: KeycloakAdmin):
  5180. """Test refresh token on connection even if it is expired.
  5181. :param admin: Keycloak admin
  5182. :type admin: KeycloakAdmin
  5183. """
  5184. admin.get_realms()
  5185. assert admin.connection.token is not None
  5186. await admin.a_user_logout(await admin.a_get_user_id(admin.connection.username))
  5187. admin.connection.refresh_token()
  5188. def test_counter_part():
  5189. """Test that each function has its async counter part."""
  5190. admin_methods = [func for func in dir(KeycloakAdmin) if callable(getattr(KeycloakAdmin, func))]
  5191. sync_methods = [
  5192. method
  5193. for method in admin_methods
  5194. if not method.startswith("a_") and not method.startswith("_")
  5195. ]
  5196. async_methods = [
  5197. method for method in admin_methods if iscoroutinefunction(getattr(KeycloakAdmin, method))
  5198. ]
  5199. for method in sync_methods:
  5200. async_method = f"a_{method}"
  5201. assert (async_method in admin_methods) is True
  5202. sync_sign = signature(getattr(KeycloakAdmin, method))
  5203. async_sign = signature(getattr(KeycloakAdmin, async_method))
  5204. assert sync_sign.parameters == async_sign.parameters
  5205. for async_method in async_methods:
  5206. if async_method[2:].startswith("_"):
  5207. continue
  5208. assert async_method[2:] in sync_methods