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.

6337 lines
226 KiB

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