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.

6311 lines
225 KiB

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