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.

6256 lines
224 KiB

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