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.

6175 lines
220 KiB

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